VirtualBox

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

Last change on this file since 100179 was 100179, checked in by vboxsync, 18 months ago

Main/Machine: If IMachine::discardSavedState() is called for a VM in the
'AbortedSaved' state then the machine state is correctly moved to
'Powered Off' but the saved state file isn't deleted due to a missing
check for this machine state transition in SessionMachine::i_setMachineState().
bugref:1484 bugref:10471

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 601.7 KB
Line 
1/* $Id: MachineImpl.cpp 100179 2023-06-15 12:30:32Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "MachineImplMoveVM.h"
61#include "ExtPackManagerImpl.h"
62#include "MachineLaunchVMCommonWorker.h"
63#include "CryptoUtils.h"
64
65// generated header
66#include "VBoxEvents.h"
67
68#ifdef VBOX_WITH_USB
69# include "USBProxyService.h"
70#endif
71
72#include "AutoCaller.h"
73#include "HashedPw.h"
74#include "Performance.h"
75#include "StringifyEnums.h"
76
77#include <iprt/asm.h>
78#include <iprt/path.h>
79#include <iprt/dir.h>
80#include <iprt/env.h>
81#include <iprt/lockvalidator.h>
82#include <iprt/memsafer.h>
83#include <iprt/process.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
86#include <iprt/sha.h>
87#include <iprt/string.h>
88#include <iprt/ctype.h>
89
90#include <VBox/com/array.h>
91#include <VBox/com/list.h>
92#include <VBox/VBoxCryptoIf.h>
93
94#include <VBox/err.h>
95#include <VBox/param.h>
96#include <VBox/settings.h>
97#include <VBox/VMMDev.h>
98#include <VBox/vmm/ssm.h>
99
100#ifdef VBOX_WITH_GUEST_PROPS
101# include <VBox/HostServices/GuestPropertySvc.h>
102# include <VBox/com/array.h>
103#endif
104
105#ifdef VBOX_WITH_SHARED_CLIPBOARD
106# include <VBox/HostServices/VBoxClipboardSvc.h>
107#endif
108
109#include "VBox/com/MultiResult.h"
110
111#include <algorithm>
112
113#ifdef VBOX_WITH_DTRACE_R3_MAIN
114# include "dtrace/VBoxAPI.h"
115#endif
116
117#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
118# define HOSTSUFF_EXE ".exe"
119#else /* !RT_OS_WINDOWS */
120# define HOSTSUFF_EXE ""
121#endif /* !RT_OS_WINDOWS */
122
123// defines / prototypes
124/////////////////////////////////////////////////////////////////////////////
125
126#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
127# define BUF_DATA_SIZE _64K
128
129enum CipherMode
130{
131 CipherModeGcm = 0,
132 CipherModeCtr,
133 CipherModeXts,
134 CipherModeMax
135};
136
137enum AesSize
138{
139 Aes128 = 0,
140 Aes256,
141 AesMax
142};
143
144const char *g_apszCipher[AesMax][CipherModeMax] =
145{
146 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
147 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
148};
149const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
150
151static const char *getCipherString(const char *pszAlgo, const int iMode)
152{
153 if (iMode >= CipherModeMax)
154 return pszAlgo;
155
156 for (int i = 0; i < AesMax; i++)
157 {
158 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
159 return g_apszCipher[i][iMode];
160 }
161 return pszAlgo;
162}
163
164static const char *getCipherStringWithoutMode(const char *pszAlgo)
165{
166 for (int i = 0; i < AesMax; i++)
167 {
168 for (int j = 0; j < CipherModeMax; j++)
169 {
170 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
171 return g_apszCipherAlgo[i];
172 }
173 }
174 return pszAlgo;
175}
176#endif
177
178/////////////////////////////////////////////////////////////////////////////
179// Machine::Data structure
180/////////////////////////////////////////////////////////////////////////////
181
182Machine::Data::Data()
183{
184 mRegistered = FALSE;
185 pMachineConfigFile = NULL;
186 /* Contains hints on what has changed when the user is using the VM (config
187 * changes, running the VM, ...). This is used to decide if a config needs
188 * to be written to disk. */
189 flModifications = 0;
190 /* VM modification usually also trigger setting the current state to
191 * "Modified". Although this is not always the case. An e.g. is the VM
192 * initialization phase or when snapshot related data is changed. The
193 * actually behavior is controlled by the following flag. */
194 m_fAllowStateModification = false;
195 mAccessible = FALSE;
196 /* mUuid is initialized in Machine::init() */
197
198 mMachineState = MachineState_PoweredOff;
199 RTTimeNow(&mLastStateChange);
200
201 mMachineStateDeps = 0;
202 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
203 mMachineStateChangePending = 0;
204
205 mCurrentStateModified = TRUE;
206 mGuestPropertiesModified = FALSE;
207
208 mSession.mPID = NIL_RTPROCESS;
209 mSession.mLockType = LockType_Null;
210 mSession.mState = SessionState_Unlocked;
211
212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
213 mpKeyStore = NULL;
214#endif
215}
216
217Machine::Data::~Data()
218{
219 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
220 {
221 RTSemEventMultiDestroy(mMachineStateDepsSem);
222 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
223 }
224 if (pMachineConfigFile)
225 {
226 delete pMachineConfigFile;
227 pMachineConfigFile = NULL;
228 }
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HWData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::HWData::HWData()
236{
237 /* default values for a newly created machine */
238 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
239 mMemorySize = 128;
240 mCPUCount = 1;
241 mCPUHotPlugEnabled = false;
242 mMemoryBalloonSize = 0;
243 mPageFusionEnabled = false;
244 mHWVirtExEnabled = true;
245 mHWVirtExNestedPagingEnabled = true;
246 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
247 mHWVirtExVPIDEnabled = true;
248 mHWVirtExUXEnabled = true;
249 mHWVirtExForceEnabled = false;
250 mHWVirtExUseNativeApi = false;
251 mHWVirtExVirtVmsaveVmload = true;
252#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
253 mPAEEnabled = true;
254#else
255 mPAEEnabled = false;
256#endif
257 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
258 mTripleFaultReset = false;
259 mAPIC = true;
260 mX2APIC = false;
261 mIBPBOnVMExit = false;
262 mIBPBOnVMEntry = false;
263 mSpecCtrl = false;
264 mSpecCtrlByHost = false;
265 mL1DFlushOnSched = true;
266 mL1DFlushOnVMEntry = false;
267 mMDSClearOnSched = true;
268 mMDSClearOnVMEntry = false;
269 mNestedHWVirt = false;
270 mHPETEnabled = false;
271 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
272 mCpuIdPortabilityLevel = 0;
273 mCpuProfile = "host";
274
275 /* default boot order: floppy - DVD - HDD */
276 mBootOrder[0] = DeviceType_Floppy;
277 mBootOrder[1] = DeviceType_DVD;
278 mBootOrder[2] = DeviceType_HardDisk;
279 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
280 mBootOrder[i] = DeviceType_Null;
281
282 mClipboardMode = ClipboardMode_Disabled;
283 mClipboardFileTransfersEnabled = FALSE;
284
285 mDnDMode = DnDMode_Disabled;
286
287 mFirmwareType = FirmwareType_BIOS;
288 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
289 mPointingHIDType = PointingHIDType_PS2Mouse;
290 mChipsetType = ChipsetType_PIIX3;
291 mIommuType = IommuType_None;
292 mParavirtProvider = ParavirtProvider_Default;
293 mEmulatedUSBCardReaderEnabled = FALSE;
294
295 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
296 mCPUAttached[i] = false;
297
298 mIOCacheEnabled = true;
299 mIOCacheSize = 5; /* 5MB */
300}
301
302Machine::HWData::~HWData()
303{
304}
305
306/////////////////////////////////////////////////////////////////////////////
307// Machine class
308/////////////////////////////////////////////////////////////////////////////
309
310// constructor / destructor
311/////////////////////////////////////////////////////////////////////////////
312
313Machine::Machine() :
314#ifdef VBOX_WITH_RESOURCE_USAGE_API
315 mCollectorGuest(NULL),
316#endif
317 mPeer(NULL),
318 mParent(NULL),
319 mSerialPorts(),
320 mParallelPorts(),
321 uRegistryNeedsSaving(0)
322{}
323
324Machine::~Machine()
325{}
326
327HRESULT Machine::FinalConstruct()
328{
329 LogFlowThisFunc(("\n"));
330 return BaseFinalConstruct();
331}
332
333void Machine::FinalRelease()
334{
335 LogFlowThisFunc(("\n"));
336 uninit();
337 BaseFinalRelease();
338}
339
340/**
341 * Initializes a new machine instance; this init() variant creates a new, empty machine.
342 * This gets called from VirtualBox::CreateMachine().
343 *
344 * @param aParent Associated parent object
345 * @param strConfigFile Local file system path to the VM settings file (can
346 * be relative to the VirtualBox config directory).
347 * @param strName name for the machine
348 * @param llGroups list of groups for the machine
349 * @param strOsType OS Type string (stored as is if aOsType is NULL).
350 * @param aOsType OS Type of this machine or NULL.
351 * @param aId UUID for the new machine.
352 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
353 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
354 * scheme (includes the UUID).
355 * @param aCipher The cipher to encrypt the VM with.
356 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
357 * @param aPassword The password to encrypt the VM with.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 const Utf8Str &strName,
364 const StringsList &llGroups,
365 const Utf8Str &strOsType,
366 GuestOSType *aOsType,
367 const Guid &aId,
368 bool fForceOverwrite,
369 bool fDirectoryIncludesUUID,
370 const com::Utf8Str &aCipher,
371 const com::Utf8Str &aPasswordId,
372 const com::Utf8Str &aPassword)
373{
374 LogFlowThisFuncEnter();
375 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
376
377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
378 RT_NOREF(aCipher);
379 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
380 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
381#endif
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT hrc = initImpl(aParent, strConfigFile);
388 if (FAILED(hrc)) return hrc;
389
390#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
391 com::Utf8Str strSsmKeyId;
392 com::Utf8Str strSsmKeyStore;
393 com::Utf8Str strNVRAMKeyId;
394 com::Utf8Str strNVRAMKeyStore;
395
396 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
397 {
398 /* Resolve the cryptographic interface. */
399 PCVBOXCRYPTOIF pCryptoIf = NULL;
400 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
401 if (SUCCEEDED(hrc))
402 {
403 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
404 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
405 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
406
407 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
408 {
409 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
410 if (!pszCipher)
411 {
412 hrc = setError(VBOX_E_NOT_SUPPORTED,
413 tr("The cipher '%s' is not supported"), aCipher.c_str());
414 break;
415 }
416
417 VBOXCRYPTOCTX hCryptoCtx;
418 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 char *pszKeyStore;
426 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
427 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
428 AssertRC(vrc2);
429
430 if (RT_FAILURE(vrc))
431 {
432 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
433 break;
434 }
435
436 *(astrKeyStore[i]) = pszKeyStore;
437 RTMemFree(pszKeyStore);
438 *(astrKeyId[i]) = aPasswordId;
439 }
440
441 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
442 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
443
444 if (FAILED(hrc))
445 return hrc; /* Error is set. */
446 }
447 else
448 return hrc; /* Error is set. */
449 }
450#endif
451
452 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(hrc)) return hrc;
454
455 if (SUCCEEDED(hrc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 hrc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(hrc))
464 {
465#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
466 mSSData->strStateKeyId = strSsmKeyId;
467 mSSData->strStateKeyStore = strSsmKeyStore;
468#endif
469
470 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
471 mData->mAccessible = TRUE;
472
473 unconst(mData->mUuid) = aId;
474
475 mUserData->s.strName = strName;
476
477 if (llGroups.size())
478 mUserData->s.llGroups = llGroups;
479
480 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
481 // the "name sync" flag determines whether the machine directory gets renamed along
482 // with the machine file; say so if the settings file name is the same as the
483 // settings file parent directory (machine directory)
484 mUserData->s.fNameSync = i_isInOwnDir();
485
486 // initialize the default snapshots folder
487 hrc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(hrc);
489
490 if (aOsType)
491 {
492 /* Store OS type */
493 mUserData->s.strOsType = aOsType->i_id();
494
495 /* Let the OS type select 64-bit ness. */
496 mHWData->mLongMode = aOsType->i_is64Bit()
497 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
498
499 /* Let the OS type enable the X2APIC */
500 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
501
502 hrc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(hrc);
504 }
505 else if (!strOsType.isEmpty())
506 {
507 /* Store OS type */
508 mUserData->s.strOsType = strOsType;
509
510 /* No guest OS type object. Pick some plausible defaults which the
511 * host can handle. There's no way to know or validate anything. */
512 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
513 mHWData->mX2APIC = false;
514 }
515
516 /* Apply BIOS defaults. */
517 mBIOSSettings->i_applyDefaults(aOsType);
518
519 /* Apply TPM defaults. */
520 mTrustedPlatformModule->i_applyDefaults(aOsType);
521
522 /* Apply recording defaults. */
523 mRecordingSettings->i_applyDefaults();
524
525 /* Apply network adapters defaults */
526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
527 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
528
529 /* Apply serial port defaults */
530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
531 mSerialPorts[slot]->i_applyDefaults(aOsType);
532
533 /* Apply parallel port defaults */
534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
535 mParallelPorts[slot]->i_applyDefaults();
536
537 /* Enable the VMMDev testing feature for bootsector VMs: */
538 if (aOsType && aOsType->i_id() == "VBoxBS_64")
539 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
540
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(hrc))
545 {
546 /* At this point the changing of the current state modification
547 * flag is allowed. */
548 i_allowStateModification();
549
550 /* commit all changes made during the initialization */
551 i_commit();
552 }
553 }
554
555 /* Confirm a successful initialization when it's the case */
556 if (SUCCEEDED(hrc))
557 {
558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
559 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
560 {
561 size_t cbPassword = aPassword.length() + 1;
562 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
563 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
564 }
565#endif
566
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 autoInitSpan.setLimited();
571 }
572
573 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 hrc));
578
579 LogFlowThisFuncLeave();
580
581 return hrc;
582}
583
584/**
585 * Initializes a new instance with data from machine XML (formerly Init_Registered).
586 * Gets called in two modes:
587 *
588 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
589 * UUID is specified and we mark the machine as "registered";
590 *
591 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
592 * and the machine remains unregistered until RegisterMachine() is called.
593 *
594 * @param aParent Associated parent object
595 * @param strConfigFile Local file system path to the VM settings file (can
596 * be relative to the VirtualBox config directory).
597 * @param aId UUID of the machine or NULL (see above).
598 * @param strPassword Password for decrypting the config
599 *
600 * @return Success indicator. if not S_OK, the machine object is invalid
601 */
602HRESULT Machine::initFromSettings(VirtualBox *aParent,
603 const Utf8Str &strConfigFile,
604 const Guid *aId,
605 const com::Utf8Str &strPassword)
606{
607 LogFlowThisFuncEnter();
608 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
609
610 PCVBOXCRYPTOIF pCryptoIf = NULL;
611#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
612 if (strPassword.isNotEmpty())
613 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
614#else
615 if (strPassword.isNotEmpty())
616 {
617 /* Get at the crpytographic interface. */
618 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
619 if (FAILED(hrc))
620 return hrc; /* Error is set. */
621 }
622#endif
623
624 /* Enclose the state transition NotReady->InInit->Ready */
625 AutoInitSpan autoInitSpan(this);
626 AssertReturn(autoInitSpan.isOk(), E_FAIL);
627
628 HRESULT hrc = initImpl(aParent, strConfigFile);
629 if (FAILED(hrc)) return hrc;
630
631 if (aId)
632 {
633 // loading a registered VM:
634 unconst(mData->mUuid) = *aId;
635 mData->mRegistered = TRUE;
636 // now load the settings from XML:
637 hrc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 hrc = initDataAndChildObjects();
644 if (SUCCEEDED(hrc))
645 {
646 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
647 mData->mAccessible = TRUE;
648
649 try
650 {
651 // load and parse machine XML; this will throw on XML or logic errors
652 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
653 pCryptoIf,
654 strPassword.c_str());
655
656 // reject VM UUID duplicates, they can happen if someone
657 // tries to register an already known VM config again
658 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
659 true /* fPermitInaccessible */,
660 false /* aDoSetError */,
661 NULL) != VBOX_E_OBJECT_NOT_FOUND)
662 {
663 throw setError(E_FAIL,
664 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
665 mData->m_strConfigFile.c_str());
666 }
667
668 // use UUID from machine config
669 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
670
671#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
672 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
673 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
674 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
675 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
676#endif
677
678 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
679 {
680 // We just set the inaccessible state and fill the error info allowing the caller
681 // to register the machine with encrypted config even if the password is incorrect
682 mData->mAccessible = FALSE;
683
684 /* fetch the current error info */
685 mData->mAccessError = com::ErrorInfo();
686
687 setError(VBOX_E_PASSWORD_INCORRECT,
688 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
689 mData->pMachineConfigFile->uuid.raw());
690 }
691 else
692 {
693#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
694 if (strPassword.isNotEmpty())
695 {
696 size_t cbKey = strPassword.length() + 1; /* Include terminator */
697 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
698 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
699 }
700#endif
701
702 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
703 if (FAILED(hrc)) throw hrc;
704
705 /* At this point the changing of the current state modification
706 * flag is allowed. */
707 i_allowStateModification();
708
709 i_commit();
710 }
711 }
712 catch (HRESULT err)
713 {
714 /* we assume that error info is set by the thrower */
715 hrc = err;
716 }
717 catch (...)
718 {
719 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
720 }
721 }
722 }
723
724 /* Confirm a successful initialization when it's the case */
725 if (SUCCEEDED(hrc))
726 {
727 if (mData->mAccessible)
728 autoInitSpan.setSucceeded();
729 else
730 {
731 autoInitSpan.setLimited();
732
733 // uninit media from this machine's media registry, or else
734 // reloading the settings will fail
735 mParent->i_unregisterMachineMedia(i_getId());
736 }
737 }
738
739#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
740 if (pCryptoIf)
741 {
742 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
743 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
744 }
745#endif
746
747 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
748 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
749
750 LogFlowThisFuncLeave();
751
752 return hrc;
753}
754
755/**
756 * Initializes a new instance from a machine config that is already in memory
757 * (import OVF case). Since we are importing, the UUID in the machine
758 * config is ignored and we always generate a fresh one.
759 *
760 * @param aParent Associated parent object.
761 * @param strName Name for the new machine; this overrides what is specified in config.
762 * @param strSettingsFilename File name of .vbox file.
763 * @param config Machine configuration loaded and parsed from XML.
764 *
765 * @return Success indicator. if not S_OK, the machine object is invalid
766 */
767HRESULT Machine::init(VirtualBox *aParent,
768 const Utf8Str &strName,
769 const Utf8Str &strSettingsFilename,
770 const settings::MachineConfigFile &config)
771{
772 LogFlowThisFuncEnter();
773
774 /* Enclose the state transition NotReady->InInit->Ready */
775 AutoInitSpan autoInitSpan(this);
776 AssertReturn(autoInitSpan.isOk(), E_FAIL);
777
778 HRESULT hrc = initImpl(aParent, strSettingsFilename);
779 if (FAILED(hrc)) return hrc;
780
781 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
782 if (FAILED(hrc)) return hrc;
783
784 hrc = initDataAndChildObjects();
785 if (SUCCEEDED(hrc))
786 {
787 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
788 mData->mAccessible = TRUE;
789
790 // create empty machine config for instance data
791 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
792
793 // generate fresh UUID, ignore machine config
794 unconst(mData->mUuid).create();
795
796 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
797
798 // override VM name as well, it may be different
799 mUserData->s.strName = strName;
800
801 if (SUCCEEDED(hrc))
802 {
803 /* At this point the changing of the current state modification
804 * flag is allowed. */
805 i_allowStateModification();
806
807 /* commit all changes made during the initialization */
808 i_commit();
809 }
810 }
811
812 /* Confirm a successful initialization when it's the case */
813 if (SUCCEEDED(hrc))
814 {
815 if (mData->mAccessible)
816 autoInitSpan.setSucceeded();
817 else
818 {
819 /* Ignore all errors from unregistering, they would destroy
820- * the more interesting error information we already have,
821- * pinpointing the issue with the VM config. */
822 ErrorInfoKeeper eik;
823
824 autoInitSpan.setLimited();
825
826 // uninit media from this machine's media registry, or else
827 // reloading the settings will fail
828 mParent->i_unregisterMachineMedia(i_getId());
829 }
830 }
831
832 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
833 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
834
835 LogFlowThisFuncLeave();
836
837 return hrc;
838}
839
840/**
841 * Shared code between the various init() implementations.
842 * @param aParent The VirtualBox object.
843 * @param strConfigFile Settings file.
844 * @return
845 */
846HRESULT Machine::initImpl(VirtualBox *aParent,
847 const Utf8Str &strConfigFile)
848{
849 LogFlowThisFuncEnter();
850
851 AssertReturn(aParent, E_INVALIDARG);
852 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
853
854 HRESULT hrc = S_OK;
855
856 /* share the parent weakly */
857 unconst(mParent) = aParent;
858
859 /* allocate the essential machine data structure (the rest will be
860 * allocated later by initDataAndChildObjects() */
861 mData.allocate();
862
863 /* memorize the config file name (as provided) */
864 mData->m_strConfigFile = strConfigFile;
865
866 /* get the full file name */
867 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
868 if (RT_FAILURE(vrc1))
869 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
870 tr("Invalid machine settings file name '%s' (%Rrc)"),
871 strConfigFile.c_str(),
872 vrc1);
873
874#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
875 /** @todo Only create when the machine is going to be encrypted. */
876 /* Non-pageable memory is not accessible for non-VM process */
877 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
878 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
879#endif
880
881 LogFlowThisFuncLeave();
882
883 return hrc;
884}
885
886/**
887 * Tries to create a machine settings file in the path stored in the machine
888 * instance data. Used when a new machine is created to fail gracefully if
889 * the settings file could not be written (e.g. because machine dir is read-only).
890 * @return
891 */
892HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
893{
894 HRESULT hrc = S_OK;
895
896 // when we create a new machine, we must be able to create the settings file
897 RTFILE f = NIL_RTFILE;
898 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
899 if ( RT_SUCCESS(vrc)
900 || vrc == VERR_SHARING_VIOLATION
901 )
902 {
903 if (RT_SUCCESS(vrc))
904 RTFileClose(f);
905 if (!fForceOverwrite)
906 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
907 else
908 {
909 /* try to delete the config file, as otherwise the creation
910 * of a new settings file will fail. */
911 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
912 }
913 }
914 else if ( vrc != VERR_FILE_NOT_FOUND
915 && vrc != VERR_PATH_NOT_FOUND
916 )
917 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
918 mData->m_strConfigFileFull.c_str(), vrc);
919 return hrc;
920}
921
922/**
923 * Initializes the registered machine by loading the settings file.
924 * This method is separated from #init() in order to make it possible to
925 * retry the operation after VirtualBox startup instead of refusing to
926 * startup the whole VirtualBox server in case if the settings file of some
927 * registered VM is invalid or inaccessible.
928 *
929 * @note Must be always called from this object's write lock
930 * (unless called from #init() that doesn't need any locking).
931 * @note Locks the mUSBController method for writing.
932 * @note Subclasses must not call this method.
933 */
934HRESULT Machine::i_registeredInit()
935{
936 AssertReturn(!i_isSessionMachine(), E_FAIL);
937 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
938 AssertReturn(mData->mUuid.isValid(), E_FAIL);
939 AssertReturn(!mData->mAccessible, E_FAIL);
940
941 HRESULT hrc = initDataAndChildObjects();
942 if (SUCCEEDED(hrc))
943 {
944 /* Temporarily reset the registered flag in order to let setters
945 * potentially called from loadSettings() succeed (isMutable() used in
946 * all setters will return FALSE for a Machine instance if mRegistered
947 * is TRUE). */
948 mData->mRegistered = FALSE;
949
950 PCVBOXCRYPTOIF pCryptoIf = NULL;
951 SecretKey *pKey = NULL;
952 const char *pszPassword = NULL;
953#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
954 /* Resolve password and cryptographic support interface if machine is encrypted. */
955 if (mData->mstrKeyId.isNotEmpty())
956 {
957 /* Get at the crpytographic interface. */
958 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
959 if (SUCCEEDED(hrc))
960 {
961 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
962 if (RT_SUCCESS(vrc))
963 pszPassword = (const char *)pKey->getKeyBuffer();
964 else
965 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
966 mData->mstrKeyId.c_str(), vrc);
967 }
968 }
969#else
970 RT_NOREF(pKey);
971#endif
972
973 if (SUCCEEDED(hrc))
974 {
975 try
976 {
977 // load and parse machine XML; this will throw on XML or logic errors
978 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
979 pCryptoIf, pszPassword);
980
981 if (mData->mUuid != mData->pMachineConfigFile->uuid)
982 throw setError(E_FAIL,
983 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
984 mData->pMachineConfigFile->uuid.raw(),
985 mData->m_strConfigFileFull.c_str(),
986 mData->mUuid.toString().c_str(),
987 mParent->i_settingsFilePath().c_str());
988
989#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
990 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
991 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
992 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
993 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
994
995 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
996 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
997 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
998 mData->pMachineConfigFile->uuid.raw());
999 else
1000#endif
1001 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
1002 if (FAILED(hrc)) throw hrc;
1003 }
1004 catch (HRESULT err)
1005 {
1006 /* we assume that error info is set by the thrower */
1007 hrc = err;
1008 }
1009 catch (...)
1010 {
1011 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1012 }
1013
1014 /* Restore the registered flag (even on failure) */
1015 mData->mRegistered = TRUE;
1016 }
1017
1018#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1019 if (pCryptoIf)
1020 mParent->i_releaseCryptoIf(pCryptoIf);
1021 if (pKey)
1022 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1023#endif
1024 }
1025
1026 if (SUCCEEDED(hrc))
1027 {
1028 /* Set mAccessible to TRUE only if we successfully locked and loaded
1029 * the settings file */
1030 mData->mAccessible = TRUE;
1031
1032 /* commit all changes made during loading the settings file */
1033 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1034 /// @todo r=klaus for some reason the settings loading logic backs up
1035 // the settings, and therefore a commit is needed. Should probably be changed.
1036 }
1037 else
1038 {
1039 /* If the machine is registered, then, instead of returning a
1040 * failure, we mark it as inaccessible and set the result to
1041 * success to give it a try later */
1042
1043 /* fetch the current error info */
1044 mData->mAccessError = com::ErrorInfo();
1045 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1046
1047 /* rollback all changes */
1048 i_rollback(false /* aNotify */);
1049
1050 // uninit media from this machine's media registry, or else
1051 // reloading the settings will fail
1052 mParent->i_unregisterMachineMedia(i_getId());
1053
1054 /* uninitialize the common part to make sure all data is reset to
1055 * default (null) values */
1056 uninitDataAndChildObjects();
1057
1058 hrc = S_OK;
1059 }
1060
1061 return hrc;
1062}
1063
1064/**
1065 * Uninitializes the instance.
1066 * Called either from FinalRelease() or by the parent when it gets destroyed.
1067 *
1068 * @note The caller of this method must make sure that this object
1069 * a) doesn't have active callers on the current thread and b) is not locked
1070 * by the current thread; otherwise uninit() will hang either a) due to
1071 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1072 * a dead-lock caused by this thread waiting for all callers on the other
1073 * threads are done but preventing them from doing so by holding a lock.
1074 */
1075void Machine::uninit()
1076{
1077 LogFlowThisFuncEnter();
1078
1079 Assert(!isWriteLockOnCurrentThread());
1080
1081 Assert(!uRegistryNeedsSaving);
1082 if (uRegistryNeedsSaving)
1083 {
1084 AutoCaller autoCaller(this);
1085 if (SUCCEEDED(autoCaller.hrc()))
1086 {
1087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1089 }
1090 }
1091
1092 /* Enclose the state transition Ready->InUninit->NotReady */
1093 AutoUninitSpan autoUninitSpan(this);
1094 if (autoUninitSpan.uninitDone())
1095 return;
1096
1097 Assert(!i_isSnapshotMachine());
1098 Assert(!i_isSessionMachine());
1099 Assert(!!mData);
1100
1101 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1102 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1103
1104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1105
1106 if (!mData->mSession.mMachine.isNull())
1107 {
1108 /* Theoretically, this can only happen if the VirtualBox server has been
1109 * terminated while there were clients running that owned open direct
1110 * sessions. Since in this case we are definitely called by
1111 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1112 * won't happen on the client watcher thread (because it has a
1113 * VirtualBox caller for the duration of the
1114 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1115 * cannot happen until the VirtualBox caller is released). This is
1116 * important, because SessionMachine::uninit() cannot correctly operate
1117 * after we return from this method (it expects the Machine instance is
1118 * still valid). We'll call it ourselves below.
1119 */
1120 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1121 (SessionMachine*)mData->mSession.mMachine));
1122
1123 if (Global::IsOnlineOrTransient(mData->mMachineState))
1124 {
1125 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1126 /* set machine state using SessionMachine reimplementation */
1127 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1128 }
1129
1130 /*
1131 * Uninitialize SessionMachine using public uninit() to indicate
1132 * an unexpected uninitialization.
1133 */
1134 mData->mSession.mMachine->uninit();
1135 /* SessionMachine::uninit() must set mSession.mMachine to null */
1136 Assert(mData->mSession.mMachine.isNull());
1137 }
1138
1139 // uninit media from this machine's media registry, if they're still there
1140 Guid uuidMachine(i_getId());
1141
1142 /* the lock is no more necessary (SessionMachine is uninitialized) */
1143 alock.release();
1144
1145 /* XXX This will fail with
1146 * "cannot be closed because it is still attached to 1 virtual machines"
1147 * because at this point we did not call uninitDataAndChildObjects() yet
1148 * and therefore also removeBackReference() for all these media was not called! */
1149
1150 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1151 mParent->i_unregisterMachineMedia(uuidMachine);
1152
1153 // has machine been modified?
1154 if (mData->flModifications)
1155 {
1156 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1157 i_rollback(false /* aNotify */);
1158 }
1159
1160 if (mData->mAccessible)
1161 uninitDataAndChildObjects();
1162
1163#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1164 if (mData->mpKeyStore != NULL)
1165 delete mData->mpKeyStore;
1166#endif
1167
1168 /* free the essential data structure last */
1169 mData.free();
1170
1171 LogFlowThisFuncLeave();
1172}
1173
1174// Wrapped IMachine properties
1175/////////////////////////////////////////////////////////////////////////////
1176HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1177{
1178 /* mParent is constant during life time, no need to lock */
1179 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1180 aParent = pVirtualBox;
1181
1182 return S_OK;
1183}
1184
1185
1186HRESULT Machine::getAccessible(BOOL *aAccessible)
1187{
1188 /* In some cases (medium registry related), it is necessary to be able to
1189 * go through the list of all machines. Happens when an inaccessible VM
1190 * has a sensible medium registry. */
1191 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT hrc = S_OK;
1195
1196 if (!mData->mAccessible)
1197 {
1198 /* try to initialize the VM once more if not accessible */
1199
1200 AutoReinitSpan autoReinitSpan(this);
1201 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1202
1203#ifdef DEBUG
1204 LogFlowThisFunc(("Dumping media backreferences\n"));
1205 mParent->i_dumpAllBackRefs();
1206#endif
1207
1208 if (mData->pMachineConfigFile)
1209 {
1210 // reset the XML file to force loadSettings() (called from i_registeredInit())
1211 // to parse it again; the file might have changed
1212 delete mData->pMachineConfigFile;
1213 mData->pMachineConfigFile = NULL;
1214 }
1215
1216 hrc = i_registeredInit();
1217
1218 if (SUCCEEDED(hrc) && mData->mAccessible)
1219 {
1220 autoReinitSpan.setSucceeded();
1221
1222 /* make sure interesting parties will notice the accessibility
1223 * state change */
1224 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1225 mParent->i_onMachineDataChanged(mData->mUuid);
1226 }
1227 }
1228
1229 if (SUCCEEDED(hrc))
1230 *aAccessible = mData->mAccessible;
1231
1232 LogFlowThisFuncLeave();
1233
1234 return hrc;
1235}
1236
1237HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1238{
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1242 {
1243 /* return shortly */
1244 aAccessError = NULL;
1245 return S_OK;
1246 }
1247
1248 HRESULT hrc = S_OK;
1249
1250 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1251 hrc = errorInfo.createObject();
1252 if (SUCCEEDED(hrc))
1253 {
1254 errorInfo->init(mData->mAccessError.getResultCode(),
1255 mData->mAccessError.getInterfaceID().ref(),
1256 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1257 Utf8Str(mData->mAccessError.getText()));
1258 aAccessError = errorInfo;
1259 }
1260
1261 return hrc;
1262}
1263
1264HRESULT Machine::getName(com::Utf8Str &aName)
1265{
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 aName = mUserData->s.strName;
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::setName(const com::Utf8Str &aName)
1274{
1275 // prohibit setting a UUID only as the machine name, or else it can
1276 // never be found by findMachine()
1277 Guid test(aName);
1278
1279 if (test.isValid())
1280 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1281
1282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1283
1284 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1285 if (FAILED(hrc)) return hrc;
1286
1287 i_setModified(IsModified_MachineData);
1288 mUserData.backup();
1289 mUserData->s.strName = aName;
1290
1291 return S_OK;
1292}
1293
1294HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1295{
1296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 aDescription = mUserData->s.strDescription;
1299
1300 return S_OK;
1301}
1302
1303HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1304{
1305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1306
1307 // this can be done in principle in any state as it doesn't affect the VM
1308 // significantly, but play safe by not messing around while complex
1309 // activities are going on
1310 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1311 if (FAILED(hrc)) return hrc;
1312
1313 i_setModified(IsModified_MachineData);
1314 mUserData.backup();
1315 mUserData->s.strDescription = aDescription;
1316
1317 return S_OK;
1318}
1319
1320HRESULT Machine::getId(com::Guid &aId)
1321{
1322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 aId = mData->mUuid;
1325
1326 return S_OK;
1327}
1328
1329HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1330{
1331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1332 aGroups.resize(mUserData->s.llGroups.size());
1333 size_t i = 0;
1334 for (StringsList::const_iterator
1335 it = mUserData->s.llGroups.begin();
1336 it != mUserData->s.llGroups.end();
1337 ++it, ++i)
1338 aGroups[i] = (*it);
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1344{
1345 StringsList llGroups;
1346 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1347 if (FAILED(hrc))
1348 return hrc;
1349
1350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1353 if (FAILED(hrc)) return hrc;
1354
1355 i_setModified(IsModified_MachineData);
1356 mUserData.backup();
1357 mUserData->s.llGroups = llGroups;
1358
1359 mParent->i_onMachineGroupsChanged(mData->mUuid);
1360 return S_OK;
1361}
1362
1363HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1364{
1365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 aOSTypeId = mUserData->s.strOsType;
1368
1369 return S_OK;
1370}
1371
1372HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1373{
1374 /* look up the object by Id to check it is valid */
1375 ComObjPtr<GuestOSType> pGuestOSType;
1376 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1377
1378 /* when setting, always use the "etalon" value for consistency -- lookup
1379 * by ID is case-insensitive and the input value may have different case */
1380 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1381
1382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1385 if (FAILED(hrc)) return hrc;
1386
1387 i_setModified(IsModified_MachineData);
1388 mUserData.backup();
1389 mUserData->s.strOsType = osTypeId;
1390
1391 return S_OK;
1392}
1393
1394HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1395{
1396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 *aFirmwareType = mHWData->mFirmwareType;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1404{
1405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1408 if (FAILED(hrc)) return hrc;
1409
1410 i_setModified(IsModified_MachineData);
1411 mHWData.backup();
1412 mHWData->mFirmwareType = aFirmwareType;
1413 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1414 alock.release();
1415
1416 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1431{
1432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1435 if (FAILED(hrc)) return hrc;
1436
1437 i_setModified(IsModified_MachineData);
1438 mHWData.backup();
1439 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1445{
1446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 *aPointingHIDType = mHWData->mPointingHIDType;
1449
1450 return S_OK;
1451}
1452
1453HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1454{
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(hrc)) return hrc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mPointingHIDType = aPointingHIDType;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aChipsetType = mHWData->mChipsetType;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1477{
1478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1481 if (FAILED(hrc)) return hrc;
1482
1483 if (aChipsetType != mHWData->mChipsetType)
1484 {
1485 i_setModified(IsModified_MachineData);
1486 mHWData.backup();
1487 mHWData->mChipsetType = aChipsetType;
1488
1489 // Resize network adapter array, to be finalized on commit/rollback.
1490 // We must not throw away entries yet, otherwise settings are lost
1491 // without a way to roll back.
1492 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1493 size_t oldCount = mNetworkAdapters.size();
1494 if (newCount > oldCount)
1495 {
1496 mNetworkAdapters.resize(newCount);
1497 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1498 {
1499 unconst(mNetworkAdapters[slot]).createObject();
1500 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1501 }
1502 }
1503 }
1504
1505 return S_OK;
1506}
1507
1508HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1509{
1510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1511
1512 *aIommuType = mHWData->mIommuType;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::setIommuType(IommuType_T aIommuType)
1518{
1519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1522 if (FAILED(hrc)) return hrc;
1523
1524 if (aIommuType != mHWData->mIommuType)
1525 {
1526 if (aIommuType == IommuType_Intel)
1527 {
1528#ifndef VBOX_WITH_IOMMU_INTEL
1529 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1530 return E_UNEXPECTED;
1531#endif
1532 }
1533
1534 i_setModified(IsModified_MachineData);
1535 mHWData.backup();
1536 mHWData->mIommuType = aIommuType;
1537 }
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1543{
1544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 aParavirtDebug = mHWData->mParavirtDebug;
1547 return S_OK;
1548}
1549
1550HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1551{
1552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1555 if (FAILED(hrc)) return hrc;
1556
1557 /** @todo Parse/validate options? */
1558 if (aParavirtDebug != mHWData->mParavirtDebug)
1559 {
1560 i_setModified(IsModified_MachineData);
1561 mHWData.backup();
1562 mHWData->mParavirtDebug = aParavirtDebug;
1563 }
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1569{
1570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 *aParavirtProvider = mHWData->mParavirtProvider;
1573
1574 return S_OK;
1575}
1576
1577HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1578{
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(hrc)) return hrc;
1583
1584 if (aParavirtProvider != mHWData->mParavirtProvider)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588 mHWData->mParavirtProvider = aParavirtProvider;
1589 }
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1595{
1596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 *aParavirtProvider = mHWData->mParavirtProvider;
1599 switch (mHWData->mParavirtProvider)
1600 {
1601 case ParavirtProvider_None:
1602 case ParavirtProvider_HyperV:
1603 case ParavirtProvider_KVM:
1604 case ParavirtProvider_Minimal:
1605 break;
1606
1607 /* Resolve dynamic provider types to the effective types. */
1608 default:
1609 {
1610 ComObjPtr<GuestOSType> pGuestOSType;
1611 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1612 pGuestOSType);
1613 if (FAILED(hrc2) || pGuestOSType.isNull())
1614 {
1615 *aParavirtProvider = ParavirtProvider_None;
1616 break;
1617 }
1618
1619 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1620 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1621
1622 switch (mHWData->mParavirtProvider)
1623 {
1624 case ParavirtProvider_Legacy:
1625 {
1626 if (fOsXGuest)
1627 *aParavirtProvider = ParavirtProvider_Minimal;
1628 else
1629 *aParavirtProvider = ParavirtProvider_None;
1630 break;
1631 }
1632
1633 case ParavirtProvider_Default:
1634 {
1635 if (fOsXGuest)
1636 *aParavirtProvider = ParavirtProvider_Minimal;
1637 else if ( mUserData->s.strOsType == "Windows11_64"
1638 || mUserData->s.strOsType == "Windows10"
1639 || mUserData->s.strOsType == "Windows10_64"
1640 || mUserData->s.strOsType == "Windows81"
1641 || mUserData->s.strOsType == "Windows81_64"
1642 || mUserData->s.strOsType == "Windows8"
1643 || mUserData->s.strOsType == "Windows8_64"
1644 || mUserData->s.strOsType == "Windows7"
1645 || mUserData->s.strOsType == "Windows7_64"
1646 || mUserData->s.strOsType == "WindowsVista"
1647 || mUserData->s.strOsType == "WindowsVista_64"
1648 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1649 || mUserData->s.strOsType.startsWith("Windows201"))
1650 && mUserData->s.strOsType.endsWith("_64"))
1651 || mUserData->s.strOsType == "Windows2012"
1652 || mUserData->s.strOsType == "Windows2012_64"
1653 || mUserData->s.strOsType == "Windows2008"
1654 || mUserData->s.strOsType == "Windows2008_64")
1655 {
1656 *aParavirtProvider = ParavirtProvider_HyperV;
1657 }
1658 else if (guestTypeFamilyId == "Linux" &&
1659 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1660 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1661 mUserData->s.strOsType != "Linux24_64")
1662 {
1663 *aParavirtProvider = ParavirtProvider_KVM;
1664 }
1665 else
1666 *aParavirtProvider = ParavirtProvider_None;
1667 break;
1668 }
1669
1670 default: AssertFailedBreak(); /* Shut up MSC. */
1671 }
1672 break;
1673 }
1674 }
1675
1676 Assert( *aParavirtProvider == ParavirtProvider_None
1677 || *aParavirtProvider == ParavirtProvider_Minimal
1678 || *aParavirtProvider == ParavirtProvider_HyperV
1679 || *aParavirtProvider == ParavirtProvider_KVM);
1680 return S_OK;
1681}
1682
1683HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1684{
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 aHardwareVersion = mHWData->mHWVersion;
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1693{
1694 /* check known version */
1695 Utf8Str hwVersion = aHardwareVersion;
1696 if ( hwVersion.compare("1") != 0
1697 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1698 return setError(E_INVALIDARG,
1699 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1700
1701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1704 if (FAILED(hrc)) return hrc;
1705
1706 i_setModified(IsModified_MachineData);
1707 mHWData.backup();
1708 mHWData->mHWVersion = aHardwareVersion;
1709
1710 return S_OK;
1711}
1712
1713HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1714{
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 if (!mHWData->mHardwareUUID.isZero())
1718 aHardwareUUID = mHWData->mHardwareUUID;
1719 else
1720 aHardwareUUID = mData->mUuid;
1721
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1726{
1727 if (!aHardwareUUID.isValid())
1728 return E_INVALIDARG;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1733 if (FAILED(hrc)) return hrc;
1734
1735 i_setModified(IsModified_MachineData);
1736 mHWData.backup();
1737 if (aHardwareUUID == mData->mUuid)
1738 mHWData->mHardwareUUID.clear();
1739 else
1740 mHWData->mHardwareUUID = aHardwareUUID;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aMemorySize = mHWData->mMemorySize;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setMemorySize(ULONG aMemorySize)
1755{
1756 /* check RAM limits */
1757 if ( aMemorySize < MM_RAM_MIN_IN_MB
1758 || aMemorySize > MM_RAM_MAX_IN_MB
1759 )
1760 return setError(E_INVALIDARG,
1761 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1762 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1767 if (FAILED(hrc)) return hrc;
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mMemorySize = aMemorySize;
1772
1773 return S_OK;
1774}
1775
1776HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1777{
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 *aCPUCount = mHWData->mCPUCount;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::setCPUCount(ULONG aCPUCount)
1786{
1787 /* check CPU limits */
1788 if ( aCPUCount < SchemaDefs::MinCPUCount
1789 || aCPUCount > SchemaDefs::MaxCPUCount
1790 )
1791 return setError(E_INVALIDARG,
1792 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1793 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1794
1795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1798 if (mHWData->mCPUHotPlugEnabled)
1799 {
1800 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1801 {
1802 if (mHWData->mCPUAttached[idx])
1803 return setError(E_INVALIDARG,
1804 tr("There is still a CPU attached to socket %lu."
1805 "Detach the CPU before removing the socket"),
1806 aCPUCount, idx+1);
1807 }
1808 }
1809
1810 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1811 if (FAILED(hrc)) return hrc;
1812
1813 i_setModified(IsModified_MachineData);
1814 mHWData.backup();
1815 mHWData->mCPUCount = aCPUCount;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1830{
1831 /* check throttle limits */
1832 if ( aCPUExecutionCap < 1
1833 || aCPUExecutionCap > 100
1834 )
1835 return setError(E_INVALIDARG,
1836 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1837 aCPUExecutionCap, 1, 100);
1838
1839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1842 if (FAILED(hrc)) return hrc;
1843
1844 alock.release();
1845 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1846 alock.acquire();
1847 if (FAILED(hrc)) return hrc;
1848
1849 i_setModified(IsModified_MachineData);
1850 mHWData.backup();
1851 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1852
1853 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1854 if (Global::IsOnline(mData->mMachineState))
1855 i_saveSettings(NULL, alock);
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1870{
1871 HRESULT hrc = S_OK;
1872
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 hrc = i_checkStateDependency(MutableStateDep);
1876 if (FAILED(hrc)) return hrc;
1877
1878 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1879 {
1880 if (aCPUHotPlugEnabled)
1881 {
1882 i_setModified(IsModified_MachineData);
1883 mHWData.backup();
1884
1885 /* Add the amount of CPUs currently attached */
1886 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1887 mHWData->mCPUAttached[i] = true;
1888 }
1889 else
1890 {
1891 /*
1892 * We can disable hotplug only if the amount of maximum CPUs is equal
1893 * to the amount of attached CPUs
1894 */
1895 unsigned cCpusAttached = 0;
1896 unsigned iHighestId = 0;
1897
1898 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1899 {
1900 if (mHWData->mCPUAttached[i])
1901 {
1902 cCpusAttached++;
1903 iHighestId = i;
1904 }
1905 }
1906
1907 if ( (cCpusAttached != mHWData->mCPUCount)
1908 || (iHighestId >= mHWData->mCPUCount))
1909 return setError(E_INVALIDARG,
1910 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 }
1915 }
1916
1917 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1918
1919 return hrc;
1920}
1921
1922HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1927
1928 return S_OK;
1929}
1930
1931HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1932{
1933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1934
1935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1936 if (SUCCEEDED(hrc))
1937 {
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1941 }
1942 return hrc;
1943}
1944
1945HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1946{
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948 aCPUProfile = mHWData->mCpuProfile;
1949 return S_OK;
1950}
1951
1952HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1953{
1954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1955 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1956 if (SUCCEEDED(hrc))
1957 {
1958 i_setModified(IsModified_MachineData);
1959 mHWData.backup();
1960 /* Empty equals 'host'. */
1961 if (aCPUProfile.isNotEmpty())
1962 mHWData->mCpuProfile = aCPUProfile;
1963 else
1964 mHWData->mCpuProfile = "host";
1965 }
1966 return hrc;
1967}
1968
1969HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1970{
1971#ifdef VBOX_WITH_USB_CARDREADER
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1975
1976 return S_OK;
1977#else
1978 NOREF(aEmulatedUSBCardReaderEnabled);
1979 return E_NOTIMPL;
1980#endif
1981}
1982
1983HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1984{
1985#ifdef VBOX_WITH_USB_CARDREADER
1986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1989 if (FAILED(hrc)) return hrc;
1990
1991 i_setModified(IsModified_MachineData);
1992 mHWData.backup();
1993 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1994
1995 return S_OK;
1996#else
1997 NOREF(aEmulatedUSBCardReaderEnabled);
1998 return E_NOTIMPL;
1999#endif
2000}
2001
2002HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2003{
2004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 *aHPETEnabled = mHWData->mHPETEnabled;
2007
2008 return S_OK;
2009}
2010
2011HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2012{
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(hrc)) return hrc;
2017
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020
2021 mHWData->mHPETEnabled = aHPETEnabled;
2022
2023 return hrc;
2024}
2025
2026/** @todo this method should not be public */
2027HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2032
2033 return S_OK;
2034}
2035
2036/**
2037 * Set the memory balloon size.
2038 *
2039 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2040 * we have to make sure that we never call IGuest from here.
2041 */
2042HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2043{
2044 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2045#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2046 /* check limits */
2047 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2048 return setError(E_INVALIDARG,
2049 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2050 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2051
2052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2055 if (FAILED(hrc)) return hrc;
2056
2057 i_setModified(IsModified_MachineData);
2058 mHWData.backup();
2059 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2060
2061 return S_OK;
2062#else
2063 NOREF(aMemoryBalloonSize);
2064 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2065#endif
2066}
2067
2068HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2069{
2070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2071
2072 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2073 return S_OK;
2074}
2075
2076HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2077{
2078#ifdef VBOX_WITH_PAGE_SHARING
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2082 if (FAILED(hrc)) return hrc;
2083
2084 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2088 return S_OK;
2089#else
2090 NOREF(aPageFusionEnabled);
2091 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2096{
2097 /* mBIOSSettings is constant during life time, no need to lock */
2098 aBIOSSettings = mBIOSSettings;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2104{
2105 /* mTrustedPlatformModule is constant during life time, no need to lock */
2106 aTrustedPlatformModule = mTrustedPlatformModule;
2107
2108 return S_OK;
2109}
2110
2111HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2112{
2113 /* mNvramStore is constant during life time, no need to lock */
2114 aNvramStore = mNvramStore;
2115
2116 return S_OK;
2117}
2118
2119HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 aRecordingSettings = mRecordingSettings;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2129{
2130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 aGraphicsAdapter = mGraphicsAdapter;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2138{
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 switch (aProperty)
2142 {
2143 case CPUPropertyType_PAE:
2144 *aValue = mHWData->mPAEEnabled;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComObjPtr<GuestOSType> pGuestOSType;
2161 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2162 pGuestOSType);
2163 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2164 {
2165 if (pGuestOSType->i_is64Bit())
2166 {
2167 ComObjPtr<Host> pHost = mParent->i_host();
2168 alock.release();
2169
2170 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2171 if (FAILED(hrc2))
2172 *aValue = FALSE;
2173 }
2174 }
2175 }
2176#endif
2177 break;
2178
2179 case CPUPropertyType_TripleFaultReset:
2180 *aValue = mHWData->mTripleFaultReset;
2181 break;
2182
2183 case CPUPropertyType_APIC:
2184 *aValue = mHWData->mAPIC;
2185 break;
2186
2187 case CPUPropertyType_X2APIC:
2188 *aValue = mHWData->mX2APIC;
2189 break;
2190
2191 case CPUPropertyType_IBPBOnVMExit:
2192 *aValue = mHWData->mIBPBOnVMExit;
2193 break;
2194
2195 case CPUPropertyType_IBPBOnVMEntry:
2196 *aValue = mHWData->mIBPBOnVMEntry;
2197 break;
2198
2199 case CPUPropertyType_SpecCtrl:
2200 *aValue = mHWData->mSpecCtrl;
2201 break;
2202
2203 case CPUPropertyType_SpecCtrlByHost:
2204 *aValue = mHWData->mSpecCtrlByHost;
2205 break;
2206
2207 case CPUPropertyType_HWVirt:
2208 *aValue = mHWData->mNestedHWVirt;
2209 break;
2210
2211 case CPUPropertyType_L1DFlushOnEMTScheduling:
2212 *aValue = mHWData->mL1DFlushOnSched;
2213 break;
2214
2215 case CPUPropertyType_L1DFlushOnVMEntry:
2216 *aValue = mHWData->mL1DFlushOnVMEntry;
2217 break;
2218
2219 case CPUPropertyType_MDSClearOnEMTScheduling:
2220 *aValue = mHWData->mMDSClearOnSched;
2221 break;
2222
2223 case CPUPropertyType_MDSClearOnVMEntry:
2224 *aValue = mHWData->mMDSClearOnVMEntry;
2225 break;
2226
2227 default:
2228 return E_INVALIDARG;
2229 }
2230 return S_OK;
2231}
2232
2233HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2234{
2235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2236
2237 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2238 if (FAILED(hrc)) return hrc;
2239
2240 switch (aProperty)
2241 {
2242 case CPUPropertyType_PAE:
2243 i_setModified(IsModified_MachineData);
2244 mHWData.backup();
2245 mHWData->mPAEEnabled = !!aValue;
2246 break;
2247
2248 case CPUPropertyType_LongMode:
2249 i_setModified(IsModified_MachineData);
2250 mHWData.backup();
2251 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2252 break;
2253
2254 case CPUPropertyType_TripleFaultReset:
2255 i_setModified(IsModified_MachineData);
2256 mHWData.backup();
2257 mHWData->mTripleFaultReset = !!aValue;
2258 break;
2259
2260 case CPUPropertyType_APIC:
2261 if (mHWData->mX2APIC)
2262 aValue = TRUE;
2263 i_setModified(IsModified_MachineData);
2264 mHWData.backup();
2265 mHWData->mAPIC = !!aValue;
2266 break;
2267
2268 case CPUPropertyType_X2APIC:
2269 i_setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 mHWData->mX2APIC = !!aValue;
2272 if (aValue)
2273 mHWData->mAPIC = !!aValue;
2274 break;
2275
2276 case CPUPropertyType_IBPBOnVMExit:
2277 i_setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mIBPBOnVMExit = !!aValue;
2280 break;
2281
2282 case CPUPropertyType_IBPBOnVMEntry:
2283 i_setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mIBPBOnVMEntry = !!aValue;
2286 break;
2287
2288 case CPUPropertyType_SpecCtrl:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mSpecCtrl = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_SpecCtrlByHost:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mSpecCtrlByHost = !!aValue;
2298 break;
2299
2300 case CPUPropertyType_HWVirt:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mNestedHWVirt = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_L1DFlushOnEMTScheduling:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mL1DFlushOnSched = !!aValue;
2310 break;
2311
2312 case CPUPropertyType_L1DFlushOnVMEntry:
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mL1DFlushOnVMEntry = !!aValue;
2316 break;
2317
2318 case CPUPropertyType_MDSClearOnEMTScheduling:
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mMDSClearOnSched = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_MDSClearOnVMEntry:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mMDSClearOnVMEntry = !!aValue;
2328 break;
2329
2330 default:
2331 return E_INVALIDARG;
2332 }
2333 return S_OK;
2334}
2335
2336HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2337 ULONG *aValEcx, ULONG *aValEdx)
2338{
2339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2340 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2341 {
2342 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2343 it != mHWData->mCpuIdLeafList.end();
2344 ++it)
2345 {
2346 if (aOrdinal == 0)
2347 {
2348 const settings::CpuIdLeaf &rLeaf= *it;
2349 *aIdx = rLeaf.idx;
2350 *aSubIdx = rLeaf.idxSub;
2351 *aValEax = rLeaf.uEax;
2352 *aValEbx = rLeaf.uEbx;
2353 *aValEcx = rLeaf.uEcx;
2354 *aValEdx = rLeaf.uEdx;
2355 return S_OK;
2356 }
2357 aOrdinal--;
2358 }
2359 }
2360 return E_INVALIDARG;
2361}
2362
2363HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2364{
2365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 /*
2368 * Search the list.
2369 */
2370 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2371 {
2372 const settings::CpuIdLeaf &rLeaf= *it;
2373 if ( rLeaf.idx == aIdx
2374 && ( aSubIdx == UINT32_MAX
2375 || rLeaf.idxSub == aSubIdx) )
2376 {
2377 *aValEax = rLeaf.uEax;
2378 *aValEbx = rLeaf.uEbx;
2379 *aValEcx = rLeaf.uEcx;
2380 *aValEdx = rLeaf.uEdx;
2381 return S_OK;
2382 }
2383 }
2384
2385 return E_INVALIDARG;
2386}
2387
2388
2389HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2390{
2391 /*
2392 * Validate input before taking locks and checking state.
2393 */
2394 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2395 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2396 if ( aIdx >= UINT32_C(0x20)
2397 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2398 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2399 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2400
2401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2402 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2403 if (FAILED(hrc)) return hrc;
2404
2405 /*
2406 * Impose a maximum number of leaves.
2407 */
2408 if (mHWData->mCpuIdLeafList.size() > 256)
2409 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2410
2411 /*
2412 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2413 */
2414 i_setModified(IsModified_MachineData);
2415 mHWData.backup();
2416
2417 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2418 {
2419 settings::CpuIdLeaf &rLeaf= *it;
2420 if ( rLeaf.idx == aIdx
2421 && ( aSubIdx == UINT32_MAX
2422 || rLeaf.idxSub == aSubIdx) )
2423 it = mHWData->mCpuIdLeafList.erase(it);
2424 else
2425 ++it;
2426 }
2427
2428 settings::CpuIdLeaf NewLeaf;
2429 NewLeaf.idx = aIdx;
2430 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2431 NewLeaf.uEax = aValEax;
2432 NewLeaf.uEbx = aValEbx;
2433 NewLeaf.uEcx = aValEcx;
2434 NewLeaf.uEdx = aValEdx;
2435 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2436 return S_OK;
2437}
2438
2439HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2440{
2441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2444 if (FAILED(hrc)) return hrc;
2445
2446 /*
2447 * Do the removal.
2448 */
2449 bool fModified = mHWData.isBackedUp();
2450 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2451 {
2452 settings::CpuIdLeaf &rLeaf= *it;
2453 if ( rLeaf.idx == aIdx
2454 && ( aSubIdx == UINT32_MAX
2455 || rLeaf.idxSub == aSubIdx) )
2456 {
2457 if (!fModified)
2458 {
2459 fModified = true;
2460 i_setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 // Start from the beginning, since mHWData.backup() creates
2463 // a new list, causing iterator mixup. This makes sure that
2464 // the settings are not unnecessarily marked as modified,
2465 // at the price of extra list walking.
2466 it = mHWData->mCpuIdLeafList.begin();
2467 }
2468 else
2469 it = mHWData->mCpuIdLeafList.erase(it);
2470 }
2471 else
2472 ++it;
2473 }
2474
2475 return S_OK;
2476}
2477
2478HRESULT Machine::removeAllCPUIDLeaves()
2479{
2480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2483 if (FAILED(hrc)) return hrc;
2484
2485 if (mHWData->mCpuIdLeafList.size() > 0)
2486 {
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489
2490 mHWData->mCpuIdLeafList.clear();
2491 }
2492
2493 return S_OK;
2494}
2495HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2496{
2497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 switch(aProperty)
2500 {
2501 case HWVirtExPropertyType_Enabled:
2502 *aValue = mHWData->mHWVirtExEnabled;
2503 break;
2504
2505 case HWVirtExPropertyType_VPID:
2506 *aValue = mHWData->mHWVirtExVPIDEnabled;
2507 break;
2508
2509 case HWVirtExPropertyType_NestedPaging:
2510 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2511 break;
2512
2513 case HWVirtExPropertyType_UnrestrictedExecution:
2514 *aValue = mHWData->mHWVirtExUXEnabled;
2515 break;
2516
2517 case HWVirtExPropertyType_LargePages:
2518 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2519 break;
2520
2521 case HWVirtExPropertyType_Force:
2522 *aValue = mHWData->mHWVirtExForceEnabled;
2523 break;
2524
2525 case HWVirtExPropertyType_UseNativeApi:
2526 *aValue = mHWData->mHWVirtExUseNativeApi;
2527 break;
2528
2529 case HWVirtExPropertyType_VirtVmsaveVmload:
2530 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2531 break;
2532
2533 default:
2534 return E_INVALIDARG;
2535 }
2536 return S_OK;
2537}
2538
2539HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2540{
2541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2544 if (FAILED(hrc)) return hrc;
2545
2546 switch (aProperty)
2547 {
2548 case HWVirtExPropertyType_Enabled:
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mHWVirtExEnabled = !!aValue;
2552 break;
2553
2554 case HWVirtExPropertyType_VPID:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_NestedPaging:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_UnrestrictedExecution:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExUXEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_LargePages:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_Force:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExForceEnabled = !!aValue;
2582 break;
2583
2584 case HWVirtExPropertyType_UseNativeApi:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExUseNativeApi = !!aValue;
2588 break;
2589
2590 case HWVirtExPropertyType_VirtVmsaveVmload:
2591 i_setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2594 break;
2595
2596 default:
2597 return E_INVALIDARG;
2598 }
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2613{
2614 /** @todo (r=dmik):
2615 * 1. Allow to change the name of the snapshot folder containing snapshots
2616 * 2. Rename the folder on disk instead of just changing the property
2617 * value (to be smart and not to leave garbage). Note that it cannot be
2618 * done here because the change may be rolled back. Thus, the right
2619 * place is #saveSettings().
2620 */
2621
2622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2625 if (FAILED(hrc)) return hrc;
2626
2627 if (!mData->mCurrentSnapshot.isNull())
2628 return setError(E_FAIL,
2629 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2630
2631 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2632
2633 if (strSnapshotFolder.isEmpty())
2634 strSnapshotFolder = "Snapshots";
2635 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2636 if (RT_FAILURE(vrc))
2637 return setErrorBoth(E_FAIL, vrc,
2638 tr("Invalid snapshot folder '%s' (%Rrc)"),
2639 strSnapshotFolder.c_str(), vrc);
2640
2641 i_setModified(IsModified_MachineData);
2642 mUserData.backup();
2643
2644 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aMediumAttachments.resize(mMediumAttachments->size());
2654 size_t i = 0;
2655 for (MediumAttachmentList::const_iterator
2656 it = mMediumAttachments->begin();
2657 it != mMediumAttachments->end();
2658 ++it, ++i)
2659 aMediumAttachments[i] = *it;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 Assert(!!mVRDEServer);
2669
2670 aVRDEServer = mVRDEServer;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 aAudioSettings = mAudioSettings;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2685{
2686#ifdef VBOX_WITH_VUSB
2687 clearError();
2688 MultiResult hrcMult(S_OK);
2689
2690# ifdef VBOX_WITH_USB
2691 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2692 if (FAILED(hrcMult)) return hrcMult;
2693# endif
2694
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aUSBControllers.resize(mUSBControllers->size());
2698 size_t i = 0;
2699 for (USBControllerList::const_iterator
2700 it = mUSBControllers->begin();
2701 it != mUSBControllers->end();
2702 ++it, ++i)
2703 aUSBControllers[i] = *it;
2704
2705 return S_OK;
2706#else
2707 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2708 * extended error info to indicate that USB is simply not available
2709 * (w/o treating it as a failure), for example, as in OSE */
2710 NOREF(aUSBControllers);
2711 ReturnComNotImplemented();
2712#endif /* VBOX_WITH_VUSB */
2713}
2714
2715HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2716{
2717#ifdef VBOX_WITH_VUSB
2718 clearError();
2719 MultiResult hrcMult(S_OK);
2720
2721# ifdef VBOX_WITH_USB
2722 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2723 if (FAILED(hrcMult)) return hrcMult;
2724# endif
2725
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 aUSBDeviceFilters = mUSBDeviceFilters;
2729 return hrcMult;
2730#else
2731 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2732 * extended error info to indicate that USB is simply not available
2733 * (w/o treating it as a failure), for example, as in OSE */
2734 NOREF(aUSBDeviceFilters);
2735 ReturnComNotImplemented();
2736#endif /* VBOX_WITH_VUSB */
2737}
2738
2739HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aSettingsFilePath = mData->m_strConfigFileFull;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2749{
2750 RT_NOREF(aSettingsFilePath);
2751 ReturnComNotImplemented();
2752}
2753
2754HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2759 if (FAILED(hrc)) return hrc;
2760
2761 if (!mData->pMachineConfigFile->fileExists())
2762 // this is a new machine, and no config file exists yet:
2763 *aSettingsModified = TRUE;
2764 else
2765 *aSettingsModified = (mData->flModifications != 0);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 *aSessionState = mData->mSession.mState;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 aSessionName = mData->mSession.mName;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 *aSessionPID = mData->mSession.mPID;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getState(MachineState_T *aState)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 *aState = mData->mMachineState;
2802 Assert(mData->mMachineState != MachineState_Null);
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 aStateFilePath = mSSData->strStateFilePath;
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 i_getLogFolder(aLogFolder);
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 aCurrentSnapshot = mData->mCurrentSnapshot;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2848 ? 0
2849 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2855{
2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 /* Note: for machines with no snapshots, we always return FALSE
2859 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2860 * reasons :) */
2861
2862 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2863 ? FALSE
2864 : mData->mCurrentStateModified;
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 aSharedFolders.resize(mHWData->mSharedFolders.size());
2874 size_t i = 0;
2875 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2876 it = mHWData->mSharedFolders.begin();
2877 it != mHWData->mSharedFolders.end();
2878 ++it, ++i)
2879 aSharedFolders[i] = *it;
2880
2881 return S_OK;
2882}
2883
2884HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2885{
2886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 *aClipboardMode = mHWData->mClipboardMode;
2889
2890 return S_OK;
2891}
2892
2893HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2894{
2895 HRESULT hrc = S_OK;
2896
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2900 if (FAILED(hrc)) return hrc;
2901
2902 alock.release();
2903 hrc = i_onClipboardModeChange(aClipboardMode);
2904 alock.acquire();
2905 if (FAILED(hrc)) return hrc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mHWData.backup();
2909 mHWData->mClipboardMode = aClipboardMode;
2910
2911 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2912 if (Global::IsOnline(mData->mMachineState))
2913 i_saveSettings(NULL, alock);
2914
2915 return S_OK;
2916}
2917
2918HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2919{
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2923
2924 return S_OK;
2925}
2926
2927HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2928{
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2932 if (FAILED(hrc)) return hrc;
2933
2934 alock.release();
2935 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2936 alock.acquire();
2937 if (FAILED(hrc)) return hrc;
2938
2939 i_setModified(IsModified_MachineData);
2940 mHWData.backup();
2941 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2942
2943 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2944 if (Global::IsOnline(mData->mMachineState))
2945 i_saveSettings(NULL, alock);
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 *aDnDMode = mHWData->mDnDMode;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2964 if (FAILED(hrc)) return hrc;
2965
2966 alock.release();
2967 hrc = i_onDnDModeChange(aDnDMode);
2968
2969 alock.acquire();
2970 if (FAILED(hrc)) return hrc;
2971
2972 i_setModified(IsModified_MachineData);
2973 mHWData.backup();
2974 mHWData->mDnDMode = aDnDMode;
2975
2976 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2977 if (Global::IsOnline(mData->mMachineState))
2978 i_saveSettings(NULL, alock);
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2984{
2985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 aStorageControllers.resize(mStorageControllers->size());
2988 size_t i = 0;
2989 for (StorageControllerList::const_iterator
2990 it = mStorageControllers->begin();
2991 it != mStorageControllers->end();
2992 ++it, ++i)
2993 aStorageControllers[i] = *it;
2994
2995 return S_OK;
2996}
2997
2998HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2999{
3000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 *aEnabled = mUserData->s.fTeleporterEnabled;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* Only allow it to be set to true when PoweredOff or Aborted.
3012 (Clearing it is always permitted.) */
3013 if ( aTeleporterEnabled
3014 && mData->mRegistered
3015 && ( !i_isSessionMachine()
3016 || ( mData->mMachineState != MachineState_PoweredOff
3017 && mData->mMachineState != MachineState_Teleported
3018 && mData->mMachineState != MachineState_Aborted
3019 )
3020 )
3021 )
3022 return setError(VBOX_E_INVALID_VM_STATE,
3023 tr("The machine is not powered off (state is %s)"),
3024 Global::stringifyMachineState(mData->mMachineState));
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3029
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3043{
3044 if (aTeleporterPort >= _64K)
3045 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3046
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3050 if (FAILED(hrc)) return hrc;
3051
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3055
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3069{
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3073 if (FAILED(hrc)) return hrc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3078
3079 return S_OK;
3080}
3081
3082HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3083{
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3091{
3092 /*
3093 * Hash the password first.
3094 */
3095 com::Utf8Str aT = aTeleporterPassword;
3096
3097 if (!aT.isEmpty())
3098 {
3099 if (VBoxIsPasswordHashed(&aT))
3100 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3101 VBoxHashPassword(&aT);
3102 }
3103
3104 /*
3105 * Do the update.
3106 */
3107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3108 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3109 if (SUCCEEDED(hrc))
3110 {
3111 i_setModified(IsModified_MachineData);
3112 mUserData.backup();
3113 mUserData->s.strTeleporterPassword = aT;
3114 }
3115
3116 return hrc;
3117}
3118
3119HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3120{
3121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3124
3125 return S_OK;
3126}
3127
3128HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3129{
3130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 /* Only allow it to be set to true when PoweredOff or Aborted.
3133 (Clearing it is always permitted.) */
3134 if ( aRTCUseUTC
3135 && mData->mRegistered
3136 && ( !i_isSessionMachine()
3137 || ( mData->mMachineState != MachineState_PoweredOff
3138 && mData->mMachineState != MachineState_Teleported
3139 && mData->mMachineState != MachineState_Aborted
3140 )
3141 )
3142 )
3143 return setError(VBOX_E_INVALID_VM_STATE,
3144 tr("The machine is not powered off (state is %s)"),
3145 Global::stringifyMachineState(mData->mMachineState));
3146
3147 i_setModified(IsModified_MachineData);
3148 mUserData.backup();
3149 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3150
3151 return S_OK;
3152}
3153
3154HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3155{
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3159
3160 return S_OK;
3161}
3162
3163HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3164{
3165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3166
3167 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3168 if (FAILED(hrc)) return hrc;
3169
3170 i_setModified(IsModified_MachineData);
3171 mHWData.backup();
3172 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3173
3174 return S_OK;
3175}
3176
3177HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3178{
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 *aIOCacheSize = mHWData->mIOCacheSize;
3182
3183 return S_OK;
3184}
3185
3186HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3187{
3188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3189
3190 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3191 if (FAILED(hrc)) return hrc;
3192
3193 i_setModified(IsModified_MachineData);
3194 mHWData.backup();
3195 mHWData->mIOCacheSize = aIOCacheSize;
3196
3197 return S_OK;
3198}
3199
3200HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3201{
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203
3204#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3205 aKeyId = mSSData->strStateKeyId;
3206#else
3207 aKeyId = com::Utf8Str::Empty;
3208#endif
3209
3210 return S_OK;
3211}
3212
3213HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3214{
3215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3218 aKeyStore = mSSData->strStateKeyStore;
3219#else
3220 aKeyStore = com::Utf8Str::Empty;
3221#endif
3222
3223 return S_OK;
3224}
3225
3226HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3227{
3228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3229
3230#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3231 aKeyId = mData->mstrLogKeyId;
3232#else
3233 aKeyId = com::Utf8Str::Empty;
3234#endif
3235
3236 return S_OK;
3237}
3238
3239HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3240{
3241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3244 aKeyStore = mData->mstrLogKeyStore;
3245#else
3246 aKeyStore = com::Utf8Str::Empty;
3247#endif
3248
3249 return S_OK;
3250}
3251
3252HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
3253{
3254 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
3255
3256 return S_OK;
3257}
3258
3259
3260/**
3261 * @note Locks objects!
3262 */
3263HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3264 LockType_T aLockType)
3265{
3266 /* check the session state */
3267 SessionState_T state;
3268 HRESULT hrc = aSession->COMGETTER(State)(&state);
3269 if (FAILED(hrc)) return hrc;
3270
3271 if (state != SessionState_Unlocked)
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The given session is busy"));
3274
3275 // get the client's IInternalSessionControl interface
3276 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3277 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3278 E_INVALIDARG);
3279
3280 // session name (only used in some code paths)
3281 Utf8Str strSessionName;
3282
3283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3284
3285 if (!mData->mRegistered)
3286 return setError(E_UNEXPECTED,
3287 tr("The machine '%s' is not registered"),
3288 mUserData->s.strName.c_str());
3289
3290 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3291
3292 SessionState_T oldState = mData->mSession.mState;
3293 /* Hack: in case the session is closing and there is a progress object
3294 * which allows waiting for the session to be closed, take the opportunity
3295 * and do a limited wait (max. 1 second). This helps a lot when the system
3296 * is busy and thus session closing can take a little while. */
3297 if ( mData->mSession.mState == SessionState_Unlocking
3298 && mData->mSession.mProgress)
3299 {
3300 alock.release();
3301 mData->mSession.mProgress->WaitForCompletion(1000);
3302 alock.acquire();
3303 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3304 }
3305
3306 // try again now
3307 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3308 // (i.e. session machine exists)
3309 && (aLockType == LockType_Shared) // caller wants a shared link to the
3310 // existing session that holds the write lock:
3311 )
3312 {
3313 // OK, share the session... we are now dealing with three processes:
3314 // 1) VBoxSVC (where this code runs);
3315 // 2) process C: the caller's client process (who wants a shared session);
3316 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3317
3318 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3319 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3320 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3321 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3322 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3323
3324 /*
3325 * Release the lock before calling the client process. It's safe here
3326 * since the only thing to do after we get the lock again is to add
3327 * the remote control to the list (which doesn't directly influence
3328 * anything).
3329 */
3330 alock.release();
3331
3332 // get the console of the session holding the write lock (this is a remote call)
3333 ComPtr<IConsole> pConsoleW;
3334 if (mData->mSession.mLockType == LockType_VM)
3335 {
3336 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3337 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3338 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
3339 if (FAILED(hrc))
3340 // the failure may occur w/o any error info (from RPC), so provide one
3341 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
3342 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3343 }
3344
3345 // share the session machine and W's console with the caller's session
3346 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3347 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3348 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
3349
3350 if (FAILED(hrc))
3351 // the failure may occur w/o any error info (from RPC), so provide one
3352 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
3353 alock.acquire();
3354
3355 // need to revalidate the state after acquiring the lock again
3356 if (mData->mSession.mState != SessionState_Locked)
3357 {
3358 pSessionControl->Uninitialize();
3359 return setError(VBOX_E_INVALID_SESSION_STATE,
3360 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3361 mUserData->s.strName.c_str());
3362 }
3363
3364 // add the caller's session to the list
3365 mData->mSession.mRemoteControls.push_back(pSessionControl);
3366 }
3367 else if ( mData->mSession.mState == SessionState_Locked
3368 || mData->mSession.mState == SessionState_Unlocking
3369 )
3370 {
3371 // sharing not permitted, or machine still unlocking:
3372 return setError(VBOX_E_INVALID_OBJECT_STATE,
3373 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3374 mUserData->s.strName.c_str());
3375 }
3376 else
3377 {
3378 // machine is not locked: then write-lock the machine (create the session machine)
3379
3380 // must not be busy
3381 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3382
3383 // get the caller's session PID
3384 RTPROCESS pid = NIL_RTPROCESS;
3385 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3386 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3387 Assert(pid != NIL_RTPROCESS);
3388
3389 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3390
3391 if (fLaunchingVMProcess)
3392 {
3393 if (mData->mSession.mPID == NIL_RTPROCESS)
3394 {
3395 // two or more clients racing for a lock, the one which set the
3396 // session state to Spawning will win, the others will get an
3397 // error as we can't decide here if waiting a little would help
3398 // (only for shared locks this would avoid an error)
3399 return setError(VBOX_E_INVALID_OBJECT_STATE,
3400 tr("The machine '%s' already has a lock request pending"),
3401 mUserData->s.strName.c_str());
3402 }
3403
3404 // this machine is awaiting for a spawning session to be opened:
3405 // then the calling process must be the one that got started by
3406 // LaunchVMProcess()
3407
3408 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3409 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3410
3411#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3412 /* Hardened windows builds spawns three processes when a VM is
3413 launched, the 3rd one is the one that will end up here. */
3414 RTPROCESS pidParent;
3415 int vrc = RTProcQueryParent(pid, &pidParent);
3416 if (RT_SUCCESS(vrc))
3417 vrc = RTProcQueryParent(pidParent, &pidParent);
3418 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3419 || vrc == VERR_ACCESS_DENIED)
3420 {
3421 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3422 mData->mSession.mPID = pid;
3423 }
3424#endif
3425
3426 if (mData->mSession.mPID != pid)
3427 return setError(E_ACCESSDENIED,
3428 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3429 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3430 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3431 }
3432
3433 // create the mutable SessionMachine from the current machine
3434 ComObjPtr<SessionMachine> sessionMachine;
3435 sessionMachine.createObject();
3436 hrc = sessionMachine->init(this);
3437 AssertComRC(hrc);
3438
3439 /* NOTE: doing return from this function after this point but
3440 * before the end is forbidden since it may call SessionMachine::uninit()
3441 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3442 * lock while still holding the Machine lock in alock so that a deadlock
3443 * is possible due to the wrong lock order. */
3444
3445 if (SUCCEEDED(hrc))
3446 {
3447 /*
3448 * Set the session state to Spawning to protect against subsequent
3449 * attempts to open a session and to unregister the machine after
3450 * we release the lock.
3451 */
3452 SessionState_T origState = mData->mSession.mState;
3453 mData->mSession.mState = SessionState_Spawning;
3454
3455#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3456 /* Get the client token ID to be passed to the client process */
3457 Utf8Str strTokenId;
3458 sessionMachine->i_getTokenId(strTokenId);
3459 Assert(!strTokenId.isEmpty());
3460#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3461 /* Get the client token to be passed to the client process */
3462 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3463 /* The token is now "owned" by pToken, fix refcount */
3464 if (!pToken.isNull())
3465 pToken->Release();
3466#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3467
3468 /*
3469 * Release the lock before calling the client process -- it will call
3470 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3471 * because the state is Spawning, so that LaunchVMProcess() and
3472 * LockMachine() calls will fail. This method, called before we
3473 * acquire the lock again, will fail because of the wrong PID.
3474 *
3475 * Note that mData->mSession.mRemoteControls accessed outside
3476 * the lock may not be modified when state is Spawning, so it's safe.
3477 */
3478 alock.release();
3479
3480 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3481#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3482 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3483#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3484 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3485 /* Now the token is owned by the client process. */
3486 pToken.setNull();
3487#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3488 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
3489
3490 /* The failure may occur w/o any error info (from RPC), so provide one */
3491 if (FAILED(hrc))
3492 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
3493
3494 // get session name, either to remember or to compare against
3495 // the already known session name.
3496 {
3497 Bstr bstrSessionName;
3498 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3499 if (SUCCEEDED(hrc2))
3500 strSessionName = bstrSessionName;
3501 }
3502
3503 if ( SUCCEEDED(hrc)
3504 && fLaunchingVMProcess
3505 )
3506 {
3507 /* complete the remote session initialization */
3508
3509 /* get the console from the direct session */
3510 ComPtr<IConsole> console;
3511 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3512 ComAssertComRC(hrc);
3513
3514 if (SUCCEEDED(hrc) && !console)
3515 {
3516 ComAssert(!!console);
3517 hrc = E_FAIL;
3518 }
3519
3520 /* assign machine & console to the remote session */
3521 if (SUCCEEDED(hrc))
3522 {
3523 /*
3524 * after LaunchVMProcess(), the first and the only
3525 * entry in remoteControls is that remote session
3526 */
3527 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3528 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3529 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
3530
3531 /* The failure may occur w/o any error info (from RPC), so provide one */
3532 if (FAILED(hrc))
3533 setError(VBOX_E_VM_ERROR,
3534 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
3535 }
3536
3537 if (FAILED(hrc))
3538 pSessionControl->Uninitialize();
3539 }
3540
3541 /* acquire the lock again */
3542 alock.acquire();
3543
3544 /* Restore the session state */
3545 mData->mSession.mState = origState;
3546 }
3547
3548 // finalize spawning anyway (this is why we don't return on errors above)
3549 if (fLaunchingVMProcess)
3550 {
3551 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
3552 /* Note that the progress object is finalized later */
3553 /** @todo Consider checking mData->mSession.mProgress for cancellation
3554 * around here. */
3555
3556 /* We don't reset mSession.mPID here because it is necessary for
3557 * SessionMachine::uninit() to reap the child process later. */
3558
3559 if (FAILED(hrc))
3560 {
3561 /* Close the remote session, remove the remote control from the list
3562 * and reset session state to Closed (@note keep the code in sync
3563 * with the relevant part in checkForSpawnFailure()). */
3564
3565 Assert(mData->mSession.mRemoteControls.size() == 1);
3566 if (mData->mSession.mRemoteControls.size() == 1)
3567 {
3568 ErrorInfoKeeper eik;
3569 mData->mSession.mRemoteControls.front()->Uninitialize();
3570 }
3571
3572 mData->mSession.mRemoteControls.clear();
3573 mData->mSession.mState = SessionState_Unlocked;
3574 }
3575 }
3576 else
3577 {
3578 /* memorize PID of the directly opened session */
3579 if (SUCCEEDED(hrc))
3580 mData->mSession.mPID = pid;
3581 }
3582
3583 if (SUCCEEDED(hrc))
3584 {
3585 mData->mSession.mLockType = aLockType;
3586 /* memorize the direct session control and cache IUnknown for it */
3587 mData->mSession.mDirectControl = pSessionControl;
3588 mData->mSession.mState = SessionState_Locked;
3589 if (!fLaunchingVMProcess)
3590 mData->mSession.mName = strSessionName;
3591 /* associate the SessionMachine with this Machine */
3592 mData->mSession.mMachine = sessionMachine;
3593
3594 /* request an IUnknown pointer early from the remote party for later
3595 * identity checks (it will be internally cached within mDirectControl
3596 * at least on XPCOM) */
3597 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3598 NOREF(unk);
3599
3600#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3601 if (aLockType == LockType_VM)
3602 {
3603 /* get the console from the direct session */
3604 ComPtr<IConsole> console;
3605 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3606 ComAssertComRC(hrc);
3607 /* send passswords to console */
3608 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3609 it != mData->mpKeyStore->end();
3610 ++it)
3611 {
3612 SecretKey *pKey = it->second;
3613 pKey->retain();
3614 console->AddEncryptionPassword(Bstr(it->first).raw(),
3615 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3616 TRUE);
3617 pKey->release();
3618 }
3619
3620 }
3621#endif
3622 }
3623
3624 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3625 * would break the lock order */
3626 alock.release();
3627
3628 /* uninitialize the created session machine on failure */
3629 if (FAILED(hrc))
3630 sessionMachine->uninit();
3631 }
3632
3633 if (SUCCEEDED(hrc))
3634 {
3635 /*
3636 * tell the client watcher thread to update the set of
3637 * machines that have open sessions
3638 */
3639 mParent->i_updateClientWatcher();
3640
3641 if (oldState != SessionState_Locked)
3642 /* fire an event */
3643 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3644 }
3645
3646 return hrc;
3647}
3648
3649/**
3650 * @note Locks objects!
3651 */
3652HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3653 const com::Utf8Str &aName,
3654 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3655 ComPtr<IProgress> &aProgress)
3656{
3657 Utf8Str strFrontend(aName);
3658 /* "emergencystop" doesn't need the session, so skip the checks/interface
3659 * retrieval. This code doesn't quite fit in here, but introducing a
3660 * special API method would be even more effort, and would require explicit
3661 * support by every API client. It's better to hide the feature a bit. */
3662 if (strFrontend != "emergencystop")
3663 CheckComArgNotNull(aSession);
3664
3665 HRESULT hrc = S_OK;
3666 if (strFrontend.isEmpty())
3667 {
3668 Bstr bstrFrontend;
3669 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3670 if (FAILED(hrc))
3671 return hrc;
3672 strFrontend = bstrFrontend;
3673 if (strFrontend.isEmpty())
3674 {
3675 ComPtr<ISystemProperties> systemProperties;
3676 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3677 if (FAILED(hrc))
3678 return hrc;
3679 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3680 if (FAILED(hrc))
3681 return hrc;
3682 strFrontend = bstrFrontend;
3683 }
3684 /* paranoia - emergencystop is not a valid default */
3685 if (strFrontend == "emergencystop")
3686 strFrontend = Utf8Str::Empty;
3687 }
3688 /* default frontend: Qt GUI */
3689 if (strFrontend.isEmpty())
3690 strFrontend = "GUI/Qt";
3691
3692 if (strFrontend != "emergencystop")
3693 {
3694 /* check the session state */
3695 SessionState_T state;
3696 hrc = aSession->COMGETTER(State)(&state);
3697 if (FAILED(hrc))
3698 return hrc;
3699
3700 if (state != SessionState_Unlocked)
3701 return setError(VBOX_E_INVALID_OBJECT_STATE,
3702 tr("The given session is busy"));
3703
3704 /* get the IInternalSessionControl interface */
3705 ComPtr<IInternalSessionControl> control(aSession);
3706 ComAssertMsgRet(!control.isNull(),
3707 ("No IInternalSessionControl interface"),
3708 E_INVALIDARG);
3709
3710 /* get the teleporter enable state for the progress object init. */
3711 BOOL fTeleporterEnabled;
3712 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3713 if (FAILED(hrc))
3714 return hrc;
3715
3716 /* create a progress object */
3717 ComObjPtr<ProgressProxy> progress;
3718 progress.createObject();
3719 hrc = progress->init(mParent,
3720 static_cast<IMachine*>(this),
3721 Bstr(tr("Starting VM")).raw(),
3722 TRUE /* aCancelable */,
3723 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3724 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3725 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3726 2 /* uFirstOperationWeight */,
3727 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3728 if (SUCCEEDED(hrc))
3729 {
3730 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3731 if (SUCCEEDED(hrc))
3732 {
3733 aProgress = progress;
3734
3735 /* signal the client watcher thread */
3736 mParent->i_updateClientWatcher();
3737
3738 /* fire an event */
3739 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3740 }
3741 }
3742 }
3743 else
3744 {
3745 /* no progress object - either instant success or failure */
3746 aProgress = NULL;
3747
3748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3749
3750 if (mData->mSession.mState != SessionState_Locked)
3751 return setError(VBOX_E_INVALID_OBJECT_STATE,
3752 tr("The machine '%s' is not locked by a session"),
3753 mUserData->s.strName.c_str());
3754
3755 /* must have a VM process associated - do not kill normal API clients
3756 * with an open session */
3757 if (!Global::IsOnline(mData->mMachineState))
3758 return setError(VBOX_E_INVALID_OBJECT_STATE,
3759 tr("The machine '%s' does not have a VM process"),
3760 mUserData->s.strName.c_str());
3761
3762 /* forcibly terminate the VM process */
3763 if (mData->mSession.mPID != NIL_RTPROCESS)
3764 RTProcTerminate(mData->mSession.mPID);
3765
3766 /* signal the client watcher thread, as most likely the client has
3767 * been terminated */
3768 mParent->i_updateClientWatcher();
3769 }
3770
3771 return hrc;
3772}
3773
3774HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3775{
3776 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3777 return setError(E_INVALIDARG,
3778 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3779 aPosition, SchemaDefs::MaxBootPosition);
3780
3781 if (aDevice == DeviceType_USB)
3782 return setError(E_NOTIMPL,
3783 tr("Booting from USB device is currently not supported"));
3784
3785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3786
3787 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3788 if (FAILED(hrc)) return hrc;
3789
3790 i_setModified(IsModified_MachineData);
3791 mHWData.backup();
3792 mHWData->mBootOrder[aPosition - 1] = aDevice;
3793
3794 return S_OK;
3795}
3796
3797HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3798{
3799 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3800 return setError(E_INVALIDARG,
3801 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3802 aPosition, SchemaDefs::MaxBootPosition);
3803
3804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3805
3806 *aDevice = mHWData->mBootOrder[aPosition - 1];
3807
3808 return S_OK;
3809}
3810
3811HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3812 LONG aControllerPort,
3813 LONG aDevice,
3814 DeviceType_T aType,
3815 const ComPtr<IMedium> &aMedium)
3816{
3817 IMedium *aM = aMedium;
3818 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3819 aName.c_str(), aControllerPort, aDevice, aType, aM));
3820
3821 // request the host lock first, since might be calling Host methods for getting host drives;
3822 // next, protect the media tree all the while we're in here, as well as our member variables
3823 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3824 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3825
3826 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3827 if (FAILED(hrc)) return hrc;
3828
3829 /// @todo NEWMEDIA implicit machine registration
3830 if (!mData->mRegistered)
3831 return setError(VBOX_E_INVALID_OBJECT_STATE,
3832 tr("Cannot attach storage devices to an unregistered machine"));
3833
3834 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3835
3836 /* Check for an existing controller. */
3837 ComObjPtr<StorageController> ctl;
3838 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3839 if (FAILED(hrc)) return hrc;
3840
3841 StorageControllerType_T ctrlType;
3842 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3843 if (FAILED(hrc))
3844 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3845
3846 bool fSilent = false;
3847
3848 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3849 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3850 if ( mData->mMachineState == MachineState_Paused
3851 && strReconfig == "1")
3852 fSilent = true;
3853
3854 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3855 bool fHotplug = false;
3856 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3857 fHotplug = true;
3858
3859 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3860 return setError(VBOX_E_INVALID_VM_STATE,
3861 tr("Controller '%s' does not support hot-plugging"),
3862 aName.c_str());
3863
3864 // check that the port and device are not out of range
3865 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3866 if (FAILED(hrc)) return hrc;
3867
3868 /* check if the device slot is already busy */
3869 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3870 aName,
3871 aControllerPort,
3872 aDevice);
3873 if (pAttachTemp)
3874 {
3875 Medium *pMedium = pAttachTemp->i_getMedium();
3876 if (pMedium)
3877 {
3878 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3879 return setError(VBOX_E_OBJECT_IN_USE,
3880 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3881 pMedium->i_getLocationFull().c_str(),
3882 aControllerPort,
3883 aDevice,
3884 aName.c_str());
3885 }
3886 else
3887 return setError(VBOX_E_OBJECT_IN_USE,
3888 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3889 aControllerPort, aDevice, aName.c_str());
3890 }
3891
3892 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3893 if (aMedium && medium.isNull())
3894 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3895
3896 AutoCaller mediumCaller(medium);
3897 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3898
3899 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3900
3901 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3902 if ( pAttachTemp
3903 && !medium.isNull()
3904 && ( medium->i_getType() != MediumType_Readonly
3905 || medium->i_getDeviceType() != DeviceType_DVD)
3906 )
3907 return setError(VBOX_E_OBJECT_IN_USE,
3908 tr("Medium '%s' is already attached to this virtual machine"),
3909 medium->i_getLocationFull().c_str());
3910
3911 if (!medium.isNull())
3912 {
3913 MediumType_T mtype = medium->i_getType();
3914 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3915 // For DVDs it's not written to the config file, so needs no global config
3916 // version bump. For floppies it's a new attribute "type", which is ignored
3917 // by older VirtualBox version, so needs no global config version bump either.
3918 // For hard disks this type is not accepted.
3919 if (mtype == MediumType_MultiAttach)
3920 {
3921 // This type is new with VirtualBox 4.0 and therefore requires settings
3922 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3923 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3924 // two reasons: The medium type is a property of the media registry tree, which
3925 // can reside in the global config file (for pre-4.0 media); we would therefore
3926 // possibly need to bump the global config version. We don't want to do that though
3927 // because that might make downgrading to pre-4.0 impossible.
3928 // As a result, we can only use these two new types if the medium is NOT in the
3929 // global registry:
3930 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3931 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3932 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3933 )
3934 return setError(VBOX_E_INVALID_OBJECT_STATE,
3935 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3936 "to machines that were created with VirtualBox 4.0 or later"),
3937 medium->i_getLocationFull().c_str());
3938 }
3939 }
3940
3941 bool fIndirect = false;
3942 if (!medium.isNull())
3943 fIndirect = medium->i_isReadOnly();
3944 bool associate = true;
3945
3946 do
3947 {
3948 if ( aType == DeviceType_HardDisk
3949 && mMediumAttachments.isBackedUp())
3950 {
3951 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3952
3953 /* check if the medium was attached to the VM before we started
3954 * changing attachments in which case the attachment just needs to
3955 * be restored */
3956 pAttachTemp = i_findAttachment(oldAtts, medium);
3957 if (pAttachTemp)
3958 {
3959 AssertReturn(!fIndirect, E_FAIL);
3960
3961 /* see if it's the same bus/channel/device */
3962 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3963 {
3964 /* the simplest case: restore the whole attachment
3965 * and return, nothing else to do */
3966 mMediumAttachments->push_back(pAttachTemp);
3967
3968 /* Reattach the medium to the VM. */
3969 if (fHotplug || fSilent)
3970 {
3971 mediumLock.release();
3972 treeLock.release();
3973 alock.release();
3974
3975 MediumLockList *pMediumLockList(new MediumLockList());
3976
3977 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3978 medium /* pToLockWrite */,
3979 false /* fMediumLockWriteAll */,
3980 NULL,
3981 *pMediumLockList);
3982 alock.acquire();
3983 if (FAILED(hrc))
3984 delete pMediumLockList;
3985 else
3986 {
3987 Assert(mData->mSession.mLockedMedia.IsLocked());
3988 mData->mSession.mLockedMedia.Unlock();
3989 alock.release();
3990 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3991 mData->mSession.mLockedMedia.Lock();
3992 alock.acquire();
3993 }
3994 alock.release();
3995
3996 if (SUCCEEDED(hrc))
3997 {
3998 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3999 /* Remove lock list in case of error. */
4000 if (FAILED(hrc))
4001 {
4002 mData->mSession.mLockedMedia.Unlock();
4003 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4004 mData->mSession.mLockedMedia.Lock();
4005 }
4006 }
4007 }
4008
4009 return S_OK;
4010 }
4011
4012 /* bus/channel/device differ; we need a new attachment object,
4013 * but don't try to associate it again */
4014 associate = false;
4015 break;
4016 }
4017 }
4018
4019 /* go further only if the attachment is to be indirect */
4020 if (!fIndirect)
4021 break;
4022
4023 /* perform the so called smart attachment logic for indirect
4024 * attachments. Note that smart attachment is only applicable to base
4025 * hard disks. */
4026
4027 if (medium->i_getParent().isNull())
4028 {
4029 /* first, investigate the backup copy of the current hard disk
4030 * attachments to make it possible to re-attach existing diffs to
4031 * another device slot w/o losing their contents */
4032 if (mMediumAttachments.isBackedUp())
4033 {
4034 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4035
4036 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4037 uint32_t foundLevel = 0;
4038
4039 for (MediumAttachmentList::const_iterator
4040 it = oldAtts.begin();
4041 it != oldAtts.end();
4042 ++it)
4043 {
4044 uint32_t level = 0;
4045 MediumAttachment *pAttach = *it;
4046 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4047 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4048 if (pMedium.isNull())
4049 continue;
4050
4051 if (pMedium->i_getBase(&level) == medium)
4052 {
4053 /* skip the hard disk if its currently attached (we
4054 * cannot attach the same hard disk twice) */
4055 if (i_findAttachment(*mMediumAttachments.data(),
4056 pMedium))
4057 continue;
4058
4059 /* matched device, channel and bus (i.e. attached to the
4060 * same place) will win and immediately stop the search;
4061 * otherwise the attachment that has the youngest
4062 * descendant of medium will be used
4063 */
4064 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4065 {
4066 /* the simplest case: restore the whole attachment
4067 * and return, nothing else to do */
4068 mMediumAttachments->push_back(*it);
4069
4070 /* Reattach the medium to the VM. */
4071 if (fHotplug || fSilent)
4072 {
4073 mediumLock.release();
4074 treeLock.release();
4075 alock.release();
4076
4077 MediumLockList *pMediumLockList(new MediumLockList());
4078
4079 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4080 medium /* pToLockWrite */,
4081 false /* fMediumLockWriteAll */,
4082 NULL,
4083 *pMediumLockList);
4084 alock.acquire();
4085 if (FAILED(hrc))
4086 delete pMediumLockList;
4087 else
4088 {
4089 Assert(mData->mSession.mLockedMedia.IsLocked());
4090 mData->mSession.mLockedMedia.Unlock();
4091 alock.release();
4092 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4093 mData->mSession.mLockedMedia.Lock();
4094 alock.acquire();
4095 }
4096 alock.release();
4097
4098 if (SUCCEEDED(hrc))
4099 {
4100 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4101 /* Remove lock list in case of error. */
4102 if (FAILED(hrc))
4103 {
4104 mData->mSession.mLockedMedia.Unlock();
4105 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4106 mData->mSession.mLockedMedia.Lock();
4107 }
4108 }
4109 }
4110
4111 return S_OK;
4112 }
4113 else if ( foundIt == oldAtts.end()
4114 || level > foundLevel /* prefer younger */
4115 )
4116 {
4117 foundIt = it;
4118 foundLevel = level;
4119 }
4120 }
4121 }
4122
4123 if (foundIt != oldAtts.end())
4124 {
4125 /* use the previously attached hard disk */
4126 medium = (*foundIt)->i_getMedium();
4127 mediumCaller.attach(medium);
4128 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4129 mediumLock.attach(medium);
4130 /* not implicit, doesn't require association with this VM */
4131 fIndirect = false;
4132 associate = false;
4133 /* go right to the MediumAttachment creation */
4134 break;
4135 }
4136 }
4137
4138 /* must give up the medium lock and medium tree lock as below we
4139 * go over snapshots, which needs a lock with higher lock order. */
4140 mediumLock.release();
4141 treeLock.release();
4142
4143 /* then, search through snapshots for the best diff in the given
4144 * hard disk's chain to base the new diff on */
4145
4146 ComObjPtr<Medium> base;
4147 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4148 while (snap)
4149 {
4150 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4151
4152 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4153
4154 MediumAttachment *pAttachFound = NULL;
4155 uint32_t foundLevel = 0;
4156
4157 for (MediumAttachmentList::const_iterator
4158 it = snapAtts.begin();
4159 it != snapAtts.end();
4160 ++it)
4161 {
4162 MediumAttachment *pAttach = *it;
4163 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4164 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4165 if (pMedium.isNull())
4166 continue;
4167
4168 uint32_t level = 0;
4169 if (pMedium->i_getBase(&level) == medium)
4170 {
4171 /* matched device, channel and bus (i.e. attached to the
4172 * same place) will win and immediately stop the search;
4173 * otherwise the attachment that has the youngest
4174 * descendant of medium will be used
4175 */
4176 if ( pAttach->i_getDevice() == aDevice
4177 && pAttach->i_getPort() == aControllerPort
4178 && pAttach->i_getControllerName() == aName
4179 )
4180 {
4181 pAttachFound = pAttach;
4182 break;
4183 }
4184 else if ( !pAttachFound
4185 || level > foundLevel /* prefer younger */
4186 )
4187 {
4188 pAttachFound = pAttach;
4189 foundLevel = level;
4190 }
4191 }
4192 }
4193
4194 if (pAttachFound)
4195 {
4196 base = pAttachFound->i_getMedium();
4197 break;
4198 }
4199
4200 snap = snap->i_getParent();
4201 }
4202
4203 /* re-lock medium tree and the medium, as we need it below */
4204 treeLock.acquire();
4205 mediumLock.acquire();
4206
4207 /* found a suitable diff, use it as a base */
4208 if (!base.isNull())
4209 {
4210 medium = base;
4211 mediumCaller.attach(medium);
4212 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4213 mediumLock.attach(medium);
4214 }
4215 }
4216
4217 Utf8Str strFullSnapshotFolder;
4218 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4219
4220 ComObjPtr<Medium> diff;
4221 diff.createObject();
4222 // store this diff in the same registry as the parent
4223 Guid uuidRegistryParent;
4224 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4225 {
4226 // parent image has no registry: this can happen if we're attaching a new immutable
4227 // image that has not yet been attached (medium then points to the base and we're
4228 // creating the diff image for the immutable, and the parent is not yet registered);
4229 // put the parent in the machine registry then
4230 mediumLock.release();
4231 treeLock.release();
4232 alock.release();
4233 i_addMediumToRegistry(medium);
4234 alock.acquire();
4235 treeLock.acquire();
4236 mediumLock.acquire();
4237 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4238 }
4239 hrc = diff->init(mParent,
4240 medium->i_getPreferredDiffFormat(),
4241 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4242 uuidRegistryParent,
4243 DeviceType_HardDisk);
4244 if (FAILED(hrc)) return hrc;
4245
4246 /* Apply the normal locking logic to the entire chain. */
4247 MediumLockList *pMediumLockList(new MediumLockList());
4248 mediumLock.release();
4249 treeLock.release();
4250 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4251 diff /* pToLockWrite */,
4252 false /* fMediumLockWriteAll */,
4253 medium,
4254 *pMediumLockList);
4255 treeLock.acquire();
4256 mediumLock.acquire();
4257 if (SUCCEEDED(hrc))
4258 {
4259 mediumLock.release();
4260 treeLock.release();
4261 hrc = pMediumLockList->Lock();
4262 treeLock.acquire();
4263 mediumLock.acquire();
4264 if (FAILED(hrc))
4265 setError(hrc,
4266 tr("Could not lock medium when creating diff '%s'"),
4267 diff->i_getLocationFull().c_str());
4268 else
4269 {
4270 /* will release the lock before the potentially lengthy
4271 * operation, so protect with the special state */
4272 MachineState_T oldState = mData->mMachineState;
4273 i_setMachineState(MachineState_SettingUp);
4274
4275 mediumLock.release();
4276 treeLock.release();
4277 alock.release();
4278
4279 hrc = medium->i_createDiffStorage(diff,
4280 medium->i_getPreferredDiffVariant(),
4281 pMediumLockList,
4282 NULL /* aProgress */,
4283 true /* aWait */,
4284 false /* aNotify */);
4285
4286 alock.acquire();
4287 treeLock.acquire();
4288 mediumLock.acquire();
4289
4290 i_setMachineState(oldState);
4291 }
4292 }
4293
4294 /* Unlock the media and free the associated memory. */
4295 delete pMediumLockList;
4296
4297 if (FAILED(hrc)) return hrc;
4298
4299 /* use the created diff for the actual attachment */
4300 medium = diff;
4301 mediumCaller.attach(medium);
4302 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4303 mediumLock.attach(medium);
4304 }
4305 while (0);
4306
4307 ComObjPtr<MediumAttachment> attachment;
4308 attachment.createObject();
4309 hrc = attachment->init(this,
4310 medium,
4311 aName,
4312 aControllerPort,
4313 aDevice,
4314 aType,
4315 fIndirect,
4316 false /* fPassthrough */,
4317 false /* fTempEject */,
4318 false /* fNonRotational */,
4319 false /* fDiscard */,
4320 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
4321 Utf8Str::Empty);
4322 if (FAILED(hrc)) return hrc;
4323
4324 if (associate && !medium.isNull())
4325 {
4326 // as the last step, associate the medium to the VM
4327 hrc = medium->i_addBackReference(mData->mUuid);
4328 // here we can fail because of Deleting, or being in process of creating a Diff
4329 if (FAILED(hrc)) return hrc;
4330
4331 mediumLock.release();
4332 treeLock.release();
4333 alock.release();
4334 i_addMediumToRegistry(medium);
4335 alock.acquire();
4336 treeLock.acquire();
4337 mediumLock.acquire();
4338 }
4339
4340 /* success: finally remember the attachment */
4341 i_setModified(IsModified_Storage);
4342 mMediumAttachments.backup();
4343 mMediumAttachments->push_back(attachment);
4344
4345 mediumLock.release();
4346 treeLock.release();
4347 alock.release();
4348
4349 if (fHotplug || fSilent)
4350 {
4351 if (!medium.isNull())
4352 {
4353 MediumLockList *pMediumLockList(new MediumLockList());
4354
4355 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4356 medium /* pToLockWrite */,
4357 false /* fMediumLockWriteAll */,
4358 NULL,
4359 *pMediumLockList);
4360 alock.acquire();
4361 if (FAILED(hrc))
4362 delete pMediumLockList;
4363 else
4364 {
4365 Assert(mData->mSession.mLockedMedia.IsLocked());
4366 mData->mSession.mLockedMedia.Unlock();
4367 alock.release();
4368 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4369 mData->mSession.mLockedMedia.Lock();
4370 alock.acquire();
4371 }
4372 alock.release();
4373 }
4374
4375 if (SUCCEEDED(hrc))
4376 {
4377 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4378 /* Remove lock list in case of error. */
4379 if (FAILED(hrc))
4380 {
4381 mData->mSession.mLockedMedia.Unlock();
4382 mData->mSession.mLockedMedia.Remove(attachment);
4383 mData->mSession.mLockedMedia.Lock();
4384 }
4385 }
4386 }
4387
4388 /* Save modified registries, but skip this machine as it's the caller's
4389 * job to save its settings like all other settings changes. */
4390 mParent->i_unmarkRegistryModified(i_getId());
4391 mParent->i_saveModifiedRegistries();
4392
4393 if (SUCCEEDED(hrc))
4394 {
4395 if (fIndirect && medium != aM)
4396 mParent->i_onMediumConfigChanged(medium);
4397 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4398 }
4399
4400 return hrc;
4401}
4402
4403HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4404 LONG aDevice)
4405{
4406 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
4407
4408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4409
4410 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4411 if (FAILED(hrc)) return hrc;
4412
4413 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4414
4415 /* Check for an existing controller. */
4416 ComObjPtr<StorageController> ctl;
4417 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4418 if (FAILED(hrc)) return hrc;
4419
4420 StorageControllerType_T ctrlType;
4421 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4422 if (FAILED(hrc))
4423 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
4424
4425 bool fSilent = false;
4426 Utf8Str strReconfig;
4427
4428 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4429 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4430 if ( mData->mMachineState == MachineState_Paused
4431 && strReconfig == "1")
4432 fSilent = true;
4433
4434 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4435 bool fHotplug = false;
4436 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4437 fHotplug = true;
4438
4439 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4440 return setError(VBOX_E_INVALID_VM_STATE,
4441 tr("Controller '%s' does not support hot-plugging"),
4442 aName.c_str());
4443
4444 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4445 aName,
4446 aControllerPort,
4447 aDevice);
4448 if (!pAttach)
4449 return setError(VBOX_E_OBJECT_NOT_FOUND,
4450 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4451 aDevice, aControllerPort, aName.c_str());
4452
4453 if (fHotplug && !pAttach->i_getHotPluggable())
4454 return setError(VBOX_E_NOT_SUPPORTED,
4455 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4456 aDevice, aControllerPort, aName.c_str());
4457
4458 /*
4459 * The VM has to detach the device before we delete any implicit diffs.
4460 * If this fails we can roll back without loosing data.
4461 */
4462 if (fHotplug || fSilent)
4463 {
4464 alock.release();
4465 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4466 alock.acquire();
4467 }
4468 if (FAILED(hrc)) return hrc;
4469
4470 /* If we are here everything went well and we can delete the implicit now. */
4471 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4472
4473 alock.release();
4474
4475 /* Save modified registries, but skip this machine as it's the caller's
4476 * job to save its settings like all other settings changes. */
4477 mParent->i_unmarkRegistryModified(i_getId());
4478 mParent->i_saveModifiedRegistries();
4479
4480 if (SUCCEEDED(hrc))
4481 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4482
4483 return hrc;
4484}
4485
4486HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aPassthrough)
4488{
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4490 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4495 if (FAILED(hrc)) return hrc;
4496
4497 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4498
4499 /* Check for an existing controller. */
4500 ComObjPtr<StorageController> ctl;
4501 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4502 if (FAILED(hrc)) return hrc;
4503
4504 StorageControllerType_T ctrlType;
4505 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4506 if (FAILED(hrc))
4507 return setError(E_FAIL,
4508 tr("Could not get type of controller '%s'"),
4509 aName.c_str());
4510
4511 bool fSilent = false;
4512 Utf8Str strReconfig;
4513
4514 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4515 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4516 if ( mData->mMachineState == MachineState_Paused
4517 && strReconfig == "1")
4518 fSilent = true;
4519
4520 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4521 bool fHotplug = false;
4522 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4523 fHotplug = true;
4524
4525 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4526 return setError(VBOX_E_INVALID_VM_STATE,
4527 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4528 aName.c_str());
4529
4530 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4531 aName,
4532 aControllerPort,
4533 aDevice);
4534 if (!pAttach)
4535 return setError(VBOX_E_OBJECT_NOT_FOUND,
4536 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4537 aDevice, aControllerPort, aName.c_str());
4538
4539
4540 i_setModified(IsModified_Storage);
4541 mMediumAttachments.backup();
4542
4543 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4544
4545 if (pAttach->i_getType() != DeviceType_DVD)
4546 return setError(E_INVALIDARG,
4547 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4551
4552 pAttach->i_updatePassthrough(!!aPassthrough);
4553
4554 attLock.release();
4555 alock.release();
4556 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4557 if (SUCCEEDED(hrc) && fValueChanged)
4558 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4559
4560 return hrc;
4561}
4562
4563HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4564 LONG aDevice, BOOL aTemporaryEject)
4565{
4566
4567 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4568 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4569
4570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4571
4572 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4573 if (FAILED(hrc)) return hrc;
4574
4575 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4576 aName,
4577 aControllerPort,
4578 aDevice);
4579 if (!pAttach)
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584
4585 i_setModified(IsModified_Storage);
4586 mMediumAttachments.backup();
4587
4588 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4589
4590 if (pAttach->i_getType() != DeviceType_DVD)
4591 return setError(E_INVALIDARG,
4592 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4593 aDevice, aControllerPort, aName.c_str());
4594 pAttach->i_updateTempEject(!!aTemporaryEject);
4595
4596 return S_OK;
4597}
4598
4599HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4600 LONG aDevice, BOOL aNonRotational)
4601{
4602
4603 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4604 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4605
4606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4609 if (FAILED(hrc)) return hrc;
4610
4611 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4612
4613 if (Global::IsOnlineOrTransient(mData->mMachineState))
4614 return setError(VBOX_E_INVALID_VM_STATE,
4615 tr("Invalid machine state: %s"),
4616 Global::stringifyMachineState(mData->mMachineState));
4617
4618 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4619 aName,
4620 aControllerPort,
4621 aDevice);
4622 if (!pAttach)
4623 return setError(VBOX_E_OBJECT_NOT_FOUND,
4624 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4625 aDevice, aControllerPort, aName.c_str());
4626
4627
4628 i_setModified(IsModified_Storage);
4629 mMediumAttachments.backup();
4630
4631 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4632
4633 if (pAttach->i_getType() != DeviceType_HardDisk)
4634 return setError(E_INVALIDARG,
4635 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4636 aDevice, aControllerPort, aName.c_str());
4637 pAttach->i_updateNonRotational(!!aNonRotational);
4638
4639 return S_OK;
4640}
4641
4642HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4643 LONG aDevice, BOOL aDiscard)
4644{
4645
4646 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4647 aName.c_str(), aControllerPort, aDevice, aDiscard));
4648
4649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4650
4651 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4652 if (FAILED(hrc)) return hrc;
4653
4654 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4655
4656 if (Global::IsOnlineOrTransient(mData->mMachineState))
4657 return setError(VBOX_E_INVALID_VM_STATE,
4658 tr("Invalid machine state: %s"),
4659 Global::stringifyMachineState(mData->mMachineState));
4660
4661 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4662 aName,
4663 aControllerPort,
4664 aDevice);
4665 if (!pAttach)
4666 return setError(VBOX_E_OBJECT_NOT_FOUND,
4667 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4668 aDevice, aControllerPort, aName.c_str());
4669
4670
4671 i_setModified(IsModified_Storage);
4672 mMediumAttachments.backup();
4673
4674 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4675
4676 if (pAttach->i_getType() != DeviceType_HardDisk)
4677 return setError(E_INVALIDARG,
4678 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4679 aDevice, aControllerPort, aName.c_str());
4680 pAttach->i_updateDiscard(!!aDiscard);
4681
4682 return S_OK;
4683}
4684
4685HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4686 LONG aDevice, BOOL aHotPluggable)
4687{
4688 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4689 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4690
4691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4692
4693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4694 if (FAILED(hrc)) return hrc;
4695
4696 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4697
4698 if (Global::IsOnlineOrTransient(mData->mMachineState))
4699 return setError(VBOX_E_INVALID_VM_STATE,
4700 tr("Invalid machine state: %s"),
4701 Global::stringifyMachineState(mData->mMachineState));
4702
4703 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4704 aName,
4705 aControllerPort,
4706 aDevice);
4707 if (!pAttach)
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4710 aDevice, aControllerPort, aName.c_str());
4711
4712 /* Check for an existing controller. */
4713 ComObjPtr<StorageController> ctl;
4714 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4715 if (FAILED(hrc)) return hrc;
4716
4717 StorageControllerType_T ctrlType;
4718 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4719 if (FAILED(hrc))
4720 return setError(E_FAIL,
4721 tr("Could not get type of controller '%s'"),
4722 aName.c_str());
4723
4724 if (!i_isControllerHotplugCapable(ctrlType))
4725 return setError(VBOX_E_NOT_SUPPORTED,
4726 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4727 aName.c_str());
4728
4729 /* silently ignore attempts to modify the hot-plug status of USB devices */
4730 if (ctrlType == StorageControllerType_USB)
4731 return S_OK;
4732
4733 i_setModified(IsModified_Storage);
4734 mMediumAttachments.backup();
4735
4736 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4737
4738 if (pAttach->i_getType() == DeviceType_Floppy)
4739 return setError(E_INVALIDARG,
4740 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4741 aDevice, aControllerPort, aName.c_str());
4742 pAttach->i_updateHotPluggable(!!aHotPluggable);
4743
4744 return S_OK;
4745}
4746
4747HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4748 LONG aDevice)
4749{
4750 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4751 aName.c_str(), aControllerPort, aDevice));
4752
4753 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4754}
4755
4756HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4757 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4758{
4759 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4760 aName.c_str(), aControllerPort, aDevice));
4761
4762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4763
4764 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4765 if (FAILED(hrc)) return hrc;
4766
4767 if (Global::IsOnlineOrTransient(mData->mMachineState))
4768 return setError(VBOX_E_INVALID_VM_STATE,
4769 tr("Invalid machine state: %s"),
4770 Global::stringifyMachineState(mData->mMachineState));
4771
4772 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4773 aName,
4774 aControllerPort,
4775 aDevice);
4776 if (!pAttach)
4777 return setError(VBOX_E_OBJECT_NOT_FOUND,
4778 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4779 aDevice, aControllerPort, aName.c_str());
4780
4781
4782 i_setModified(IsModified_Storage);
4783 mMediumAttachments.backup();
4784
4785 IBandwidthGroup *iB = aBandwidthGroup;
4786 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4787 if (aBandwidthGroup && group.isNull())
4788 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4789
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791
4792 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4793 if (strBandwidthGroupOld.isNotEmpty())
4794 {
4795 /* Get the bandwidth group object and release it - this must not fail. */
4796 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4797 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4798 Assert(SUCCEEDED(hrc));
4799
4800 pBandwidthGroupOld->i_release();
4801 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4802 }
4803
4804 if (!group.isNull())
4805 {
4806 group->i_reference();
4807 pAttach->i_updateBandwidthGroup(group->i_getName());
4808 }
4809
4810 return S_OK;
4811}
4812
4813HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4814 LONG aControllerPort,
4815 LONG aDevice,
4816 DeviceType_T aType)
4817{
4818 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4819 aName.c_str(), aControllerPort, aDevice, aType));
4820
4821 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4822}
4823
4824
4825HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4826 LONG aControllerPort,
4827 LONG aDevice,
4828 BOOL aForce)
4829{
4830 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4831 aName.c_str(), aControllerPort, aForce));
4832
4833 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4834}
4835
4836HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4837 LONG aControllerPort,
4838 LONG aDevice,
4839 const ComPtr<IMedium> &aMedium,
4840 BOOL aForce)
4841{
4842 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4843 aName.c_str(), aControllerPort, aDevice, aForce));
4844
4845 // request the host lock first, since might be calling Host methods for getting host drives;
4846 // next, protect the media tree all the while we're in here, as well as our member variables
4847 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4848 this->lockHandle(),
4849 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4850
4851 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4852 if (FAILED(hrc)) return hrc;
4853
4854 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4855 aName,
4856 aControllerPort,
4857 aDevice);
4858 if (pAttach.isNull())
4859 return setError(VBOX_E_OBJECT_NOT_FOUND,
4860 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4861 aDevice, aControllerPort, aName.c_str());
4862
4863 /* Remember previously mounted medium. The medium before taking the
4864 * backup is not necessarily the same thing. */
4865 ComObjPtr<Medium> oldmedium;
4866 oldmedium = pAttach->i_getMedium();
4867
4868 IMedium *iM = aMedium;
4869 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4870 if (aMedium && pMedium.isNull())
4871 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4872
4873 /* Check if potential medium is already mounted */
4874 if (pMedium == oldmedium)
4875 return S_OK;
4876
4877 AutoCaller mediumCaller(pMedium);
4878 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4879
4880 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4881 if (pMedium)
4882 {
4883 DeviceType_T mediumType = pAttach->i_getType();
4884 switch (mediumType)
4885 {
4886 case DeviceType_DVD:
4887 case DeviceType_Floppy:
4888 break;
4889
4890 default:
4891 return setError(VBOX_E_INVALID_OBJECT_STATE,
4892 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4893 aControllerPort,
4894 aDevice,
4895 aName.c_str());
4896 }
4897 }
4898
4899 i_setModified(IsModified_Storage);
4900 mMediumAttachments.backup();
4901
4902 {
4903 // The backup operation makes the pAttach reference point to the
4904 // old settings. Re-get the correct reference.
4905 pAttach = i_findAttachment(*mMediumAttachments.data(),
4906 aName,
4907 aControllerPort,
4908 aDevice);
4909 if (!oldmedium.isNull())
4910 oldmedium->i_removeBackReference(mData->mUuid);
4911 if (!pMedium.isNull())
4912 {
4913 pMedium->i_addBackReference(mData->mUuid);
4914
4915 mediumLock.release();
4916 multiLock.release();
4917 i_addMediumToRegistry(pMedium);
4918 multiLock.acquire();
4919 mediumLock.acquire();
4920 }
4921
4922 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4923 pAttach->i_updateMedium(pMedium);
4924 }
4925
4926 i_setModified(IsModified_Storage);
4927
4928 mediumLock.release();
4929 multiLock.release();
4930 hrc = i_onMediumChange(pAttach, aForce);
4931 multiLock.acquire();
4932 mediumLock.acquire();
4933
4934 /* On error roll back this change only. */
4935 if (FAILED(hrc))
4936 {
4937 if (!pMedium.isNull())
4938 pMedium->i_removeBackReference(mData->mUuid);
4939 pAttach = i_findAttachment(*mMediumAttachments.data(),
4940 aName,
4941 aControllerPort,
4942 aDevice);
4943 /* If the attachment is gone in the meantime, bail out. */
4944 if (pAttach.isNull())
4945 return hrc;
4946 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4947 if (!oldmedium.isNull())
4948 oldmedium->i_addBackReference(mData->mUuid);
4949 pAttach->i_updateMedium(oldmedium);
4950 }
4951
4952 mediumLock.release();
4953 multiLock.release();
4954
4955 /* Save modified registries, but skip this machine as it's the caller's
4956 * job to save its settings like all other settings changes. */
4957 mParent->i_unmarkRegistryModified(i_getId());
4958 mParent->i_saveModifiedRegistries();
4959
4960 return hrc;
4961}
4962HRESULT Machine::getMedium(const com::Utf8Str &aName,
4963 LONG aControllerPort,
4964 LONG aDevice,
4965 ComPtr<IMedium> &aMedium)
4966{
4967 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4968 aName.c_str(), aControllerPort, aDevice));
4969
4970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4971
4972 aMedium = NULL;
4973
4974 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4975 aName,
4976 aControllerPort,
4977 aDevice);
4978 if (pAttach.isNull())
4979 return setError(VBOX_E_OBJECT_NOT_FOUND,
4980 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4981 aDevice, aControllerPort, aName.c_str());
4982
4983 aMedium = pAttach->i_getMedium();
4984
4985 return S_OK;
4986}
4987
4988HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4989{
4990 if (aSlot < RT_ELEMENTS(mSerialPorts))
4991 {
4992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4993 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4994 return S_OK;
4995 }
4996 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4997}
4998
4999HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5000{
5001 if (aSlot < RT_ELEMENTS(mParallelPorts))
5002 {
5003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5004 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5005 return S_OK;
5006 }
5007 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5008}
5009
5010
5011HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5012{
5013 /* Do not assert if slot is out of range, just return the advertised
5014 status. testdriver/vbox.py triggers this in logVmInfo. */
5015 if (aSlot >= mNetworkAdapters.size())
5016 return setError(E_INVALIDARG,
5017 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5018 aSlot, mNetworkAdapters.size());
5019
5020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5021
5022 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5023
5024 return S_OK;
5025}
5026
5027HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5028{
5029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5032 size_t i = 0;
5033 for (settings::StringsMap::const_iterator
5034 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5035 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5036 ++it, ++i)
5037 aKeys[i] = it->first;
5038
5039 return S_OK;
5040}
5041
5042 /**
5043 * @note Locks this object for reading.
5044 */
5045HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5046 com::Utf8Str &aValue)
5047{
5048 /* start with nothing found */
5049 aValue = "";
5050
5051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5054 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5055 // found:
5056 aValue = it->second; // source is a Utf8Str
5057
5058 /* return the result to caller (may be empty) */
5059 return S_OK;
5060}
5061
5062 /**
5063 * @note Locks mParent for writing + this object for writing.
5064 */
5065HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5066{
5067 /* Because control characters in aKey have caused problems in the settings
5068 * they are rejected unless the key should be deleted. */
5069 if (!aValue.isEmpty())
5070 {
5071 for (size_t i = 0; i < aKey.length(); ++i)
5072 {
5073 char ch = aKey[i];
5074 if (RTLocCIsCntrl(ch))
5075 return E_INVALIDARG;
5076 }
5077 }
5078
5079 Utf8Str strOldValue; // empty
5080
5081 // locking note: we only hold the read lock briefly to look up the old value,
5082 // then release it and call the onExtraCanChange callbacks. There is a small
5083 // chance of a race insofar as the callback might be called twice if two callers
5084 // change the same key at the same time, but that's a much better solution
5085 // than the deadlock we had here before. The actual changing of the extradata
5086 // is then performed under the write lock and race-free.
5087
5088 // look up the old value first; if nothing has changed then we need not do anything
5089 {
5090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5091
5092 // For snapshots don't even think about allowing changes, extradata
5093 // is global for a machine, so there is nothing snapshot specific.
5094 if (i_isSnapshotMachine())
5095 return setError(VBOX_E_INVALID_VM_STATE,
5096 tr("Cannot set extradata for a snapshot"));
5097
5098 // check if the right IMachine instance is used
5099 if (mData->mRegistered && !i_isSessionMachine())
5100 return setError(VBOX_E_INVALID_VM_STATE,
5101 tr("Cannot set extradata for an immutable machine"));
5102
5103 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5104 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5105 strOldValue = it->second;
5106 }
5107
5108 bool fChanged;
5109 if ((fChanged = (strOldValue != aValue)))
5110 {
5111 // ask for permission from all listeners outside the locks;
5112 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5113 // lock to copy the list of callbacks to invoke
5114 Bstr bstrError;
5115 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5116 {
5117 const char *sep = bstrError.isEmpty() ? "" : ": ";
5118 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5119 return setError(E_ACCESSDENIED,
5120 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5121 aKey.c_str(),
5122 aValue.c_str(),
5123 sep,
5124 bstrError.raw());
5125 }
5126
5127 // data is changing and change not vetoed: then write it out under the lock
5128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5129
5130 if (aValue.isEmpty())
5131 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5132 else
5133 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5134 // creates a new key if needed
5135
5136 bool fNeedsGlobalSaveSettings = false;
5137 // This saving of settings is tricky: there is no "old state" for the
5138 // extradata items at all (unlike all other settings), so the old/new
5139 // settings comparison would give a wrong result!
5140 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5141
5142 if (fNeedsGlobalSaveSettings)
5143 {
5144 // save the global settings; for that we should hold only the VirtualBox lock
5145 alock.release();
5146 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5147 mParent->i_saveSettings();
5148 }
5149 }
5150
5151 // fire notification outside the lock
5152 if (fChanged)
5153 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5154
5155 return S_OK;
5156}
5157
5158HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5159{
5160 aProgress = NULL;
5161 NOREF(aSettingsFilePath);
5162 ReturnComNotImplemented();
5163}
5164
5165HRESULT Machine::saveSettings()
5166{
5167 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5170 if (FAILED(hrc)) return hrc;
5171
5172 /* the settings file path may never be null */
5173 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5174
5175 /* save all VM data excluding snapshots */
5176 bool fNeedsGlobalSaveSettings = false;
5177 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5178 mlock.release();
5179
5180 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
5181 {
5182 // save the global settings; for that we should hold only the VirtualBox lock
5183 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5184 hrc = mParent->i_saveSettings();
5185 }
5186
5187 return hrc;
5188}
5189
5190
5191HRESULT Machine::discardSettings()
5192{
5193 /*
5194 * We need to take the machine list lock here as well as the machine one
5195 * or we'll get into trouble should any media stuff require rolling back.
5196 *
5197 * Details:
5198 *
5199 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5201 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5202 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5203 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5205 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5206 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5207 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5208 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5209 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5210 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5212 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5213 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5214 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5215 * 0:005> k
5216 * # Child-SP RetAddr Call Site
5217 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5218 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5219 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5220 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5221 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5222 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5223 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5224 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5225 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5226 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5227 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5228 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5229 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5230 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5231 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5232 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5233 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5234 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5235 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5236 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5237 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5238 *
5239 */
5240 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5242
5243 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5244 if (FAILED(hrc)) return hrc;
5245
5246 /*
5247 * during this rollback, the session will be notified if data has
5248 * been actually changed
5249 */
5250 i_rollback(true /* aNotify */);
5251
5252 return S_OK;
5253}
5254
5255/** @note Locks objects! */
5256HRESULT Machine::unregister(AutoCaller &autoCaller,
5257 CleanupMode_T aCleanupMode,
5258 std::vector<ComPtr<IMedium> > &aMedia)
5259{
5260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5261
5262 Guid id(i_getId());
5263
5264 if (mData->mSession.mState != SessionState_Unlocked)
5265 return setError(VBOX_E_INVALID_OBJECT_STATE,
5266 tr("Cannot unregister the machine '%s' while it is locked"),
5267 mUserData->s.strName.c_str());
5268
5269 // wait for state dependents to drop to zero
5270 i_ensureNoStateDependencies(alock);
5271
5272 if (!mData->mAccessible)
5273 {
5274 // inaccessible machines can only be unregistered; uninitialize ourselves
5275 // here because currently there may be no unregistered that are inaccessible
5276 // (this state combination is not supported). Note releasing the caller and
5277 // leaving the lock before calling uninit()
5278 alock.release();
5279 autoCaller.release();
5280
5281 uninit();
5282
5283 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5284 // calls VirtualBox::i_saveSettings()
5285
5286 return S_OK;
5287 }
5288
5289 HRESULT hrc = S_OK;
5290 mData->llFilesToDelete.clear();
5291
5292 if (!mSSData->strStateFilePath.isEmpty())
5293 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5294
5295 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5296 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5297 mData->llFilesToDelete.push_back(strNVRAMFile);
5298
5299 // This list collects the medium objects from all medium attachments
5300 // which we will detach from the machine and its snapshots, in a specific
5301 // order which allows for closing all media without getting "media in use"
5302 // errors, simply by going through the list from the front to the back:
5303 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5304 // and must be closed before the parent media from the snapshots, or closing the parents
5305 // will fail because they still have children);
5306 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5307 // the root ("first") snapshot of the machine.
5308 MediaList llMedia;
5309
5310 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5311 && mMediumAttachments->size()
5312 )
5313 {
5314 // we have media attachments: detach them all and add the Medium objects to our list
5315 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5316 }
5317
5318 if (mData->mFirstSnapshot)
5319 {
5320 // add the media from the medium attachments of the snapshots to
5321 // llMedia as well, after the "main" machine media;
5322 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5323 // snapshot machine, depth first.
5324
5325 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5326 MachineState_T oldState = mData->mMachineState;
5327 mData->mMachineState = MachineState_DeletingSnapshot;
5328
5329 // make a copy of the first snapshot reference so the refcount does not
5330 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5331 // (would hang due to the AutoCaller voodoo)
5332 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5333
5334 // GO!
5335 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5336
5337 mData->mMachineState = oldState;
5338 }
5339
5340 if (FAILED(hrc))
5341 {
5342 i_rollbackMedia();
5343 return hrc;
5344 }
5345
5346 // commit all the media changes made above
5347 i_commitMedia();
5348
5349 mData->mRegistered = false;
5350
5351 // machine lock no longer needed
5352 alock.release();
5353
5354 /* Make sure that the settings of the current VM are not saved, because
5355 * they are rather crippled at this point to meet the cleanup expectations
5356 * and there's no point destroying the VM config on disk just because. */
5357 mParent->i_unmarkRegistryModified(id);
5358
5359 // return media to caller
5360 aMedia.resize(llMedia.size());
5361 size_t i = 0;
5362 for (MediaList::const_iterator
5363 it = llMedia.begin();
5364 it != llMedia.end();
5365 ++it, ++i)
5366 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5367
5368 mParent->i_unregisterMachine(this, aCleanupMode, id);
5369 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5370
5371 return S_OK;
5372}
5373
5374/**
5375 * Task record for deleting a machine config.
5376 */
5377class Machine::DeleteConfigTask
5378 : public Machine::Task
5379{
5380public:
5381 DeleteConfigTask(Machine *m,
5382 Progress *p,
5383 const Utf8Str &t,
5384 const RTCList<ComPtr<IMedium> > &llMedia,
5385 const StringsList &llFilesToDelete)
5386 : Task(m, p, t),
5387 m_llMedia(llMedia),
5388 m_llFilesToDelete(llFilesToDelete)
5389 {}
5390
5391private:
5392 void handler()
5393 {
5394 try
5395 {
5396 m_pMachine->i_deleteConfigHandler(*this);
5397 }
5398 catch (...)
5399 {
5400 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5401 }
5402 }
5403
5404 RTCList<ComPtr<IMedium> > m_llMedia;
5405 StringsList m_llFilesToDelete;
5406
5407 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5408};
5409
5410/**
5411 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5412 * SessionMachine::taskHandler().
5413 *
5414 * @note Locks this object for writing.
5415 *
5416 * @param task
5417 */
5418void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5419{
5420 LogFlowThisFuncEnter();
5421
5422 AutoCaller autoCaller(this);
5423 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5424 if (FAILED(autoCaller.hrc()))
5425 {
5426 /* we might have been uninitialized because the session was accidentally
5427 * closed by the client, so don't assert */
5428 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
5429 task.m_pProgress->i_notifyComplete(hrc);
5430 LogFlowThisFuncLeave();
5431 return;
5432 }
5433
5434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5435
5436 HRESULT hrc;
5437 try
5438 {
5439 ULONG uLogHistoryCount = 3;
5440 ComPtr<ISystemProperties> systemProperties;
5441 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5442 if (FAILED(hrc)) throw hrc;
5443
5444 if (!systemProperties.isNull())
5445 {
5446 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5447 if (FAILED(hrc)) throw hrc;
5448 }
5449
5450 MachineState_T oldState = mData->mMachineState;
5451 i_setMachineState(MachineState_SettingUp);
5452 alock.release();
5453 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
5454 {
5455 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
5456 {
5457 AutoCaller mac(pMedium);
5458 if (FAILED(mac.hrc())) throw mac.hrc();
5459 Utf8Str strLocation = pMedium->i_getLocationFull();
5460 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5461 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5462 if (FAILED(hrc)) throw hrc;
5463 }
5464 if (pMedium->i_isMediumFormatFile())
5465 {
5466 ComPtr<IProgress> pProgress2;
5467 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
5468 if (FAILED(hrc)) throw hrc;
5469 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5470 if (FAILED(hrc)) throw hrc;
5471 }
5472
5473 /* Close the medium, deliberately without checking the return
5474 * code, and without leaving any trace in the error info, as
5475 * a failure here is a very minor issue, which shouldn't happen
5476 * as above we even managed to delete the medium. */
5477 {
5478 ErrorInfoKeeper eik;
5479 pMedium->Close();
5480 }
5481 }
5482 i_setMachineState(oldState);
5483 alock.acquire();
5484
5485 // delete the files pushed on the task list by Machine::Delete()
5486 // (this includes saved states of the machine and snapshots and
5487 // medium storage files from the IMedium list passed in, and the
5488 // machine XML file)
5489 for (StringsList::const_iterator
5490 it = task.m_llFilesToDelete.begin();
5491 it != task.m_llFilesToDelete.end();
5492 ++it)
5493 {
5494 const Utf8Str &strFile = *it;
5495 LogFunc(("Deleting file %s\n", strFile.c_str()));
5496 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5497 if (FAILED(hrc)) throw hrc;
5498 i_deleteFile(strFile);
5499 }
5500
5501 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5502 if (FAILED(hrc)) throw hrc;
5503
5504 /* delete the settings only when the file actually exists */
5505 if (mData->pMachineConfigFile->fileExists())
5506 {
5507 /* Delete any backup or uncommitted XML files. Ignore failures.
5508 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5509 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5510 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5511 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5512 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5513 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5514
5515 /* delete the Logs folder, nothing important should be left
5516 * there (we don't check for errors because the user might have
5517 * some private files there that we don't want to delete) */
5518 Utf8Str logFolder;
5519 getLogFolder(logFolder);
5520 Assert(logFolder.length());
5521 if (RTDirExists(logFolder.c_str()))
5522 {
5523 /* Delete all VBox.log[.N] files from the Logs folder
5524 * (this must be in sync with the rotation logic in
5525 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5526 * files that may have been created by the GUI. */
5527 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5528 i_deleteFile(log, true /* fIgnoreFailures */);
5529 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5530 i_deleteFile(log, true /* fIgnoreFailures */);
5531 for (ULONG i = uLogHistoryCount; i > 0; i--)
5532 {
5533 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5534 i_deleteFile(log, true /* fIgnoreFailures */);
5535 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5536 i_deleteFile(log, true /* fIgnoreFailures */);
5537 }
5538 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5539 i_deleteFile(log, true /* fIgnoreFailures */);
5540#if defined(RT_OS_WINDOWS)
5541 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5542 i_deleteFile(log, true /* fIgnoreFailures */);
5543 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5544 i_deleteFile(log, true /* fIgnoreFailures */);
5545#endif
5546
5547 RTDirRemove(logFolder.c_str());
5548 }
5549
5550 /* delete the Snapshots folder, nothing important should be left
5551 * there (we don't check for errors because the user might have
5552 * some private files there that we don't want to delete) */
5553 Utf8Str strFullSnapshotFolder;
5554 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5555 Assert(!strFullSnapshotFolder.isEmpty());
5556 if (RTDirExists(strFullSnapshotFolder.c_str()))
5557 RTDirRemove(strFullSnapshotFolder.c_str());
5558
5559 // delete the directory that contains the settings file, but only
5560 // if it matches the VM name
5561 Utf8Str settingsDir;
5562 if (i_isInOwnDir(&settingsDir))
5563 RTDirRemove(settingsDir.c_str());
5564 }
5565
5566 alock.release();
5567
5568 mParent->i_saveModifiedRegistries();
5569 }
5570 catch (HRESULT hrcXcpt)
5571 {
5572 hrc = hrcXcpt;
5573 }
5574
5575 task.m_pProgress->i_notifyComplete(hrc);
5576
5577 LogFlowThisFuncLeave();
5578}
5579
5580HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5581{
5582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5583
5584 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5585 if (FAILED(hrc)) return hrc;
5586
5587 if (mData->mRegistered)
5588 return setError(VBOX_E_INVALID_VM_STATE,
5589 tr("Cannot delete settings of a registered machine"));
5590
5591 // collect files to delete
5592 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5593 // machine config file
5594 if (mData->pMachineConfigFile->fileExists())
5595 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5596 // backup of machine config file
5597 Utf8Str strTmp(mData->m_strConfigFileFull);
5598 strTmp.append("-prev");
5599 if (RTFileExists(strTmp.c_str()))
5600 llFilesToDelete.push_back(strTmp);
5601
5602 RTCList<ComPtr<IMedium> > llMedia;
5603 for (size_t i = 0; i < aMedia.size(); ++i)
5604 {
5605 IMedium *pIMedium(aMedia[i]);
5606 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5607 if (pMedium.isNull())
5608 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5609 SafeArray<BSTR> ids;
5610 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5611 if (FAILED(hrc)) return hrc;
5612 /* At this point the medium should not have any back references
5613 * anymore. If it has it is attached to another VM and *must* not
5614 * deleted. */
5615 if (ids.size() < 1)
5616 llMedia.append(pMedium);
5617 }
5618
5619 ComObjPtr<Progress> pProgress;
5620 pProgress.createObject();
5621 hrc = pProgress->init(i_getVirtualBox(),
5622 static_cast<IMachine*>(this) /* aInitiator */,
5623 tr("Deleting files"),
5624 true /* fCancellable */,
5625 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
5626 tr("Collecting file inventory"));
5627 if (FAILED(hrc))
5628 return hrc;
5629
5630 /* create and start the task on a separate thread (note that it will not
5631 * start working until we release alock) */
5632 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
5633 hrc = pTask->createThread();
5634 pTask = NULL;
5635 if (FAILED(hrc))
5636 return hrc;
5637
5638 pProgress.queryInterfaceTo(aProgress.asOutParam());
5639
5640 LogFlowFuncLeave();
5641
5642 return S_OK;
5643}
5644
5645HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5646{
5647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5648
5649 ComObjPtr<Snapshot> pSnapshot;
5650 HRESULT hrc;
5651
5652 if (aNameOrId.isEmpty())
5653 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5654 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5655 else
5656 {
5657 Guid uuid(aNameOrId);
5658 if (uuid.isValid())
5659 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5660 else
5661 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5662 }
5663 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5664
5665 return hrc;
5666}
5667
5668HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5669 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5670{
5671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5672
5673 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5674 if (FAILED(hrc)) return hrc;
5675
5676 ComObjPtr<SharedFolder> sharedFolder;
5677 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5678 if (SUCCEEDED(hrc))
5679 return setError(VBOX_E_OBJECT_IN_USE,
5680 tr("Shared folder named '%s' already exists"),
5681 aName.c_str());
5682
5683 sharedFolder.createObject();
5684 hrc = sharedFolder->init(i_getMachine(),
5685 aName,
5686 aHostPath,
5687 !!aWritable,
5688 !!aAutomount,
5689 aAutoMountPoint,
5690 true /* fFailOnError */);
5691 if (FAILED(hrc)) return hrc;
5692
5693 i_setModified(IsModified_SharedFolders);
5694 mHWData.backup();
5695 mHWData->mSharedFolders.push_back(sharedFolder);
5696
5697 /* inform the direct session if any */
5698 alock.release();
5699 i_onSharedFolderChange();
5700
5701 return S_OK;
5702}
5703
5704HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5705{
5706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5707
5708 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5709 if (FAILED(hrc)) return hrc;
5710
5711 ComObjPtr<SharedFolder> sharedFolder;
5712 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5713 if (FAILED(hrc)) return hrc;
5714
5715 i_setModified(IsModified_SharedFolders);
5716 mHWData.backup();
5717 mHWData->mSharedFolders.remove(sharedFolder);
5718
5719 /* inform the direct session if any */
5720 alock.release();
5721 i_onSharedFolderChange();
5722
5723 return S_OK;
5724}
5725
5726HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5727{
5728 /* start with No */
5729 *aCanShow = FALSE;
5730
5731 ComPtr<IInternalSessionControl> directControl;
5732 {
5733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5734
5735 if (mData->mSession.mState != SessionState_Locked)
5736 return setError(VBOX_E_INVALID_VM_STATE,
5737 tr("Machine is not locked for session (session state: %s)"),
5738 Global::stringifySessionState(mData->mSession.mState));
5739
5740 if (mData->mSession.mLockType == LockType_VM)
5741 directControl = mData->mSession.mDirectControl;
5742 }
5743
5744 /* ignore calls made after #OnSessionEnd() is called */
5745 if (!directControl)
5746 return S_OK;
5747
5748 LONG64 dummy;
5749 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5750}
5751
5752HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
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(E_FAIL,
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 BOOL dummy;
5772 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5773}
5774
5775#ifdef VBOX_WITH_GUEST_PROPS
5776/**
5777 * Look up a guest property in VBoxSVC's internal structures.
5778 */
5779HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5780 com::Utf8Str &aValue,
5781 LONG64 *aTimestamp,
5782 com::Utf8Str &aFlags) const
5783{
5784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5785
5786 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5787 if (it != mHWData->mGuestProperties.end())
5788 {
5789 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5790 aValue = it->second.strValue;
5791 *aTimestamp = it->second.mTimestamp;
5792 GuestPropWriteFlags(it->second.mFlags, szFlags);
5793 aFlags = Utf8Str(szFlags);
5794 }
5795
5796 return S_OK;
5797}
5798
5799/**
5800 * Query the VM that a guest property belongs to for the property.
5801 * @returns E_ACCESSDENIED if the VM process is not available or not
5802 * currently handling queries and the lookup should then be done in
5803 * VBoxSVC.
5804 */
5805HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5806 com::Utf8Str &aValue,
5807 LONG64 *aTimestamp,
5808 com::Utf8Str &aFlags) const
5809{
5810 HRESULT hrc = S_OK;
5811 Bstr bstrValue;
5812 Bstr bstrFlags;
5813
5814 ComPtr<IInternalSessionControl> directControl;
5815 {
5816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5817 if (mData->mSession.mLockType == LockType_VM)
5818 directControl = mData->mSession.mDirectControl;
5819 }
5820
5821 /* ignore calls made after #OnSessionEnd() is called */
5822 if (!directControl)
5823 hrc = E_ACCESSDENIED;
5824 else
5825 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5826 0 /* accessMode */,
5827 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5828
5829 aValue = bstrValue;
5830 aFlags = bstrFlags;
5831
5832 return hrc;
5833}
5834#endif // VBOX_WITH_GUEST_PROPS
5835
5836HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5837 com::Utf8Str &aValue,
5838 LONG64 *aTimestamp,
5839 com::Utf8Str &aFlags)
5840{
5841#ifndef VBOX_WITH_GUEST_PROPS
5842 ReturnComNotImplemented();
5843#else // VBOX_WITH_GUEST_PROPS
5844
5845 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5846
5847 if (hrc == E_ACCESSDENIED)
5848 /* The VM is not running or the service is not (yet) accessible */
5849 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5850 return hrc;
5851#endif // VBOX_WITH_GUEST_PROPS
5852}
5853
5854HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5855{
5856 LONG64 dummyTimestamp;
5857 com::Utf8Str dummyFlags;
5858 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5859
5860}
5861HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5862{
5863 com::Utf8Str dummyFlags;
5864 com::Utf8Str dummyValue;
5865 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5866}
5867
5868#ifdef VBOX_WITH_GUEST_PROPS
5869/**
5870 * Set a guest property in VBoxSVC's internal structures.
5871 */
5872HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5873 const com::Utf8Str &aFlags, bool fDelete)
5874{
5875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5876 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5877 if (FAILED(hrc)) return hrc;
5878
5879 try
5880 {
5881 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5882 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5883 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5884
5885 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5886 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5887
5888 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5889 if (it == mHWData->mGuestProperties.end())
5890 {
5891 if (!fDelete)
5892 {
5893 i_setModified(IsModified_MachineData);
5894 mHWData.backupEx();
5895
5896 RTTIMESPEC time;
5897 HWData::GuestProperty prop;
5898 prop.strValue = aValue;
5899 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5900 prop.mFlags = fFlags;
5901 mHWData->mGuestProperties[aName] = prop;
5902 }
5903 }
5904 else
5905 {
5906 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5907 {
5908 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5909 }
5910 else
5911 {
5912 i_setModified(IsModified_MachineData);
5913 mHWData.backupEx();
5914
5915 /* The backupEx() operation invalidates our iterator,
5916 * so get a new one. */
5917 it = mHWData->mGuestProperties.find(aName);
5918 Assert(it != mHWData->mGuestProperties.end());
5919
5920 if (!fDelete)
5921 {
5922 RTTIMESPEC time;
5923 it->second.strValue = aValue;
5924 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5925 it->second.mFlags = fFlags;
5926 }
5927 else
5928 mHWData->mGuestProperties.erase(it);
5929 }
5930 }
5931
5932 if (SUCCEEDED(hrc))
5933 {
5934 alock.release();
5935
5936 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5937 }
5938 }
5939 catch (std::bad_alloc &)
5940 {
5941 hrc = E_OUTOFMEMORY;
5942 }
5943
5944 return hrc;
5945}
5946
5947/**
5948 * Set a property on the VM that that property belongs to.
5949 * @returns E_ACCESSDENIED if the VM process is not available or not
5950 * currently handling queries and the setting should then be done in
5951 * VBoxSVC.
5952 */
5953HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5954 const com::Utf8Str &aFlags, bool fDelete)
5955{
5956 HRESULT hrc;
5957
5958 try
5959 {
5960 ComPtr<IInternalSessionControl> directControl;
5961 {
5962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5963 if (mData->mSession.mLockType == LockType_VM)
5964 directControl = mData->mSession.mDirectControl;
5965 }
5966
5967 Bstr dummy1; /* will not be changed (setter) */
5968 Bstr dummy2; /* will not be changed (setter) */
5969 LONG64 dummy64;
5970 if (!directControl)
5971 hrc = E_ACCESSDENIED;
5972 else
5973 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5974 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5975 fDelete ? 2 : 1 /* accessMode */,
5976 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5977 }
5978 catch (std::bad_alloc &)
5979 {
5980 hrc = E_OUTOFMEMORY;
5981 }
5982
5983 return hrc;
5984}
5985#endif // VBOX_WITH_GUEST_PROPS
5986
5987HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5988 const com::Utf8Str &aFlags)
5989{
5990#ifndef VBOX_WITH_GUEST_PROPS
5991 ReturnComNotImplemented();
5992#else // VBOX_WITH_GUEST_PROPS
5993
5994 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5995 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5996
5997 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5998 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5999
6000 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6001 if (hrc == E_ACCESSDENIED)
6002 /* The VM is not running or the service is not (yet) accessible */
6003 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6004 return hrc;
6005#endif // VBOX_WITH_GUEST_PROPS
6006}
6007
6008HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6009{
6010 return setGuestProperty(aProperty, aValue, "");
6011}
6012
6013HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6014{
6015#ifndef VBOX_WITH_GUEST_PROPS
6016 ReturnComNotImplemented();
6017#else // VBOX_WITH_GUEST_PROPS
6018 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6019 if (hrc == E_ACCESSDENIED)
6020 /* The VM is not running or the service is not (yet) accessible */
6021 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6022 return hrc;
6023#endif // VBOX_WITH_GUEST_PROPS
6024}
6025
6026#ifdef VBOX_WITH_GUEST_PROPS
6027/**
6028 * Enumerate the guest properties in VBoxSVC's internal structures.
6029 */
6030HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6031 std::vector<com::Utf8Str> &aNames,
6032 std::vector<com::Utf8Str> &aValues,
6033 std::vector<LONG64> &aTimestamps,
6034 std::vector<com::Utf8Str> &aFlags)
6035{
6036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6037 Utf8Str strPatterns(aPatterns);
6038
6039 /*
6040 * Look for matching patterns and build up a list.
6041 */
6042 HWData::GuestPropertyMap propMap;
6043 for (HWData::GuestPropertyMap::const_iterator
6044 it = mHWData->mGuestProperties.begin();
6045 it != mHWData->mGuestProperties.end();
6046 ++it)
6047 {
6048 if ( strPatterns.isEmpty()
6049 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6050 RTSTR_MAX,
6051 it->first.c_str(),
6052 RTSTR_MAX,
6053 NULL)
6054 )
6055 propMap.insert(*it);
6056 }
6057
6058 alock.release();
6059
6060 /*
6061 * And build up the arrays for returning the property information.
6062 */
6063 size_t cEntries = propMap.size();
6064
6065 aNames.resize(cEntries);
6066 aValues.resize(cEntries);
6067 aTimestamps.resize(cEntries);
6068 aFlags.resize(cEntries);
6069
6070 size_t i = 0;
6071 for (HWData::GuestPropertyMap::const_iterator
6072 it = propMap.begin();
6073 it != propMap.end();
6074 ++it, ++i)
6075 {
6076 aNames[i] = it->first;
6077 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6078 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6079
6080 aValues[i] = it->second.strValue;
6081 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6082 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6083
6084 aTimestamps[i] = it->second.mTimestamp;
6085
6086 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6087 GuestPropWriteFlags(it->second.mFlags, szFlags);
6088 aFlags[i] = szFlags;
6089 }
6090
6091 return S_OK;
6092}
6093
6094/**
6095 * Enumerate the properties managed by a VM.
6096 * @returns E_ACCESSDENIED if the VM process is not available or not
6097 * currently handling queries and the setting should then be done in
6098 * VBoxSVC.
6099 */
6100HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6101 std::vector<com::Utf8Str> &aNames,
6102 std::vector<com::Utf8Str> &aValues,
6103 std::vector<LONG64> &aTimestamps,
6104 std::vector<com::Utf8Str> &aFlags)
6105{
6106 HRESULT hrc;
6107 ComPtr<IInternalSessionControl> directControl;
6108 {
6109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6110 if (mData->mSession.mLockType == LockType_VM)
6111 directControl = mData->mSession.mDirectControl;
6112 }
6113
6114 com::SafeArray<BSTR> bNames;
6115 com::SafeArray<BSTR> bValues;
6116 com::SafeArray<LONG64> bTimestamps;
6117 com::SafeArray<BSTR> bFlags;
6118
6119 if (!directControl)
6120 hrc = E_ACCESSDENIED;
6121 else
6122 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6123 ComSafeArrayAsOutParam(bNames),
6124 ComSafeArrayAsOutParam(bValues),
6125 ComSafeArrayAsOutParam(bTimestamps),
6126 ComSafeArrayAsOutParam(bFlags));
6127 size_t i;
6128 aNames.resize(bNames.size());
6129 for (i = 0; i < bNames.size(); ++i)
6130 aNames[i] = Utf8Str(bNames[i]);
6131 aValues.resize(bValues.size());
6132 for (i = 0; i < bValues.size(); ++i)
6133 aValues[i] = Utf8Str(bValues[i]);
6134 aTimestamps.resize(bTimestamps.size());
6135 for (i = 0; i < bTimestamps.size(); ++i)
6136 aTimestamps[i] = bTimestamps[i];
6137 aFlags.resize(bFlags.size());
6138 for (i = 0; i < bFlags.size(); ++i)
6139 aFlags[i] = Utf8Str(bFlags[i]);
6140
6141 return hrc;
6142}
6143#endif // VBOX_WITH_GUEST_PROPS
6144HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6145 std::vector<com::Utf8Str> &aNames,
6146 std::vector<com::Utf8Str> &aValues,
6147 std::vector<LONG64> &aTimestamps,
6148 std::vector<com::Utf8Str> &aFlags)
6149{
6150#ifndef VBOX_WITH_GUEST_PROPS
6151 ReturnComNotImplemented();
6152#else // VBOX_WITH_GUEST_PROPS
6153
6154 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6155
6156 if (hrc == E_ACCESSDENIED)
6157 /* The VM is not running or the service is not (yet) accessible */
6158 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6159 return hrc;
6160#endif // VBOX_WITH_GUEST_PROPS
6161}
6162
6163HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6164 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6165{
6166 MediumAttachmentList atts;
6167
6168 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
6169 if (FAILED(hrc)) return hrc;
6170
6171 aMediumAttachments.resize(atts.size());
6172 size_t i = 0;
6173 for (MediumAttachmentList::const_iterator
6174 it = atts.begin();
6175 it != atts.end();
6176 ++it, ++i)
6177 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6178
6179 return S_OK;
6180}
6181
6182HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6183 LONG aControllerPort,
6184 LONG aDevice,
6185 ComPtr<IMediumAttachment> &aAttachment)
6186{
6187 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6188 aName.c_str(), aControllerPort, aDevice));
6189
6190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6191
6192 aAttachment = NULL;
6193
6194 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6195 aName,
6196 aControllerPort,
6197 aDevice);
6198 if (pAttach.isNull())
6199 return setError(VBOX_E_OBJECT_NOT_FOUND,
6200 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6201 aDevice, aControllerPort, aName.c_str());
6202
6203 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6204
6205 return S_OK;
6206}
6207
6208
6209HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6210 StorageBus_T aConnectionType,
6211 ComPtr<IStorageController> &aController)
6212{
6213 if ( (aConnectionType <= StorageBus_Null)
6214 || (aConnectionType > StorageBus_VirtioSCSI))
6215 return setError(E_INVALIDARG,
6216 tr("Invalid connection type: %d"),
6217 aConnectionType);
6218
6219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6220
6221 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6222 if (FAILED(hrc)) return hrc;
6223
6224 /* try to find one with the name first. */
6225 ComObjPtr<StorageController> ctrl;
6226
6227 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6228 if (SUCCEEDED(hrc))
6229 return setError(VBOX_E_OBJECT_IN_USE,
6230 tr("Storage controller named '%s' already exists"),
6231 aName.c_str());
6232
6233 ctrl.createObject();
6234
6235 /* get a new instance number for the storage controller */
6236 ULONG ulInstance = 0;
6237 bool fBootable = true;
6238 for (StorageControllerList::const_iterator
6239 it = mStorageControllers->begin();
6240 it != mStorageControllers->end();
6241 ++it)
6242 {
6243 if ((*it)->i_getStorageBus() == aConnectionType)
6244 {
6245 ULONG ulCurInst = (*it)->i_getInstance();
6246
6247 if (ulCurInst >= ulInstance)
6248 ulInstance = ulCurInst + 1;
6249
6250 /* Only one controller of each type can be marked as bootable. */
6251 if ((*it)->i_getBootable())
6252 fBootable = false;
6253 }
6254 }
6255
6256 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6257 if (FAILED(hrc)) return hrc;
6258
6259 i_setModified(IsModified_Storage);
6260 mStorageControllers.backup();
6261 mStorageControllers->push_back(ctrl);
6262
6263 ctrl.queryInterfaceTo(aController.asOutParam());
6264
6265 /* inform the direct session if any */
6266 alock.release();
6267 i_onStorageControllerChange(i_getId(), aName);
6268
6269 return S_OK;
6270}
6271
6272HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6273 ComPtr<IStorageController> &aStorageController)
6274{
6275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6276
6277 ComObjPtr<StorageController> ctrl;
6278
6279 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6280 if (SUCCEEDED(hrc))
6281 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6282
6283 return hrc;
6284}
6285
6286HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6287 ULONG aInstance,
6288 ComPtr<IStorageController> &aStorageController)
6289{
6290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 for (StorageControllerList::const_iterator
6293 it = mStorageControllers->begin();
6294 it != mStorageControllers->end();
6295 ++it)
6296 {
6297 if ( (*it)->i_getStorageBus() == aConnectionType
6298 && (*it)->i_getInstance() == aInstance)
6299 {
6300 (*it).queryInterfaceTo(aStorageController.asOutParam());
6301 return S_OK;
6302 }
6303 }
6304
6305 return setError(VBOX_E_OBJECT_NOT_FOUND,
6306 tr("Could not find a storage controller with instance number '%lu'"),
6307 aInstance);
6308}
6309
6310HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6311{
6312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6315 if (FAILED(hrc)) return hrc;
6316
6317 ComObjPtr<StorageController> ctrl;
6318
6319 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6320 if (SUCCEEDED(hrc))
6321 {
6322 /* Ensure that only one controller of each type is marked as bootable. */
6323 if (aBootable == TRUE)
6324 {
6325 for (StorageControllerList::const_iterator
6326 it = mStorageControllers->begin();
6327 it != mStorageControllers->end();
6328 ++it)
6329 {
6330 ComObjPtr<StorageController> aCtrl = (*it);
6331
6332 if ( (aCtrl->i_getName() != aName)
6333 && aCtrl->i_getBootable() == TRUE
6334 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6335 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6336 {
6337 aCtrl->i_setBootable(FALSE);
6338 break;
6339 }
6340 }
6341 }
6342
6343 if (SUCCEEDED(hrc))
6344 {
6345 ctrl->i_setBootable(aBootable);
6346 i_setModified(IsModified_Storage);
6347 }
6348 }
6349
6350 if (SUCCEEDED(hrc))
6351 {
6352 /* inform the direct session if any */
6353 alock.release();
6354 i_onStorageControllerChange(i_getId(), aName);
6355 }
6356
6357 return hrc;
6358}
6359
6360HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6361{
6362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6363
6364 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6365 if (FAILED(hrc)) return hrc;
6366
6367 ComObjPtr<StorageController> ctrl;
6368 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6369 if (FAILED(hrc)) return hrc;
6370
6371 MediumAttachmentList llDetachedAttachments;
6372 {
6373 /* find all attached devices to the appropriate storage controller and detach them all */
6374 // make a temporary list because detachDevice invalidates iterators into
6375 // mMediumAttachments
6376 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6377
6378 for (MediumAttachmentList::const_iterator
6379 it = llAttachments2.begin();
6380 it != llAttachments2.end();
6381 ++it)
6382 {
6383 MediumAttachment *pAttachTemp = *it;
6384
6385 AutoCaller localAutoCaller(pAttachTemp);
6386 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
6387
6388 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6389
6390 if (pAttachTemp->i_getControllerName() == aName)
6391 {
6392 llDetachedAttachments.push_back(pAttachTemp);
6393 hrc = i_detachDevice(pAttachTemp, alock, NULL);
6394 if (FAILED(hrc)) return hrc;
6395 }
6396 }
6397 }
6398
6399 /* send event about detached devices before removing parent controller */
6400 for (MediumAttachmentList::const_iterator
6401 it = llDetachedAttachments.begin();
6402 it != llDetachedAttachments.end();
6403 ++it)
6404 {
6405 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6406 }
6407
6408 /* We can remove it now. */
6409 i_setModified(IsModified_Storage);
6410 mStorageControllers.backup();
6411
6412 ctrl->i_unshare();
6413
6414 mStorageControllers->remove(ctrl);
6415
6416 /* inform the direct session if any */
6417 alock.release();
6418 i_onStorageControllerChange(i_getId(), aName);
6419
6420 return S_OK;
6421}
6422
6423HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6424 ComPtr<IUSBController> &aController)
6425{
6426 if ( (aType <= USBControllerType_Null)
6427 || (aType >= USBControllerType_Last))
6428 return setError(E_INVALIDARG,
6429 tr("Invalid USB controller type: %d"),
6430 aType);
6431
6432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6433
6434 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6435 if (FAILED(hrc)) return hrc;
6436
6437 /* try to find one with the same type first. */
6438 ComObjPtr<USBController> ctrl;
6439
6440 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6441 if (SUCCEEDED(hrc))
6442 return setError(VBOX_E_OBJECT_IN_USE,
6443 tr("USB controller named '%s' already exists"),
6444 aName.c_str());
6445
6446 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6447 ULONG maxInstances;
6448 hrc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6449 if (FAILED(hrc))
6450 return hrc;
6451
6452 ULONG cInstances = i_getUSBControllerCountByType(aType);
6453 if (cInstances >= maxInstances)
6454 return setError(E_INVALIDARG,
6455 tr("Too many USB controllers of this type"));
6456
6457 ctrl.createObject();
6458
6459 hrc = ctrl->init(this, aName, aType);
6460 if (FAILED(hrc)) return hrc;
6461
6462 i_setModified(IsModified_USB);
6463 mUSBControllers.backup();
6464 mUSBControllers->push_back(ctrl);
6465
6466 ctrl.queryInterfaceTo(aController.asOutParam());
6467
6468 /* inform the direct session if any */
6469 alock.release();
6470 i_onUSBControllerChange();
6471
6472 return S_OK;
6473}
6474
6475HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6476{
6477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6478
6479 ComObjPtr<USBController> ctrl;
6480
6481 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6482 if (SUCCEEDED(hrc))
6483 ctrl.queryInterfaceTo(aController.asOutParam());
6484
6485 return hrc;
6486}
6487
6488HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6489 ULONG *aControllers)
6490{
6491 if ( (aType <= USBControllerType_Null)
6492 || (aType >= USBControllerType_Last))
6493 return setError(E_INVALIDARG,
6494 tr("Invalid USB controller type: %d"),
6495 aType);
6496
6497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6498
6499 ComObjPtr<USBController> ctrl;
6500
6501 *aControllers = i_getUSBControllerCountByType(aType);
6502
6503 return S_OK;
6504}
6505
6506HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6507{
6508
6509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6512 if (FAILED(hrc)) return hrc;
6513
6514 ComObjPtr<USBController> ctrl;
6515 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6516 if (FAILED(hrc)) return hrc;
6517
6518 i_setModified(IsModified_USB);
6519 mUSBControllers.backup();
6520
6521 ctrl->i_unshare();
6522
6523 mUSBControllers->remove(ctrl);
6524
6525 /* inform the direct session if any */
6526 alock.release();
6527 i_onUSBControllerChange();
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6533 ULONG *aOriginX,
6534 ULONG *aOriginY,
6535 ULONG *aWidth,
6536 ULONG *aHeight,
6537 BOOL *aEnabled)
6538{
6539 uint32_t u32OriginX= 0;
6540 uint32_t u32OriginY= 0;
6541 uint32_t u32Width = 0;
6542 uint32_t u32Height = 0;
6543 uint16_t u16Flags = 0;
6544
6545#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6546 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6547#else
6548 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6549#endif
6550 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6551 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6552 if (RT_FAILURE(vrc))
6553 {
6554#ifdef RT_OS_WINDOWS
6555 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6556 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6557 * So just assign fEnable to TRUE again.
6558 * The right fix would be to change GUI API wrappers to make sure that parameters
6559 * are changed only if API succeeds.
6560 */
6561 *aEnabled = TRUE;
6562#endif
6563 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6564 tr("Saved guest size is not available (%Rrc)"),
6565 vrc);
6566 }
6567
6568 *aOriginX = u32OriginX;
6569 *aOriginY = u32OriginY;
6570 *aWidth = u32Width;
6571 *aHeight = u32Height;
6572 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6573
6574 return S_OK;
6575}
6576
6577HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6578 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6579{
6580 if (aScreenId != 0)
6581 return E_NOTIMPL;
6582
6583 if ( aBitmapFormat != BitmapFormat_BGR0
6584 && aBitmapFormat != BitmapFormat_BGRA
6585 && aBitmapFormat != BitmapFormat_RGBA
6586 && aBitmapFormat != BitmapFormat_PNG)
6587 return setError(E_NOTIMPL,
6588 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6589
6590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6591
6592 uint8_t *pu8Data = NULL;
6593 uint32_t cbData = 0;
6594 uint32_t u32Width = 0;
6595 uint32_t u32Height = 0;
6596
6597#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6598 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6599#else
6600 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6601#endif
6602 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6603 &pu8Data, &cbData, &u32Width, &u32Height);
6604 if (RT_FAILURE(vrc))
6605 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6606 tr("Saved thumbnail data is not available (%Rrc)"),
6607 vrc);
6608
6609 HRESULT hrc = S_OK;
6610
6611 *aWidth = u32Width;
6612 *aHeight = u32Height;
6613
6614 if (cbData > 0)
6615 {
6616 /* Convert pixels to the format expected by the API caller. */
6617 if (aBitmapFormat == BitmapFormat_BGR0)
6618 {
6619 /* [0] B, [1] G, [2] R, [3] 0. */
6620 aData.resize(cbData);
6621 memcpy(&aData.front(), pu8Data, cbData);
6622 }
6623 else if (aBitmapFormat == BitmapFormat_BGRA)
6624 {
6625 /* [0] B, [1] G, [2] R, [3] A. */
6626 aData.resize(cbData);
6627 for (uint32_t i = 0; i < cbData; i += 4)
6628 {
6629 aData[i] = pu8Data[i];
6630 aData[i + 1] = pu8Data[i + 1];
6631 aData[i + 2] = pu8Data[i + 2];
6632 aData[i + 3] = 0xff;
6633 }
6634 }
6635 else if (aBitmapFormat == BitmapFormat_RGBA)
6636 {
6637 /* [0] R, [1] G, [2] B, [3] A. */
6638 aData.resize(cbData);
6639 for (uint32_t i = 0; i < cbData; i += 4)
6640 {
6641 aData[i] = pu8Data[i + 2];
6642 aData[i + 1] = pu8Data[i + 1];
6643 aData[i + 2] = pu8Data[i];
6644 aData[i + 3] = 0xff;
6645 }
6646 }
6647 else if (aBitmapFormat == BitmapFormat_PNG)
6648 {
6649 uint8_t *pu8PNG = NULL;
6650 uint32_t cbPNG = 0;
6651 uint32_t cxPNG = 0;
6652 uint32_t cyPNG = 0;
6653
6654 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6655
6656 if (RT_SUCCESS(vrc))
6657 {
6658 aData.resize(cbPNG);
6659 if (cbPNG)
6660 memcpy(&aData.front(), pu8PNG, cbPNG);
6661 }
6662 else
6663 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6664
6665 RTMemFree(pu8PNG);
6666 }
6667 }
6668
6669 freeSavedDisplayScreenshot(pu8Data);
6670
6671 return hrc;
6672}
6673
6674HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6675 ULONG *aWidth,
6676 ULONG *aHeight,
6677 std::vector<BitmapFormat_T> &aBitmapFormats)
6678{
6679 if (aScreenId != 0)
6680 return E_NOTIMPL;
6681
6682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 uint8_t *pu8Data = NULL;
6685 uint32_t cbData = 0;
6686 uint32_t u32Width = 0;
6687 uint32_t u32Height = 0;
6688
6689#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6690 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6691#else
6692 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6693#endif
6694 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6695 &pu8Data, &cbData, &u32Width, &u32Height);
6696
6697 if (RT_FAILURE(vrc))
6698 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6699 tr("Saved screenshot data is not available (%Rrc)"),
6700 vrc);
6701
6702 *aWidth = u32Width;
6703 *aHeight = u32Height;
6704 aBitmapFormats.resize(1);
6705 aBitmapFormats[0] = BitmapFormat_PNG;
6706
6707 freeSavedDisplayScreenshot(pu8Data);
6708
6709 return S_OK;
6710}
6711
6712HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6713 BitmapFormat_T aBitmapFormat,
6714 ULONG *aWidth,
6715 ULONG *aHeight,
6716 std::vector<BYTE> &aData)
6717{
6718 if (aScreenId != 0)
6719 return E_NOTIMPL;
6720
6721 if (aBitmapFormat != BitmapFormat_PNG)
6722 return E_NOTIMPL;
6723
6724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6725
6726 uint8_t *pu8Data = NULL;
6727 uint32_t cbData = 0;
6728 uint32_t u32Width = 0;
6729 uint32_t u32Height = 0;
6730
6731#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6732 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6733#else
6734 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6735#endif
6736 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6737 &pu8Data, &cbData, &u32Width, &u32Height);
6738
6739 if (RT_FAILURE(vrc))
6740 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6741 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6742 vrc);
6743
6744 *aWidth = u32Width;
6745 *aHeight = u32Height;
6746
6747 aData.resize(cbData);
6748 if (cbData)
6749 memcpy(&aData.front(), pu8Data, cbData);
6750
6751 freeSavedDisplayScreenshot(pu8Data);
6752
6753 return S_OK;
6754}
6755
6756HRESULT Machine::hotPlugCPU(ULONG aCpu)
6757{
6758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6759
6760 if (!mHWData->mCPUHotPlugEnabled)
6761 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6762
6763 if (aCpu >= mHWData->mCPUCount)
6764 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6765
6766 if (mHWData->mCPUAttached[aCpu])
6767 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6768
6769 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6770 if (FAILED(hrc)) return hrc;
6771
6772 alock.release();
6773 hrc = i_onCPUChange(aCpu, false);
6774 alock.acquire();
6775 if (FAILED(hrc)) return hrc;
6776
6777 i_setModified(IsModified_MachineData);
6778 mHWData.backup();
6779 mHWData->mCPUAttached[aCpu] = true;
6780
6781 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6782 if (Global::IsOnline(mData->mMachineState))
6783 i_saveSettings(NULL, alock);
6784
6785 return S_OK;
6786}
6787
6788HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6789{
6790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6791
6792 if (!mHWData->mCPUHotPlugEnabled)
6793 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6794
6795 if (aCpu >= SchemaDefs::MaxCPUCount)
6796 return setError(E_INVALIDARG,
6797 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6798 SchemaDefs::MaxCPUCount);
6799
6800 if (!mHWData->mCPUAttached[aCpu])
6801 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6802
6803 /* CPU 0 can't be detached */
6804 if (aCpu == 0)
6805 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6806
6807 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6808 if (FAILED(hrc)) return hrc;
6809
6810 alock.release();
6811 hrc = i_onCPUChange(aCpu, true);
6812 alock.acquire();
6813 if (FAILED(hrc)) return hrc;
6814
6815 i_setModified(IsModified_MachineData);
6816 mHWData.backup();
6817 mHWData->mCPUAttached[aCpu] = false;
6818
6819 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6820 if (Global::IsOnline(mData->mMachineState))
6821 i_saveSettings(NULL, alock);
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6827{
6828 *aAttached = false;
6829
6830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6831
6832 /* If hotplug is enabled the CPU is always enabled. */
6833 if (!mHWData->mCPUHotPlugEnabled)
6834 {
6835 if (aCpu < mHWData->mCPUCount)
6836 *aAttached = true;
6837 }
6838 else
6839 {
6840 if (aCpu < SchemaDefs::MaxCPUCount)
6841 *aAttached = mHWData->mCPUAttached[aCpu];
6842 }
6843
6844 return S_OK;
6845}
6846
6847HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6848{
6849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6850
6851 Utf8Str log = i_getLogFilename(aIdx);
6852 if (!RTFileExists(log.c_str()))
6853 log.setNull();
6854 aFilename = log;
6855
6856 return S_OK;
6857}
6858
6859HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6860{
6861 if (aSize < 0)
6862 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6863
6864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6865
6866 HRESULT hrc = S_OK;
6867 Utf8Str log = i_getLogFilename(aIdx);
6868
6869 /* do not unnecessarily hold the lock while doing something which does
6870 * not need the lock and potentially takes a long time. */
6871 alock.release();
6872
6873 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6874 * keeps the SOAP reply size under 1M for the webservice (we're using
6875 * base64 encoded strings for binary data for years now, avoiding the
6876 * expansion of each byte array element to approx. 25 bytes of XML. */
6877 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6878 aData.resize(cbData);
6879
6880 int vrc = VINF_SUCCESS;
6881 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6882
6883#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6884 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6885 {
6886 PCVBOXCRYPTOIF pCryptoIf = NULL;
6887 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6888 if (SUCCEEDED(hrc))
6889 {
6890 alock.acquire();
6891
6892 SecretKey *pKey = NULL;
6893 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6894 alock.release();
6895
6896 if (RT_SUCCESS(vrc))
6897 {
6898 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6899 if (RT_SUCCESS(vrc))
6900 {
6901 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6902 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6903 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6904 if (RT_SUCCESS(vrc))
6905 {
6906 RTVfsIoStrmRelease(hVfsIosLog);
6907 hVfsIosLog = hVfsIosLogDec;
6908 }
6909 }
6910
6911 pKey->release();
6912 }
6913
6914 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6915 }
6916 }
6917 else
6918 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6919#else
6920 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6921#endif
6922 if (RT_SUCCESS(vrc))
6923 {
6924 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6925 cbData ? &aData.front() : NULL, cbData,
6926 true /*fBlocking*/, &cbData);
6927 if (RT_SUCCESS(vrc))
6928 aData.resize(cbData);
6929 else
6930 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6931
6932 RTVfsIoStrmRelease(hVfsIosLog);
6933 }
6934 else
6935 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6936
6937 if (FAILED(hrc))
6938 aData.resize(0);
6939
6940 return hrc;
6941}
6942
6943
6944/**
6945 * Currently this method doesn't attach device to the running VM,
6946 * just makes sure it's plugged on next VM start.
6947 */
6948HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6949{
6950 // lock scope
6951 {
6952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6953
6954 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6955 if (FAILED(hrc)) return hrc;
6956
6957 ChipsetType_T aChipset = ChipsetType_PIIX3;
6958 COMGETTER(ChipsetType)(&aChipset);
6959
6960 if (aChipset != ChipsetType_ICH9)
6961 {
6962 return setError(E_INVALIDARG,
6963 tr("Host PCI attachment only supported with ICH9 chipset"));
6964 }
6965
6966 // check if device with this host PCI address already attached
6967 for (HWData::PCIDeviceAssignmentList::const_iterator
6968 it = mHWData->mPCIDeviceAssignments.begin();
6969 it != mHWData->mPCIDeviceAssignments.end();
6970 ++it)
6971 {
6972 LONG iHostAddress = -1;
6973 ComPtr<PCIDeviceAttachment> pAttach;
6974 pAttach = *it;
6975 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6976 if (iHostAddress == aHostAddress)
6977 return setError(E_INVALIDARG,
6978 tr("Device with host PCI address already attached to this VM"));
6979 }
6980
6981 ComObjPtr<PCIDeviceAttachment> pda;
6982 char name[32];
6983
6984 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6985 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6986 pda.createObject();
6987 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6988 i_setModified(IsModified_MachineData);
6989 mHWData.backup();
6990 mHWData->mPCIDeviceAssignments.push_back(pda);
6991 }
6992
6993 return S_OK;
6994}
6995
6996/**
6997 * Currently this method doesn't detach device from the running VM,
6998 * just makes sure it's not plugged on next VM start.
6999 */
7000HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7001{
7002 ComObjPtr<PCIDeviceAttachment> pAttach;
7003 bool fRemoved = false;
7004 HRESULT hrc;
7005
7006 // lock scope
7007 {
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009
7010 hrc = i_checkStateDependency(MutableStateDep);
7011 if (FAILED(hrc)) return hrc;
7012
7013 for (HWData::PCIDeviceAssignmentList::const_iterator
7014 it = mHWData->mPCIDeviceAssignments.begin();
7015 it != mHWData->mPCIDeviceAssignments.end();
7016 ++it)
7017 {
7018 LONG iHostAddress = -1;
7019 pAttach = *it;
7020 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7021 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7022 {
7023 i_setModified(IsModified_MachineData);
7024 mHWData.backup();
7025 mHWData->mPCIDeviceAssignments.remove(pAttach);
7026 fRemoved = true;
7027 break;
7028 }
7029 }
7030 }
7031
7032
7033 /* Fire event outside of the lock */
7034 if (fRemoved)
7035 {
7036 Assert(!pAttach.isNull());
7037 ComPtr<IEventSource> es;
7038 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
7039 Assert(SUCCEEDED(hrc));
7040 Bstr mid;
7041 hrc = this->COMGETTER(Id)(mid.asOutParam());
7042 Assert(SUCCEEDED(hrc));
7043 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7044 }
7045
7046 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7047 tr("No host PCI device %08x attached"),
7048 aHostAddress
7049 );
7050}
7051
7052HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7053{
7054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7055
7056 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7057 size_t i = 0;
7058 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7059 it = mHWData->mPCIDeviceAssignments.begin();
7060 it != mHWData->mPCIDeviceAssignments.end();
7061 ++it, ++i)
7062 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7063
7064 return S_OK;
7065}
7066
7067HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7068{
7069 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7070
7071 return S_OK;
7072}
7073
7074HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7075{
7076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7077
7078 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7079
7080 return S_OK;
7081}
7082
7083HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7084{
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7087 if (SUCCEEDED(hrc))
7088 {
7089 hrc = mHWData.backupEx();
7090 if (SUCCEEDED(hrc))
7091 {
7092 i_setModified(IsModified_MachineData);
7093 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7094 }
7095 }
7096 return hrc;
7097}
7098
7099HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7100{
7101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7102 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7103 return S_OK;
7104}
7105
7106HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7107{
7108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7109 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7110 if (SUCCEEDED(hrc))
7111 {
7112 hrc = mHWData.backupEx();
7113 if (SUCCEEDED(hrc))
7114 {
7115 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7116 if (SUCCEEDED(hrc))
7117 i_setModified(IsModified_MachineData);
7118 }
7119 }
7120 return hrc;
7121}
7122
7123HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7124{
7125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7126
7127 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7128
7129 return S_OK;
7130}
7131
7132HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7133{
7134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7135 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7136 if (SUCCEEDED(hrc))
7137 {
7138 hrc = mHWData.backupEx();
7139 if (SUCCEEDED(hrc))
7140 {
7141 i_setModified(IsModified_MachineData);
7142 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7143 }
7144 }
7145 return hrc;
7146}
7147
7148HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7149{
7150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7153
7154 return S_OK;
7155}
7156
7157HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7158{
7159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7160
7161 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7162 if ( SUCCEEDED(hrc)
7163 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7164 {
7165 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7166 int vrc;
7167
7168 if (aAutostartEnabled)
7169 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7170 else
7171 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7172
7173 if (RT_SUCCESS(vrc))
7174 {
7175 hrc = mHWData.backupEx();
7176 if (SUCCEEDED(hrc))
7177 {
7178 i_setModified(IsModified_MachineData);
7179 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7180 }
7181 }
7182 else if (vrc == VERR_NOT_SUPPORTED)
7183 hrc = setError(VBOX_E_NOT_SUPPORTED,
7184 tr("The VM autostart feature is not supported on this platform"));
7185 else if (vrc == VERR_PATH_NOT_FOUND)
7186 hrc = setError(E_FAIL,
7187 tr("The path to the autostart database is not set"));
7188 else
7189 hrc = setError(E_UNEXPECTED,
7190 aAutostartEnabled ?
7191 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7192 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7193 mUserData->s.strName.c_str(), vrc);
7194 }
7195 return hrc;
7196}
7197
7198HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7199{
7200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7201
7202 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7203
7204 return S_OK;
7205}
7206
7207HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7208{
7209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7210 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7211 if (SUCCEEDED(hrc))
7212 {
7213 hrc = mHWData.backupEx();
7214 if (SUCCEEDED(hrc))
7215 {
7216 i_setModified(IsModified_MachineData);
7217 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7218 }
7219 }
7220 return hrc;
7221}
7222
7223HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7224{
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7228
7229 return S_OK;
7230}
7231
7232HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7233{
7234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7235 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7236 if ( SUCCEEDED(hrc)
7237 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7238 {
7239 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7240 int vrc;
7241
7242 if (aAutostopType != AutostopType_Disabled)
7243 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7244 else
7245 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7246
7247 if (RT_SUCCESS(vrc))
7248 {
7249 hrc = mHWData.backupEx();
7250 if (SUCCEEDED(hrc))
7251 {
7252 i_setModified(IsModified_MachineData);
7253 mHWData->mAutostart.enmAutostopType = aAutostopType;
7254 }
7255 }
7256 else if (vrc == VERR_NOT_SUPPORTED)
7257 hrc = setError(VBOX_E_NOT_SUPPORTED,
7258 tr("The VM autostop feature is not supported on this platform"));
7259 else if (vrc == VERR_PATH_NOT_FOUND)
7260 hrc = setError(E_FAIL,
7261 tr("The path to the autostart database is not set"));
7262 else
7263 hrc = setError(E_UNEXPECTED,
7264 aAutostopType != AutostopType_Disabled ?
7265 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7266 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7267 mUserData->s.strName.c_str(), vrc);
7268 }
7269 return hrc;
7270}
7271
7272HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7273{
7274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7275
7276 aDefaultFrontend = mHWData->mDefaultFrontend;
7277
7278 return S_OK;
7279}
7280
7281HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7282{
7283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7284 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7285 if (SUCCEEDED(hrc))
7286 {
7287 hrc = mHWData.backupEx();
7288 if (SUCCEEDED(hrc))
7289 {
7290 i_setModified(IsModified_MachineData);
7291 mHWData->mDefaultFrontend = aDefaultFrontend;
7292 }
7293 }
7294 return hrc;
7295}
7296
7297HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7298{
7299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7300 size_t cbIcon = mUserData->s.ovIcon.size();
7301 aIcon.resize(cbIcon);
7302 if (cbIcon)
7303 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7304 return S_OK;
7305}
7306
7307HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7308{
7309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7310 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7311 if (SUCCEEDED(hrc))
7312 {
7313 i_setModified(IsModified_MachineData);
7314 mUserData.backup();
7315 size_t cbIcon = aIcon.size();
7316 mUserData->s.ovIcon.resize(cbIcon);
7317 if (cbIcon)
7318 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7319 }
7320 return hrc;
7321}
7322
7323HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7324{
7325#ifdef VBOX_WITH_USB
7326 *aUSBProxyAvailable = true;
7327#else
7328 *aUSBProxyAvailable = false;
7329#endif
7330 return S_OK;
7331}
7332
7333HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7334{
7335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7336
7337 *aVMProcessPriority = mUserData->s.enmVMPriority;
7338
7339 return S_OK;
7340}
7341
7342HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7343{
7344 RT_NOREF(aVMProcessPriority);
7345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7346 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7347 if (SUCCEEDED(hrc))
7348 {
7349 hrc = mUserData.backupEx();
7350 if (SUCCEEDED(hrc))
7351 {
7352 i_setModified(IsModified_MachineData);
7353 mUserData->s.enmVMPriority = aVMProcessPriority;
7354 }
7355 }
7356 alock.release();
7357 if (SUCCEEDED(hrc))
7358 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7359 return hrc;
7360}
7361
7362HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7363 ComPtr<IProgress> &aProgress)
7364{
7365 ComObjPtr<Progress> pP;
7366 Progress *ppP = pP;
7367 IProgress *iP = static_cast<IProgress *>(ppP);
7368 IProgress **pProgress = &iP;
7369
7370 IMachine *pTarget = aTarget;
7371
7372 /* Convert the options. */
7373 RTCList<CloneOptions_T> optList;
7374 if (aOptions.size())
7375 for (size_t i = 0; i < aOptions.size(); ++i)
7376 optList.append(aOptions[i]);
7377
7378 if (optList.contains(CloneOptions_Link))
7379 {
7380 if (!i_isSnapshotMachine())
7381 return setError(E_INVALIDARG,
7382 tr("Linked clone can only be created from a snapshot"));
7383 if (aMode != CloneMode_MachineState)
7384 return setError(E_INVALIDARG,
7385 tr("Linked clone can only be created for a single machine state"));
7386 }
7387 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7388
7389 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7390
7391 HRESULT hrc = pWorker->start(pProgress);
7392
7393 pP = static_cast<Progress *>(*pProgress);
7394 pP.queryInterfaceTo(aProgress.asOutParam());
7395
7396 return hrc;
7397
7398}
7399
7400HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7401 const com::Utf8Str &aType,
7402 ComPtr<IProgress> &aProgress)
7403{
7404 LogFlowThisFuncEnter();
7405
7406 ComObjPtr<Progress> ptrProgress;
7407 HRESULT hrc = ptrProgress.createObject();
7408 if (SUCCEEDED(hrc))
7409 {
7410 com::Utf8Str strDefaultPath;
7411 if (aTargetPath.isEmpty())
7412 i_calculateFullPath(".", strDefaultPath);
7413
7414 /* Initialize our worker task */
7415 MachineMoveVM *pTask = NULL;
7416 try
7417 {
7418 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7419 }
7420 catch (std::bad_alloc &)
7421 {
7422 return E_OUTOFMEMORY;
7423 }
7424
7425 hrc = pTask->init();//no exceptions are thrown
7426
7427 if (SUCCEEDED(hrc))
7428 {
7429 hrc = pTask->createThread();
7430 pTask = NULL; /* Consumed by createThread(). */
7431 if (SUCCEEDED(hrc))
7432 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7433 else
7434 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7435 }
7436 else
7437 delete pTask;
7438 }
7439
7440 LogFlowThisFuncLeave();
7441 return hrc;
7442
7443}
7444
7445HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7446{
7447 NOREF(aProgress);
7448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7449
7450 // This check should always fail.
7451 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7452 if (FAILED(hrc)) return hrc;
7453
7454 AssertFailedReturn(E_NOTIMPL);
7455}
7456
7457HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7458{
7459 NOREF(aSavedStateFile);
7460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7461
7462 // This check should always fail.
7463 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7464 if (FAILED(hrc)) return hrc;
7465
7466 AssertFailedReturn(E_NOTIMPL);
7467}
7468
7469HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7470{
7471 NOREF(aFRemoveFile);
7472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7473
7474 // This check should always fail.
7475 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7476 if (FAILED(hrc)) return hrc;
7477
7478 AssertFailedReturn(E_NOTIMPL);
7479}
7480
7481// public methods for internal purposes
7482/////////////////////////////////////////////////////////////////////////////
7483
7484/**
7485 * Adds the given IsModified_* flag to the dirty flags of the machine.
7486 * This must be called either during i_loadSettings or under the machine write lock.
7487 * @param fl Flag
7488 * @param fAllowStateModification If state modifications are allowed.
7489 */
7490void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7491{
7492 mData->flModifications |= fl;
7493 if (fAllowStateModification && i_isStateModificationAllowed())
7494 mData->mCurrentStateModified = true;
7495}
7496
7497/**
7498 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7499 * care of the write locking.
7500 *
7501 * @param fModification The flag to add.
7502 * @param fAllowStateModification If state modifications are allowed.
7503 */
7504void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7505{
7506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7507 i_setModified(fModification, fAllowStateModification);
7508}
7509
7510/**
7511 * Saves the registry entry of this machine to the given configuration node.
7512 *
7513 * @param data Machine registry data.
7514 *
7515 * @note locks this object for reading.
7516 */
7517HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7518{
7519 AutoLimitedCaller autoCaller(this);
7520 AssertComRCReturnRC(autoCaller.hrc());
7521
7522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7523
7524 data.uuid = mData->mUuid;
7525 data.strSettingsFile = mData->m_strConfigFile;
7526
7527 return S_OK;
7528}
7529
7530/**
7531 * Calculates the absolute path of the given path taking the directory of the
7532 * machine settings file as the current directory.
7533 *
7534 * @param strPath Path to calculate the absolute path for.
7535 * @param aResult Where to put the result (used only on success, can be the
7536 * same Utf8Str instance as passed in @a aPath).
7537 * @return IPRT result.
7538 *
7539 * @note Locks this object for reading.
7540 */
7541int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7542{
7543 AutoCaller autoCaller(this);
7544 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
7545
7546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7547
7548 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7549
7550 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7551
7552 strSettingsDir.stripFilename();
7553 char szFolder[RTPATH_MAX];
7554 size_t cbFolder = sizeof(szFolder);
7555 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7556 if (RT_SUCCESS(vrc))
7557 aResult = szFolder;
7558
7559 return vrc;
7560}
7561
7562/**
7563 * Copies strSource to strTarget, making it relative to the machine folder
7564 * if it is a subdirectory thereof, or simply copying it otherwise.
7565 *
7566 * @param strSource Path to evaluate and copy.
7567 * @param strTarget Buffer to receive target path.
7568 *
7569 * @note Locks this object for reading.
7570 */
7571void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7572 Utf8Str &strTarget)
7573{
7574 AutoCaller autoCaller(this);
7575 AssertComRCReturn(autoCaller.hrc(), (void)0);
7576
7577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7578
7579 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7580 // use strTarget as a temporary buffer to hold the machine settings dir
7581 strTarget = mData->m_strConfigFileFull;
7582 strTarget.stripFilename();
7583 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7584 {
7585 // is relative: then append what's left
7586 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7587 // for empty paths (only possible for subdirs) use "." to avoid
7588 // triggering default settings for not present config attributes.
7589 if (strTarget.isEmpty())
7590 strTarget = ".";
7591 }
7592 else
7593 // is not relative: then overwrite
7594 strTarget = strSource;
7595}
7596
7597/**
7598 * Returns the full path to the machine's log folder in the
7599 * \a aLogFolder argument.
7600 */
7601void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7602{
7603 AutoCaller autoCaller(this);
7604 AssertComRCReturnVoid(autoCaller.hrc());
7605
7606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7607
7608 char szTmp[RTPATH_MAX];
7609 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7610 if (RT_SUCCESS(vrc))
7611 {
7612 if (szTmp[0] && !mUserData.isNull())
7613 {
7614 char szTmp2[RTPATH_MAX];
7615 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7616 if (RT_SUCCESS(vrc))
7617 aLogFolder.printf("%s%c%s",
7618 szTmp2,
7619 RTPATH_DELIMITER,
7620 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7621 }
7622 else
7623 vrc = VERR_PATH_IS_RELATIVE;
7624 }
7625
7626 if (RT_FAILURE(vrc))
7627 {
7628 // fallback if VBOX_USER_LOGHOME is not set or invalid
7629 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7630 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7631 aLogFolder.append(RTPATH_DELIMITER);
7632 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7633 }
7634}
7635
7636/**
7637 * Returns the full path to the machine's log file for an given index.
7638 */
7639Utf8Str Machine::i_getLogFilename(ULONG idx)
7640{
7641 Utf8Str logFolder;
7642 getLogFolder(logFolder);
7643 Assert(logFolder.length());
7644
7645 Utf8Str log;
7646 if (idx == 0)
7647 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7648#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7649 else if (idx == 1)
7650 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7651 else
7652 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7653#else
7654 else
7655 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7656#endif
7657 return log;
7658}
7659
7660/**
7661 * Returns the full path to the machine's hardened log file.
7662 */
7663Utf8Str Machine::i_getHardeningLogFilename(void)
7664{
7665 Utf8Str strFilename;
7666 getLogFolder(strFilename);
7667 Assert(strFilename.length());
7668 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7669 return strFilename;
7670}
7671
7672/**
7673 * Returns the default NVRAM filename based on the location of the VM config.
7674 * Note that this is a relative path.
7675 */
7676Utf8Str Machine::i_getDefaultNVRAMFilename()
7677{
7678 AutoCaller autoCaller(this);
7679 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7680
7681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7682
7683 if (i_isSnapshotMachine())
7684 return Utf8Str::Empty;
7685
7686 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7687 strNVRAMFilePath.stripPath();
7688 strNVRAMFilePath.stripSuffix();
7689 strNVRAMFilePath += ".nvram";
7690
7691 return strNVRAMFilePath;
7692}
7693
7694/**
7695 * Returns the NVRAM filename for a new snapshot. This intentionally works
7696 * similarly to the saved state file naming. Note that this is usually
7697 * a relative path, unless the snapshot folder is absolute.
7698 */
7699Utf8Str Machine::i_getSnapshotNVRAMFilename()
7700{
7701 AutoCaller autoCaller(this);
7702 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7703
7704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7705
7706 RTTIMESPEC ts;
7707 RTTimeNow(&ts);
7708 RTTIME time;
7709 RTTimeExplode(&time, &ts);
7710
7711 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7712 strNVRAMFilePath += RTPATH_DELIMITER;
7713 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7714 time.i32Year, time.u8Month, time.u8MonthDay,
7715 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7716
7717 return strNVRAMFilePath;
7718}
7719
7720/**
7721 * Returns the version of the settings file.
7722 */
7723SettingsVersion_T Machine::i_getSettingsVersion(void)
7724{
7725 AutoCaller autoCaller(this);
7726 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7727
7728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7729
7730 return mData->pMachineConfigFile->getSettingsVersion();
7731}
7732
7733/**
7734 * Composes a unique saved state filename based on the current system time. The filename is
7735 * granular to the second so this will work so long as no more than one snapshot is taken on
7736 * a machine per second.
7737 *
7738 * Before version 4.1, we used this formula for saved state files:
7739 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7740 * which no longer works because saved state files can now be shared between the saved state of the
7741 * "saved" machine and an online snapshot, and the following would cause problems:
7742 * 1) save machine
7743 * 2) create online snapshot from that machine state --> reusing saved state file
7744 * 3) save machine again --> filename would be reused, breaking the online snapshot
7745 *
7746 * So instead we now use a timestamp.
7747 *
7748 * @param strStateFilePath
7749 */
7750
7751void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7752{
7753 AutoCaller autoCaller(this);
7754 AssertComRCReturnVoid(autoCaller.hrc());
7755
7756 {
7757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7758 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7759 }
7760
7761 RTTIMESPEC ts;
7762 RTTimeNow(&ts);
7763 RTTIME time;
7764 RTTimeExplode(&time, &ts);
7765
7766 strStateFilePath += RTPATH_DELIMITER;
7767 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7768 time.i32Year, time.u8Month, time.u8MonthDay,
7769 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7770}
7771
7772/**
7773 * Returns whether at least one USB controller is present for the VM.
7774 */
7775bool Machine::i_isUSBControllerPresent()
7776{
7777 AutoCaller autoCaller(this);
7778 AssertComRCReturn(autoCaller.hrc(), false);
7779
7780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7781
7782 return (mUSBControllers->size() > 0);
7783}
7784
7785
7786/**
7787 * @note Locks this object for writing, calls the client process
7788 * (inside the lock).
7789 */
7790HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7791 const Utf8Str &strFrontend,
7792 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7793 ProgressProxy *aProgress)
7794{
7795 LogFlowThisFuncEnter();
7796
7797 AssertReturn(aControl, E_FAIL);
7798 AssertReturn(aProgress, E_FAIL);
7799 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7800
7801 AutoCaller autoCaller(this);
7802 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7803
7804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7805
7806 if (!mData->mRegistered)
7807 return setError(E_UNEXPECTED,
7808 tr("The machine '%s' is not registered"),
7809 mUserData->s.strName.c_str());
7810
7811 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7812
7813 /* The process started when launching a VM with separate UI/VM processes is always
7814 * the UI process, i.e. needs special handling as it won't claim the session. */
7815 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7816
7817 if (fSeparate)
7818 {
7819 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7820 return setError(VBOX_E_INVALID_OBJECT_STATE,
7821 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7822 mUserData->s.strName.c_str());
7823 }
7824 else
7825 {
7826 if ( mData->mSession.mState == SessionState_Locked
7827 || mData->mSession.mState == SessionState_Spawning
7828 || mData->mSession.mState == SessionState_Unlocking)
7829 return setError(VBOX_E_INVALID_OBJECT_STATE,
7830 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7831 mUserData->s.strName.c_str());
7832
7833 /* may not be busy */
7834 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7835 }
7836
7837 /* Hardening logging */
7838#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7839 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7840 {
7841 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7842 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7843 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7844 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7845 {
7846 Utf8Str strStartupLogDir = strHardeningLogFile;
7847 strStartupLogDir.stripFilename();
7848 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7849 file without stripping the file. */
7850 }
7851 strSupHardeningLogArg.append(strHardeningLogFile);
7852
7853 /* Remove legacy log filename to avoid confusion. */
7854 Utf8Str strOldStartupLogFile;
7855 getLogFolder(strOldStartupLogFile);
7856 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7857 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7858 }
7859#else
7860 Utf8Str strSupHardeningLogArg;
7861#endif
7862
7863 Utf8Str strAppOverride;
7864#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7865 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7866#endif
7867
7868 bool fUseVBoxSDS = false;
7869 Utf8Str strCanonicalName;
7870 if (false)
7871 { }
7872#ifdef VBOX_WITH_QTGUI
7873 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7874 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7875 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7876 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7877 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7878 {
7879 strCanonicalName = "GUI/Qt";
7880 fUseVBoxSDS = true;
7881 }
7882#endif
7883#ifdef VBOX_WITH_VBOXSDL
7884 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7885 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7886 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7887 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7888 {
7889 strCanonicalName = "GUI/SDL";
7890 fUseVBoxSDS = true;
7891 }
7892#endif
7893#ifdef VBOX_WITH_HEADLESS
7894 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7895 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7896 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7897 {
7898 strCanonicalName = "headless";
7899 }
7900#endif
7901 else
7902 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7903
7904 Utf8Str idStr = mData->mUuid.toString();
7905 Utf8Str const &strMachineName = mUserData->s.strName;
7906 RTPROCESS pid = NIL_RTPROCESS;
7907
7908#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7909 RT_NOREF(fUseVBoxSDS);
7910#else
7911 DWORD idCallerSession = ~(DWORD)0;
7912 if (fUseVBoxSDS)
7913 {
7914 /*
7915 * The VBoxSDS should be used for process launching the VM with
7916 * GUI only if the caller and the VBoxSDS are in different Windows
7917 * sessions and the caller in the interactive one.
7918 */
7919 fUseVBoxSDS = false;
7920
7921 /* Get windows session of the current process. The process token used
7922 due to several reasons:
7923 1. The token is absent for the current thread except someone set it
7924 for us.
7925 2. Needs to get the id of the session where the process is started.
7926 We only need to do this once, though. */
7927 static DWORD s_idCurrentSession = ~(DWORD)0;
7928 DWORD idCurrentSession = s_idCurrentSession;
7929 if (idCurrentSession == ~(DWORD)0)
7930 {
7931 HANDLE hCurrentProcessToken = NULL;
7932 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7933 {
7934 DWORD cbIgn = 0;
7935 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7936 s_idCurrentSession = idCurrentSession;
7937 else
7938 {
7939 idCurrentSession = ~(DWORD)0;
7940 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7941 }
7942 CloseHandle(hCurrentProcessToken);
7943 }
7944 else
7945 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7946 }
7947
7948 /* get the caller's session */
7949 HRESULT hrc = CoImpersonateClient();
7950 if (SUCCEEDED(hrc))
7951 {
7952 HANDLE hCallerThreadToken;
7953 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7954 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7955 &hCallerThreadToken))
7956 {
7957 SetLastError(NO_ERROR);
7958 DWORD cbIgn = 0;
7959 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7960 {
7961 /* Only need to use SDS if the session ID differs: */
7962 if (idCurrentSession != idCallerSession)
7963 {
7964 fUseVBoxSDS = false;
7965
7966 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7967 DWORD cbTokenGroups = 0;
7968 PTOKEN_GROUPS pTokenGroups = NULL;
7969 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7970 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7971 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7972 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7973 {
7974 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7975 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7976 PSID pInteractiveSid = NULL;
7977 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7978 {
7979 /* Iterate over the groups looking for the interactive SID: */
7980 fUseVBoxSDS = false;
7981 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7982 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7983 {
7984 fUseVBoxSDS = true;
7985 break;
7986 }
7987 FreeSid(pInteractiveSid);
7988 }
7989 }
7990 else
7991 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7992 RTMemTmpFree(pTokenGroups);
7993 }
7994 }
7995 else
7996 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7997 CloseHandle(hCallerThreadToken);
7998 }
7999 else
8000 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8001 CoRevertToSelf();
8002 }
8003 else
8004 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8005 }
8006 if (fUseVBoxSDS)
8007 {
8008 /* connect to VBoxSDS */
8009 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8010 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8011 if (FAILED(hrc))
8012 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8013 strMachineName.c_str());
8014
8015 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8016 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8017 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8018 service to access the files. */
8019 hrc = CoSetProxyBlanket(pVBoxSDS,
8020 RPC_C_AUTHN_DEFAULT,
8021 RPC_C_AUTHZ_DEFAULT,
8022 COLE_DEFAULT_PRINCIPAL,
8023 RPC_C_AUTHN_LEVEL_DEFAULT,
8024 RPC_C_IMP_LEVEL_IMPERSONATE,
8025 NULL,
8026 EOAC_DEFAULT);
8027 if (FAILED(hrc))
8028 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8029
8030 size_t const cEnvVars = aEnvironmentChanges.size();
8031 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8032 for (size_t i = 0; i < cEnvVars; i++)
8033 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8034
8035 ULONG uPid = 0;
8036 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8037 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8038 idCallerSession, &uPid);
8039 if (FAILED(hrc))
8040 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8041 pid = (RTPROCESS)uPid;
8042 }
8043 else
8044#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8045 {
8046 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8047 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8048 if (RT_FAILURE(vrc))
8049 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8050 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8051 }
8052
8053 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8054 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8055
8056 if (!fSeparate)
8057 {
8058 /*
8059 * Note that we don't release the lock here before calling the client,
8060 * because it doesn't need to call us back if called with a NULL argument.
8061 * Releasing the lock here is dangerous because we didn't prepare the
8062 * launch data yet, but the client we've just started may happen to be
8063 * too fast and call LockMachine() that will fail (because of PID, etc.),
8064 * so that the Machine will never get out of the Spawning session state.
8065 */
8066
8067 /* inform the session that it will be a remote one */
8068 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8069#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8070 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8071#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8072 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8073#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8074 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
8075
8076 if (FAILED(hrc))
8077 {
8078 /* restore the session state */
8079 mData->mSession.mState = SessionState_Unlocked;
8080 alock.release();
8081 mParent->i_addProcessToReap(pid);
8082 /* The failure may occur w/o any error info (from RPC), so provide one */
8083 return setError(VBOX_E_VM_ERROR,
8084 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
8085 }
8086
8087 /* attach launch data to the machine */
8088 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8089 mData->mSession.mRemoteControls.push_back(aControl);
8090 mData->mSession.mProgress = aProgress;
8091 mData->mSession.mPID = pid;
8092 mData->mSession.mState = SessionState_Spawning;
8093 Assert(strCanonicalName.isNotEmpty());
8094 mData->mSession.mName = strCanonicalName;
8095 }
8096 else
8097 {
8098 /* For separate UI process we declare the launch as completed instantly, as the
8099 * actual headless VM start may or may not come. No point in remembering anything
8100 * yet, as what matters for us is when the headless VM gets started. */
8101 aProgress->i_notifyComplete(S_OK);
8102 }
8103
8104 alock.release();
8105 mParent->i_addProcessToReap(pid);
8106
8107 LogFlowThisFuncLeave();
8108 return S_OK;
8109}
8110
8111/**
8112 * Returns @c true if the given session machine instance has an open direct
8113 * session (and optionally also for direct sessions which are closing) and
8114 * returns the session control machine instance if so.
8115 *
8116 * Note that when the method returns @c false, the arguments remain unchanged.
8117 *
8118 * @param aMachine Session machine object.
8119 * @param aControl Direct session control object (optional).
8120 * @param aRequireVM If true then only allow VM sessions.
8121 * @param aAllowClosing If true then additionally a session which is currently
8122 * being closed will also be allowed.
8123 *
8124 * @note locks this object for reading.
8125 */
8126bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8127 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8128 bool aRequireVM /*= false*/,
8129 bool aAllowClosing /*= false*/)
8130{
8131 AutoLimitedCaller autoCaller(this);
8132 AssertComRCReturn(autoCaller.hrc(), false);
8133
8134 /* just return false for inaccessible machines */
8135 if (getObjectState().getState() != ObjectState::Ready)
8136 return false;
8137
8138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8139
8140 if ( ( mData->mSession.mState == SessionState_Locked
8141 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8142 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8143 )
8144 {
8145 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8146
8147 aMachine = mData->mSession.mMachine;
8148
8149 if (aControl != NULL)
8150 *aControl = mData->mSession.mDirectControl;
8151
8152 return true;
8153 }
8154
8155 return false;
8156}
8157
8158/**
8159 * Returns @c true if the given machine has an spawning direct session.
8160 *
8161 * @note locks this object for reading.
8162 */
8163bool Machine::i_isSessionSpawning()
8164{
8165 AutoLimitedCaller autoCaller(this);
8166 AssertComRCReturn(autoCaller.hrc(), 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_Spawning)
8175 return true;
8176
8177 return false;
8178}
8179
8180/**
8181 * Called from the client watcher thread to check for unexpected client process
8182 * death during Session_Spawning state (e.g. before it successfully opened a
8183 * direct session).
8184 *
8185 * On Win32 and on OS/2, this method is called only when we've got the
8186 * direct client's process termination notification, so it always returns @c
8187 * true.
8188 *
8189 * On other platforms, this method returns @c true if the client process is
8190 * terminated and @c false if it's still alive.
8191 *
8192 * @note Locks this object for writing.
8193 */
8194bool Machine::i_checkForSpawnFailure()
8195{
8196 AutoCaller autoCaller(this);
8197 if (!autoCaller.isOk())
8198 {
8199 /* nothing to do */
8200 LogFlowThisFunc(("Already uninitialized!\n"));
8201 return true;
8202 }
8203
8204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8205
8206 if (mData->mSession.mState != SessionState_Spawning)
8207 {
8208 /* nothing to do */
8209 LogFlowThisFunc(("Not spawning any more!\n"));
8210 return true;
8211 }
8212
8213 /* PID not yet initialized, skip check. */
8214 if (mData->mSession.mPID == NIL_RTPROCESS)
8215 return false;
8216
8217 HRESULT hrc = S_OK;
8218 RTPROCSTATUS status;
8219 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8220 if (vrc != VERR_PROCESS_RUNNING)
8221 {
8222 Utf8Str strExtraInfo;
8223
8224#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8225 /* If the startup logfile exists and is of non-zero length, tell the
8226 user to look there for more details to encourage them to attach it
8227 when reporting startup issues. */
8228 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8229 uint64_t cbStartupLogFile = 0;
8230 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8231 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8232 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8233#endif
8234
8235 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8236 hrc = setError(E_FAIL,
8237 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8238 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8239 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8240 hrc = setError(E_FAIL,
8241 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8242 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8243 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8244 hrc = setError(E_FAIL,
8245 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8246 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8247 else
8248 hrc = setErrorBoth(E_FAIL, vrc,
8249 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8250 i_getName().c_str(), vrc, strExtraInfo.c_str());
8251 }
8252
8253 if (FAILED(hrc))
8254 {
8255 /* Close the remote session, remove the remote control from the list
8256 * and reset session state to Closed (@note keep the code in sync with
8257 * the relevant part in LockMachine()). */
8258
8259 Assert(mData->mSession.mRemoteControls.size() == 1);
8260 if (mData->mSession.mRemoteControls.size() == 1)
8261 {
8262 ErrorInfoKeeper eik;
8263 mData->mSession.mRemoteControls.front()->Uninitialize();
8264 }
8265
8266 mData->mSession.mRemoteControls.clear();
8267 mData->mSession.mState = SessionState_Unlocked;
8268
8269 /* finalize the progress after setting the state */
8270 if (!mData->mSession.mProgress.isNull())
8271 {
8272 mData->mSession.mProgress->notifyComplete(hrc);
8273 mData->mSession.mProgress.setNull();
8274 }
8275
8276 mData->mSession.mPID = NIL_RTPROCESS;
8277
8278 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8279 return true;
8280 }
8281
8282 return false;
8283}
8284
8285/**
8286 * Checks whether the machine can be registered. If so, commits and saves
8287 * all settings.
8288 *
8289 * @note Must be called from mParent's write lock. Locks this object and
8290 * children for writing.
8291 */
8292HRESULT Machine::i_prepareRegister()
8293{
8294 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8295
8296 AutoLimitedCaller autoCaller(this);
8297 AssertComRCReturnRC(autoCaller.hrc());
8298
8299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8300
8301 /* wait for state dependents to drop to zero */
8302 i_ensureNoStateDependencies(alock);
8303
8304 if (!mData->mAccessible)
8305 return setError(VBOX_E_INVALID_OBJECT_STATE,
8306 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8307 mUserData->s.strName.c_str(),
8308 mData->mUuid.toString().c_str());
8309
8310 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8311
8312 if (mData->mRegistered)
8313 return setError(VBOX_E_INVALID_OBJECT_STATE,
8314 tr("The machine '%s' with UUID {%s} is already registered"),
8315 mUserData->s.strName.c_str(),
8316 mData->mUuid.toString().c_str());
8317
8318 HRESULT hrc = S_OK;
8319
8320 // Ensure the settings are saved. If we are going to be registered and
8321 // no config file exists yet, create it by calling i_saveSettings() too.
8322 if ( (mData->flModifications)
8323 || (!mData->pMachineConfigFile->fileExists())
8324 )
8325 {
8326 hrc = i_saveSettings(NULL, alock);
8327 // no need to check whether VirtualBox.xml needs saving too since
8328 // we can't have a machine XML file rename pending
8329 if (FAILED(hrc)) return hrc;
8330 }
8331
8332 /* more config checking goes here */
8333
8334 if (SUCCEEDED(hrc))
8335 {
8336 /* we may have had implicit modifications we want to fix on success */
8337 i_commit();
8338
8339 mData->mRegistered = true;
8340 }
8341 else
8342 {
8343 /* we may have had implicit modifications we want to cancel on failure*/
8344 i_rollback(false /* aNotify */);
8345 }
8346
8347 return hrc;
8348}
8349
8350/**
8351 * Increases the number of objects dependent on the machine state or on the
8352 * registered state. Guarantees that these two states will not change at least
8353 * until #i_releaseStateDependency() is called.
8354 *
8355 * Depending on the @a aDepType value, additional state checks may be made.
8356 * These checks will set extended error info on failure. See
8357 * #i_checkStateDependency() for more info.
8358 *
8359 * If this method returns a failure, the dependency is not added and the caller
8360 * is not allowed to rely on any particular machine state or registration state
8361 * value and may return the failed result code to the upper level.
8362 *
8363 * @param aDepType Dependency type to add.
8364 * @param aState Current machine state (NULL if not interested).
8365 * @param aRegistered Current registered state (NULL if not interested).
8366 *
8367 * @note Locks this object for writing.
8368 */
8369HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8370 MachineState_T *aState /* = NULL */,
8371 BOOL *aRegistered /* = NULL */)
8372{
8373 AutoCaller autoCaller(this);
8374 AssertComRCReturnRC(autoCaller.hrc());
8375
8376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8377
8378 HRESULT hrc = i_checkStateDependency(aDepType);
8379 if (FAILED(hrc)) return hrc;
8380
8381 {
8382 if (mData->mMachineStateChangePending != 0)
8383 {
8384 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8385 * drop to zero so don't add more. It may make sense to wait a bit
8386 * and retry before reporting an error (since the pending state
8387 * transition should be really quick) but let's just assert for
8388 * now to see if it ever happens on practice. */
8389
8390 AssertFailed();
8391
8392 return setError(E_ACCESSDENIED,
8393 tr("Machine state change is in progress. Please retry the operation later."));
8394 }
8395
8396 ++mData->mMachineStateDeps;
8397 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8398 }
8399
8400 if (aState)
8401 *aState = mData->mMachineState;
8402 if (aRegistered)
8403 *aRegistered = mData->mRegistered;
8404
8405 return S_OK;
8406}
8407
8408/**
8409 * Decreases the number of objects dependent on the machine state.
8410 * Must always complete the #i_addStateDependency() call after the state
8411 * dependency is no more necessary.
8412 */
8413void Machine::i_releaseStateDependency()
8414{
8415 AutoCaller autoCaller(this);
8416 AssertComRCReturnVoid(autoCaller.hrc());
8417
8418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8419
8420 /* releaseStateDependency() w/o addStateDependency()? */
8421 AssertReturnVoid(mData->mMachineStateDeps != 0);
8422 -- mData->mMachineStateDeps;
8423
8424 if (mData->mMachineStateDeps == 0)
8425 {
8426 /* inform i_ensureNoStateDependencies() that there are no more deps */
8427 if (mData->mMachineStateChangePending != 0)
8428 {
8429 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8430 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8431 }
8432 }
8433}
8434
8435Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8436{
8437 /* start with nothing found */
8438 Utf8Str strResult("");
8439
8440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8441
8442 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8443 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8444 // found:
8445 strResult = it->second; // source is a Utf8Str
8446
8447 return strResult;
8448}
8449
8450// protected methods
8451/////////////////////////////////////////////////////////////////////////////
8452
8453/**
8454 * Performs machine state checks based on the @a aDepType value. If a check
8455 * fails, this method will set extended error info, otherwise it will return
8456 * S_OK. It is supposed, that on failure, the caller will immediately return
8457 * the return value of this method to the upper level.
8458 *
8459 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8460 *
8461 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8462 * current state of this machine object allows to change settings of the
8463 * machine (i.e. the machine is not registered, or registered but not running
8464 * and not saved). It is useful to call this method from Machine setters
8465 * before performing any change.
8466 *
8467 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8468 * as for MutableStateDep except that if the machine is saved, S_OK is also
8469 * returned. This is useful in setters which allow changing machine
8470 * properties when it is in the saved state.
8471 *
8472 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8473 * if the current state of this machine object allows to change runtime
8474 * changeable settings of the machine (i.e. the machine is not registered, or
8475 * registered but either running or not running and not saved). It is useful
8476 * to call this method from Machine setters before performing any changes to
8477 * runtime changeable settings.
8478 *
8479 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8480 * the same as for MutableOrRunningStateDep except that if the machine is
8481 * saved, S_OK is also returned. This is useful in setters which allow
8482 * changing runtime and saved state changeable machine properties.
8483 *
8484 * @param aDepType Dependency type to check.
8485 *
8486 * @note Non Machine based classes should use #i_addStateDependency() and
8487 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8488 * template.
8489 *
8490 * @note This method must be called from under this object's read or write
8491 * lock.
8492 */
8493HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8494{
8495 switch (aDepType)
8496 {
8497 case AnyStateDep:
8498 {
8499 break;
8500 }
8501 case MutableStateDep:
8502 {
8503 if ( mData->mRegistered
8504 && ( !i_isSessionMachine()
8505 || ( mData->mMachineState != MachineState_Aborted
8506 && mData->mMachineState != MachineState_Teleported
8507 && mData->mMachineState != MachineState_PoweredOff
8508 )
8509 )
8510 )
8511 return setError(VBOX_E_INVALID_VM_STATE,
8512 tr("The machine is not mutable (state is %s)"),
8513 Global::stringifyMachineState(mData->mMachineState));
8514 break;
8515 }
8516 case MutableOrSavedStateDep:
8517 {
8518 if ( mData->mRegistered
8519 && ( !i_isSessionMachine()
8520 || ( mData->mMachineState != MachineState_Aborted
8521 && mData->mMachineState != MachineState_Teleported
8522 && mData->mMachineState != MachineState_Saved
8523 && mData->mMachineState != MachineState_AbortedSaved
8524 && mData->mMachineState != MachineState_PoweredOff
8525 )
8526 )
8527 )
8528 return setError(VBOX_E_INVALID_VM_STATE,
8529 tr("The machine is not mutable or saved (state is %s)"),
8530 Global::stringifyMachineState(mData->mMachineState));
8531 break;
8532 }
8533 case MutableOrRunningStateDep:
8534 {
8535 if ( mData->mRegistered
8536 && ( !i_isSessionMachine()
8537 || ( mData->mMachineState != MachineState_Aborted
8538 && mData->mMachineState != MachineState_Teleported
8539 && mData->mMachineState != MachineState_PoweredOff
8540 && !Global::IsOnline(mData->mMachineState)
8541 )
8542 )
8543 )
8544 return setError(VBOX_E_INVALID_VM_STATE,
8545 tr("The machine is not mutable or running (state is %s)"),
8546 Global::stringifyMachineState(mData->mMachineState));
8547 break;
8548 }
8549 case MutableOrSavedOrRunningStateDep:
8550 {
8551 if ( mData->mRegistered
8552 && ( !i_isSessionMachine()
8553 || ( mData->mMachineState != MachineState_Aborted
8554 && mData->mMachineState != MachineState_Teleported
8555 && mData->mMachineState != MachineState_Saved
8556 && mData->mMachineState != MachineState_AbortedSaved
8557 && mData->mMachineState != MachineState_PoweredOff
8558 && !Global::IsOnline(mData->mMachineState)
8559 )
8560 )
8561 )
8562 return setError(VBOX_E_INVALID_VM_STATE,
8563 tr("The machine is not mutable, saved or running (state is %s)"),
8564 Global::stringifyMachineState(mData->mMachineState));
8565 break;
8566 }
8567 }
8568
8569 return S_OK;
8570}
8571
8572/**
8573 * Helper to initialize all associated child objects and allocate data
8574 * structures.
8575 *
8576 * This method must be called as a part of the object's initialization procedure
8577 * (usually done in the #init() method).
8578 *
8579 * @note Must be called only from #init() or from #i_registeredInit().
8580 */
8581HRESULT Machine::initDataAndChildObjects()
8582{
8583 AutoCaller autoCaller(this);
8584 AssertComRCReturnRC(autoCaller.hrc());
8585 AssertReturn( getObjectState().getState() == ObjectState::InInit
8586 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8587
8588 AssertReturn(!mData->mAccessible, E_FAIL);
8589
8590 /* allocate data structures */
8591 mSSData.allocate();
8592 mUserData.allocate();
8593 mHWData.allocate();
8594 mMediumAttachments.allocate();
8595 mStorageControllers.allocate();
8596 mUSBControllers.allocate();
8597
8598 /* initialize mOSTypeId */
8599 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8600
8601/** @todo r=bird: init() methods never fails, right? Why don't we make them
8602 * return void then! */
8603
8604 /* create associated BIOS settings object */
8605 unconst(mBIOSSettings).createObject();
8606 mBIOSSettings->init(this);
8607
8608 /* create associated recording settings object */
8609 unconst(mRecordingSettings).createObject();
8610 mRecordingSettings->init(this);
8611
8612 /* create associated trusted platform module object */
8613 unconst(mTrustedPlatformModule).createObject();
8614 mTrustedPlatformModule->init(this);
8615
8616 /* create associated NVRAM store object */
8617 unconst(mNvramStore).createObject();
8618 mNvramStore->init(this);
8619
8620 /* create the graphics adapter object (always present) */
8621 unconst(mGraphicsAdapter).createObject();
8622 mGraphicsAdapter->init(this);
8623
8624 /* create an associated VRDE object (default is disabled) */
8625 unconst(mVRDEServer).createObject();
8626 mVRDEServer->init(this);
8627
8628 /* create associated serial port objects */
8629 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8630 {
8631 unconst(mSerialPorts[slot]).createObject();
8632 mSerialPorts[slot]->init(this, slot);
8633 }
8634
8635 /* create associated parallel port objects */
8636 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8637 {
8638 unconst(mParallelPorts[slot]).createObject();
8639 mParallelPorts[slot]->init(this, slot);
8640 }
8641
8642 /* create the audio settings object */
8643 unconst(mAudioSettings).createObject();
8644 mAudioSettings->init(this);
8645
8646 /* create the USB device filters object (always present) */
8647 unconst(mUSBDeviceFilters).createObject();
8648 mUSBDeviceFilters->init(this);
8649
8650 /* create associated network adapter objects */
8651 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8652 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8653 {
8654 unconst(mNetworkAdapters[slot]).createObject();
8655 mNetworkAdapters[slot]->init(this, slot);
8656 }
8657
8658 /* create the bandwidth control */
8659 unconst(mBandwidthControl).createObject();
8660 mBandwidthControl->init(this);
8661
8662 /* create the guest debug control object */
8663 unconst(mGuestDebugControl).createObject();
8664 mGuestDebugControl->init(this);
8665
8666 return S_OK;
8667}
8668
8669/**
8670 * Helper to uninitialize all associated child objects and to free all data
8671 * structures.
8672 *
8673 * This method must be called as a part of the object's uninitialization
8674 * procedure (usually done in the #uninit() method).
8675 *
8676 * @note Must be called only from #uninit() or from #i_registeredInit().
8677 */
8678void Machine::uninitDataAndChildObjects()
8679{
8680 AutoCaller autoCaller(this);
8681 AssertComRCReturnVoid(autoCaller.hrc());
8682 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8683 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8684 || getObjectState().getState() == ObjectState::InUninit
8685 || getObjectState().getState() == ObjectState::Limited);
8686
8687 /* tell all our other child objects we've been uninitialized */
8688 if (mGuestDebugControl)
8689 {
8690 mGuestDebugControl->uninit();
8691 unconst(mGuestDebugControl).setNull();
8692 }
8693
8694 if (mBandwidthControl)
8695 {
8696 mBandwidthControl->uninit();
8697 unconst(mBandwidthControl).setNull();
8698 }
8699
8700 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8701 {
8702 if (mNetworkAdapters[slot])
8703 {
8704 mNetworkAdapters[slot]->uninit();
8705 unconst(mNetworkAdapters[slot]).setNull();
8706 }
8707 }
8708
8709 if (mUSBDeviceFilters)
8710 {
8711 mUSBDeviceFilters->uninit();
8712 unconst(mUSBDeviceFilters).setNull();
8713 }
8714
8715 if (mAudioSettings)
8716 {
8717 mAudioSettings->uninit();
8718 unconst(mAudioSettings).setNull();
8719 }
8720
8721 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8722 {
8723 if (mParallelPorts[slot])
8724 {
8725 mParallelPorts[slot]->uninit();
8726 unconst(mParallelPorts[slot]).setNull();
8727 }
8728 }
8729
8730 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8731 {
8732 if (mSerialPorts[slot])
8733 {
8734 mSerialPorts[slot]->uninit();
8735 unconst(mSerialPorts[slot]).setNull();
8736 }
8737 }
8738
8739 if (mVRDEServer)
8740 {
8741 mVRDEServer->uninit();
8742 unconst(mVRDEServer).setNull();
8743 }
8744
8745 if (mGraphicsAdapter)
8746 {
8747 mGraphicsAdapter->uninit();
8748 unconst(mGraphicsAdapter).setNull();
8749 }
8750
8751 if (mBIOSSettings)
8752 {
8753 mBIOSSettings->uninit();
8754 unconst(mBIOSSettings).setNull();
8755 }
8756
8757 if (mRecordingSettings)
8758 {
8759 mRecordingSettings->uninit();
8760 unconst(mRecordingSettings).setNull();
8761 }
8762
8763 if (mTrustedPlatformModule)
8764 {
8765 mTrustedPlatformModule->uninit();
8766 unconst(mTrustedPlatformModule).setNull();
8767 }
8768
8769 if (mNvramStore)
8770 {
8771 mNvramStore->uninit();
8772 unconst(mNvramStore).setNull();
8773 }
8774
8775 /* Deassociate media (only when a real Machine or a SnapshotMachine
8776 * instance is uninitialized; SessionMachine instances refer to real
8777 * Machine media). This is necessary for a clean re-initialization of
8778 * the VM after successfully re-checking the accessibility state. Note
8779 * that in case of normal Machine or SnapshotMachine uninitialization (as
8780 * a result of unregistering or deleting the snapshot), outdated media
8781 * attachments will already be uninitialized and deleted, so this
8782 * code will not affect them. */
8783 if ( !mMediumAttachments.isNull()
8784 && !i_isSessionMachine()
8785 )
8786 {
8787 for (MediumAttachmentList::const_iterator
8788 it = mMediumAttachments->begin();
8789 it != mMediumAttachments->end();
8790 ++it)
8791 {
8792 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8793 if (pMedium.isNull())
8794 continue;
8795 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8796 AssertComRC(hrc);
8797 }
8798 }
8799
8800 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8801 {
8802 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8803 if (mData->mFirstSnapshot)
8804 {
8805 // Snapshots tree is protected by machine write lock.
8806 // Otherwise we assert in Snapshot::uninit()
8807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8808 mData->mFirstSnapshot->uninit();
8809 mData->mFirstSnapshot.setNull();
8810 }
8811
8812 mData->mCurrentSnapshot.setNull();
8813 }
8814
8815 /* free data structures (the essential mData structure is not freed here
8816 * since it may be still in use) */
8817 mMediumAttachments.free();
8818 mStorageControllers.free();
8819 mUSBControllers.free();
8820 mHWData.free();
8821 mUserData.free();
8822 mSSData.free();
8823}
8824
8825/**
8826 * Returns a pointer to the Machine object for this machine that acts like a
8827 * parent for complex machine data objects such as shared folders, etc.
8828 *
8829 * For primary Machine objects and for SnapshotMachine objects, returns this
8830 * object's pointer itself. For SessionMachine objects, returns the peer
8831 * (primary) machine pointer.
8832 */
8833Machine *Machine::i_getMachine()
8834{
8835 if (i_isSessionMachine())
8836 return (Machine*)mPeer;
8837 return this;
8838}
8839
8840/**
8841 * Makes sure that there are no machine state dependents. If necessary, waits
8842 * for the number of dependents to drop to zero.
8843 *
8844 * Make sure this method is called from under this object's write lock to
8845 * guarantee that no new dependents may be added when this method returns
8846 * control to the caller.
8847 *
8848 * @note Receives a lock to this object for writing. The lock will be released
8849 * while waiting (if necessary).
8850 *
8851 * @warning To be used only in methods that change the machine state!
8852 */
8853void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8854{
8855 AssertReturnVoid(isWriteLockOnCurrentThread());
8856
8857 /* Wait for all state dependents if necessary */
8858 if (mData->mMachineStateDeps != 0)
8859 {
8860 /* lazy semaphore creation */
8861 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8862 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8863
8864 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8865 mData->mMachineStateDeps));
8866
8867 ++mData->mMachineStateChangePending;
8868
8869 /* reset the semaphore before waiting, the last dependent will signal
8870 * it */
8871 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8872
8873 alock.release();
8874
8875 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8876
8877 alock.acquire();
8878
8879 -- mData->mMachineStateChangePending;
8880 }
8881}
8882
8883/**
8884 * Changes the machine state and informs callbacks.
8885 *
8886 * This method is not intended to fail so it either returns S_OK or asserts (and
8887 * returns a failure).
8888 *
8889 * @note Locks this object for writing.
8890 */
8891HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8892{
8893 LogFlowThisFuncEnter();
8894 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8895 Assert(aMachineState != MachineState_Null);
8896
8897 AutoCaller autoCaller(this);
8898 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8899
8900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8901
8902 /* wait for state dependents to drop to zero */
8903 i_ensureNoStateDependencies(alock);
8904
8905 MachineState_T const enmOldState = mData->mMachineState;
8906 if (enmOldState != aMachineState)
8907 {
8908 mData->mMachineState = aMachineState;
8909 RTTimeNow(&mData->mLastStateChange);
8910
8911#ifdef VBOX_WITH_DTRACE_R3_MAIN
8912 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8913#endif
8914 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8915 }
8916
8917 LogFlowThisFuncLeave();
8918 return S_OK;
8919}
8920
8921/**
8922 * Searches for a shared folder with the given logical name
8923 * in the collection of shared folders.
8924 *
8925 * @param aName logical name of the shared folder
8926 * @param aSharedFolder where to return the found object
8927 * @param aSetError whether to set the error info if the folder is
8928 * not found
8929 * @return
8930 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8931 *
8932 * @note
8933 * must be called from under the object's lock!
8934 */
8935HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8936 ComObjPtr<SharedFolder> &aSharedFolder,
8937 bool aSetError /* = false */)
8938{
8939 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8940 for (HWData::SharedFolderList::const_iterator
8941 it = mHWData->mSharedFolders.begin();
8942 it != mHWData->mSharedFolders.end();
8943 ++it)
8944 {
8945 SharedFolder *pSF = *it;
8946 AutoCaller autoCaller(pSF);
8947 if (pSF->i_getName() == aName)
8948 {
8949 aSharedFolder = pSF;
8950 hrc = S_OK;
8951 break;
8952 }
8953 }
8954
8955 if (aSetError && FAILED(hrc))
8956 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8957
8958 return hrc;
8959}
8960
8961/**
8962 * Initializes all machine instance data from the given settings structures
8963 * from XML. The exception is the machine UUID which needs special handling
8964 * depending on the caller's use case, so the caller needs to set that herself.
8965 *
8966 * This gets called in several contexts during machine initialization:
8967 *
8968 * -- When machine XML exists on disk already and needs to be loaded into memory,
8969 * for example, from #i_registeredInit() to load all registered machines on
8970 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8971 * attached to the machine should be part of some media registry already.
8972 *
8973 * -- During OVF import, when a machine config has been constructed from an
8974 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8975 * ensure that the media listed as attachments in the config (which have
8976 * been imported from the OVF) receive the correct registry ID.
8977 *
8978 * -- During VM cloning.
8979 *
8980 * @param config Machine settings from XML.
8981 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8982 * for each attached medium in the config.
8983 * @return
8984 */
8985HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8986 const Guid *puuidRegistry)
8987{
8988 // copy name, description, OS type, teleporter, UTC etc.
8989 mUserData->s = config.machineUserData;
8990
8991 // look up the object by Id to check it is valid
8992 ComObjPtr<GuestOSType> pGuestOSType;
8993 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8994 if (!pGuestOSType.isNull())
8995 mUserData->s.strOsType = pGuestOSType->i_id();
8996
8997#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8998 // stateFile encryption (optional)
8999 mSSData->strStateKeyId = config.strStateKeyId;
9000 mSSData->strStateKeyStore = config.strStateKeyStore;
9001 mData->mstrLogKeyId = config.strLogKeyId;
9002 mData->mstrLogKeyStore = config.strLogKeyStore;
9003#endif
9004
9005 // stateFile (optional)
9006 if (config.strStateFile.isEmpty())
9007 mSSData->strStateFilePath.setNull();
9008 else
9009 {
9010 Utf8Str stateFilePathFull(config.strStateFile);
9011 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9012 if (RT_FAILURE(vrc))
9013 return setErrorBoth(E_FAIL, vrc,
9014 tr("Invalid saved state file path '%s' (%Rrc)"),
9015 config.strStateFile.c_str(),
9016 vrc);
9017 mSSData->strStateFilePath = stateFilePathFull;
9018 }
9019
9020 // snapshot folder needs special processing so set it again
9021 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9022 if (FAILED(hrc)) return hrc;
9023
9024 /* Copy the extra data items (config may or may not be the same as
9025 * mData->pMachineConfigFile) if necessary. When loading the XML files
9026 * from disk they are the same, but not for OVF import. */
9027 if (mData->pMachineConfigFile != &config)
9028 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9029
9030 /* currentStateModified (optional, default is true) */
9031 mData->mCurrentStateModified = config.fCurrentStateModified;
9032
9033 mData->mLastStateChange = config.timeLastStateChange;
9034
9035 /*
9036 * note: all mUserData members must be assigned prior this point because
9037 * we need to commit changes in order to let mUserData be shared by all
9038 * snapshot machine instances.
9039 */
9040 mUserData.commitCopy();
9041
9042 // machine registry, if present (must be loaded before snapshots)
9043 if (config.canHaveOwnMediaRegistry())
9044 {
9045 // determine machine folder
9046 Utf8Str strMachineFolder = i_getSettingsFileFull();
9047 strMachineFolder.stripFilename();
9048 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9049 config.mediaRegistry,
9050 strMachineFolder);
9051 if (FAILED(hrc)) return hrc;
9052 }
9053
9054 /* Snapshot node (optional) */
9055 size_t cRootSnapshots;
9056 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9057 {
9058 // there must be only one root snapshot
9059 Assert(cRootSnapshots == 1);
9060 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9061
9062 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
9063 if (FAILED(hrc)) return hrc;
9064 }
9065
9066 // hardware data
9067 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9068 config.recordingSettings);
9069 if (FAILED(hrc)) return hrc;
9070
9071 /*
9072 * NOTE: the assignment below must be the last thing to do,
9073 * otherwise it will be not possible to change the settings
9074 * somewhere in the code above because all setters will be
9075 * blocked by i_checkStateDependency(MutableStateDep).
9076 */
9077
9078 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9079 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9080 {
9081 /* no need to use i_setMachineState() during init() */
9082 mData->mMachineState = MachineState_AbortedSaved;
9083 }
9084 else if (config.fAborted)
9085 {
9086 mSSData->strStateFilePath.setNull();
9087
9088 /* no need to use i_setMachineState() during init() */
9089 mData->mMachineState = MachineState_Aborted;
9090 }
9091 else if (!mSSData->strStateFilePath.isEmpty())
9092 {
9093 /* no need to use i_setMachineState() during init() */
9094 mData->mMachineState = MachineState_Saved;
9095 }
9096
9097 // after loading settings, we are no longer different from the XML on disk
9098 mData->flModifications = 0;
9099
9100 return S_OK;
9101}
9102
9103/**
9104 * Loads all snapshots starting from the given settings.
9105 *
9106 * @param data snapshot settings.
9107 * @param aCurSnapshotId Current snapshot ID from the settings file.
9108 */
9109HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9110 const Guid &aCurSnapshotId)
9111{
9112 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9113 AssertReturn(!i_isSessionMachine(), E_FAIL);
9114
9115 HRESULT hrc = S_OK;
9116
9117 std::list<const settings::Snapshot *> llSettingsTodo;
9118 llSettingsTodo.push_back(&data);
9119 std::list<Snapshot *> llParentsTodo;
9120 llParentsTodo.push_back(NULL);
9121
9122 while (llSettingsTodo.size() > 0)
9123 {
9124 const settings::Snapshot *current = llSettingsTodo.front();
9125 llSettingsTodo.pop_front();
9126 Snapshot *pParent = llParentsTodo.front();
9127 llParentsTodo.pop_front();
9128
9129 Utf8Str strStateFile;
9130 if (!current->strStateFile.isEmpty())
9131 {
9132 /* optional */
9133 strStateFile = current->strStateFile;
9134 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9135 if (RT_FAILURE(vrc))
9136 {
9137 setErrorBoth(E_FAIL, vrc,
9138 tr("Invalid saved state file path '%s' (%Rrc)"),
9139 strStateFile.c_str(), vrc);
9140 }
9141 }
9142
9143 /* create a snapshot machine object */
9144 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9145 pSnapshotMachine.createObject();
9146 hrc = pSnapshotMachine->initFromSettings(this,
9147 current->hardware,
9148 &current->debugging,
9149 &current->autostart,
9150 current->recordingSettings,
9151 current->uuid.ref(),
9152 strStateFile);
9153 if (FAILED(hrc)) break;
9154
9155 /* create a snapshot object */
9156 ComObjPtr<Snapshot> pSnapshot;
9157 pSnapshot.createObject();
9158 /* initialize the snapshot */
9159 hrc = pSnapshot->init(mParent, // VirtualBox object
9160 current->uuid,
9161 current->strName,
9162 current->strDescription,
9163 current->timestamp,
9164 pSnapshotMachine,
9165 pParent);
9166 if (FAILED(hrc)) break;
9167
9168 /* memorize the first snapshot if necessary */
9169 if (!mData->mFirstSnapshot)
9170 {
9171 Assert(pParent == NULL);
9172 mData->mFirstSnapshot = pSnapshot;
9173 }
9174
9175 /* memorize the current snapshot when appropriate */
9176 if ( !mData->mCurrentSnapshot
9177 && pSnapshot->i_getId() == aCurSnapshotId
9178 )
9179 mData->mCurrentSnapshot = pSnapshot;
9180
9181 /* create all children */
9182 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9183 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9184 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9185 {
9186 llSettingsTodo.push_back(&*it);
9187 llParentsTodo.push_back(pSnapshot);
9188 }
9189 }
9190
9191 return hrc;
9192}
9193
9194/**
9195 * Loads settings into mHWData.
9196 *
9197 * @param puuidRegistry Registry ID.
9198 * @param puuidSnapshot Snapshot ID
9199 * @param data Reference to the hardware settings.
9200 * @param pDbg Pointer to the debugging settings.
9201 * @param pAutostart Pointer to the autostart settings
9202 * @param recording Reference to recording settings.
9203 */
9204HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9205 const Guid *puuidSnapshot,
9206 const settings::Hardware &data,
9207 const settings::Debugging *pDbg,
9208 const settings::Autostart *pAutostart,
9209 const settings::RecordingSettings &recording)
9210{
9211 AssertReturn(!i_isSessionMachine(), E_FAIL);
9212
9213 HRESULT hrc = S_OK;
9214
9215 try
9216 {
9217 ComObjPtr<GuestOSType> pGuestOSType;
9218 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9219
9220 /* The hardware version attribute (optional). */
9221 mHWData->mHWVersion = data.strVersion;
9222 mHWData->mHardwareUUID = data.uuid;
9223
9224 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9225 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9226 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9227 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9228 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9229 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9230 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9231 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9232 mHWData->mPAEEnabled = data.fPAE;
9233 mHWData->mLongMode = data.enmLongMode;
9234 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9235 mHWData->mAPIC = data.fAPIC;
9236 mHWData->mX2APIC = data.fX2APIC;
9237 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9238 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9239 mHWData->mSpecCtrl = data.fSpecCtrl;
9240 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9241 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9242 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9243 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9244 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9245 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9246 mHWData->mCPUCount = data.cCPUs;
9247 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9248 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9249 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9250 mHWData->mCpuProfile = data.strCpuProfile;
9251
9252 // cpu
9253 if (mHWData->mCPUHotPlugEnabled)
9254 {
9255 for (settings::CpuList::const_iterator
9256 it = data.llCpus.begin();
9257 it != data.llCpus.end();
9258 ++it)
9259 {
9260 const settings::Cpu &cpu = *it;
9261
9262 mHWData->mCPUAttached[cpu.ulId] = true;
9263 }
9264 }
9265
9266 // cpuid leafs
9267 for (settings::CpuIdLeafsList::const_iterator
9268 it = data.llCpuIdLeafs.begin();
9269 it != data.llCpuIdLeafs.end();
9270 ++it)
9271 {
9272 const settings::CpuIdLeaf &rLeaf= *it;
9273 if ( rLeaf.idx < UINT32_C(0x20)
9274 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9275 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9276 mHWData->mCpuIdLeafList.push_back(rLeaf);
9277 /* else: just ignore */
9278 }
9279
9280 mHWData->mMemorySize = data.ulMemorySizeMB;
9281 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9282
9283 // boot order
9284 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9285 {
9286 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9287 if (it == data.mapBootOrder.end())
9288 mHWData->mBootOrder[i] = DeviceType_Null;
9289 else
9290 mHWData->mBootOrder[i] = it->second;
9291 }
9292
9293 mHWData->mFirmwareType = data.firmwareType;
9294 mHWData->mPointingHIDType = data.pointingHIDType;
9295 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9296 mHWData->mChipsetType = data.chipsetType;
9297 mHWData->mIommuType = data.iommuType;
9298 mHWData->mParavirtProvider = data.paravirtProvider;
9299 mHWData->mParavirtDebug = data.strParavirtDebug;
9300 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9301 mHWData->mHPETEnabled = data.fHPETEnabled;
9302
9303 /* GraphicsAdapter */
9304 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9305 if (FAILED(hrc)) return hrc;
9306
9307 /* VRDEServer */
9308 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9309 if (FAILED(hrc)) return hrc;
9310
9311 /* BIOS */
9312 hrc = mBIOSSettings->i_loadSettings(data.biosSettings);
9313 if (FAILED(hrc)) return hrc;
9314
9315 /* Recording */
9316 hrc = mRecordingSettings->i_loadSettings(recording);
9317 if (FAILED(hrc)) return hrc;
9318
9319 /* Trusted Platform Module */
9320 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9321 if (FAILED(hrc)) return hrc;
9322
9323 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
9324 if (FAILED(hrc)) return hrc;
9325
9326 // Bandwidth control (must come before network adapters)
9327 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
9328 if (FAILED(hrc)) return hrc;
9329
9330 /* USB controllers */
9331 for (settings::USBControllerList::const_iterator
9332 it = data.usbSettings.llUSBControllers.begin();
9333 it != data.usbSettings.llUSBControllers.end();
9334 ++it)
9335 {
9336 const settings::USBController &settingsCtrl = *it;
9337 ComObjPtr<USBController> newCtrl;
9338
9339 newCtrl.createObject();
9340 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9341 mUSBControllers->push_back(newCtrl);
9342 }
9343
9344 /* USB device filters */
9345 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9346 if (FAILED(hrc)) return hrc;
9347
9348 // network adapters (establish array size first and apply defaults, to
9349 // ensure reading the same settings as we saved, since the list skips
9350 // adapters having defaults)
9351 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9352 size_t oldCount = mNetworkAdapters.size();
9353 if (newCount > oldCount)
9354 {
9355 mNetworkAdapters.resize(newCount);
9356 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9357 {
9358 unconst(mNetworkAdapters[slot]).createObject();
9359 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9360 }
9361 }
9362 else if (newCount < oldCount)
9363 mNetworkAdapters.resize(newCount);
9364 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9365 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9366 for (settings::NetworkAdaptersList::const_iterator
9367 it = data.llNetworkAdapters.begin();
9368 it != data.llNetworkAdapters.end();
9369 ++it)
9370 {
9371 const settings::NetworkAdapter &nic = *it;
9372
9373 /* slot uniqueness is guaranteed by XML Schema */
9374 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9375 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9376 if (FAILED(hrc)) return hrc;
9377 }
9378
9379 // serial ports (establish defaults first, to ensure reading the same
9380 // settings as we saved, since the list skips ports having defaults)
9381 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9382 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9383 for (settings::SerialPortsList::const_iterator
9384 it = data.llSerialPorts.begin();
9385 it != data.llSerialPorts.end();
9386 ++it)
9387 {
9388 const settings::SerialPort &s = *it;
9389
9390 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9391 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9392 if (FAILED(hrc)) return hrc;
9393 }
9394
9395 // parallel ports (establish defaults first, to ensure reading the same
9396 // settings as we saved, since the list skips ports having defaults)
9397 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9398 mParallelPorts[i]->i_applyDefaults();
9399 for (settings::ParallelPortsList::const_iterator
9400 it = data.llParallelPorts.begin();
9401 it != data.llParallelPorts.end();
9402 ++it)
9403 {
9404 const settings::ParallelPort &p = *it;
9405
9406 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9407 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9408 if (FAILED(hrc)) return hrc;
9409 }
9410
9411 /* Audio settings */
9412 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
9413 if (FAILED(hrc)) return hrc;
9414
9415 /* storage controllers */
9416 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
9417 if (FAILED(hrc)) return hrc;
9418
9419 /* Shared folders */
9420 for (settings::SharedFoldersList::const_iterator
9421 it = data.llSharedFolders.begin();
9422 it != data.llSharedFolders.end();
9423 ++it)
9424 {
9425 const settings::SharedFolder &sf = *it;
9426
9427 ComObjPtr<SharedFolder> sharedFolder;
9428 /* Check for double entries. Not allowed! */
9429 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9430 if (SUCCEEDED(hrc))
9431 return setError(VBOX_E_OBJECT_IN_USE,
9432 tr("Shared folder named '%s' already exists"),
9433 sf.strName.c_str());
9434
9435 /* Create the new shared folder. Don't break on error. This will be
9436 * reported when the machine starts. */
9437 sharedFolder.createObject();
9438 hrc = sharedFolder->init(i_getMachine(),
9439 sf.strName,
9440 sf.strHostPath,
9441 RT_BOOL(sf.fWritable),
9442 RT_BOOL(sf.fAutoMount),
9443 sf.strAutoMountPoint,
9444 false /* fFailOnError */);
9445 if (FAILED(hrc)) return hrc;
9446 mHWData->mSharedFolders.push_back(sharedFolder);
9447 }
9448
9449 // Clipboard
9450 mHWData->mClipboardMode = data.clipboardMode;
9451 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9452
9453 // drag'n'drop
9454 mHWData->mDnDMode = data.dndMode;
9455
9456 // guest settings
9457 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9458
9459 // IO settings
9460 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9461 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9462
9463 // Host PCI devices
9464 for (settings::HostPCIDeviceAttachmentList::const_iterator
9465 it = data.pciAttachments.begin();
9466 it != data.pciAttachments.end();
9467 ++it)
9468 {
9469 const settings::HostPCIDeviceAttachment &hpda = *it;
9470 ComObjPtr<PCIDeviceAttachment> pda;
9471
9472 pda.createObject();
9473 pda->i_loadSettings(this, hpda);
9474 mHWData->mPCIDeviceAssignments.push_back(pda);
9475 }
9476
9477 /*
9478 * (The following isn't really real hardware, but it lives in HWData
9479 * for reasons of convenience.)
9480 */
9481
9482#ifdef VBOX_WITH_GUEST_PROPS
9483 /* Guest properties (optional) */
9484
9485 /* Only load transient guest properties for configs which have saved
9486 * state, because there shouldn't be any for powered off VMs. The same
9487 * logic applies for snapshots, as offline snapshots shouldn't have
9488 * any such properties. They confuse the code in various places.
9489 * Note: can't rely on the machine state, as it isn't set yet. */
9490 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9491 /* apologies for the hacky unconst() usage, but this needs hacking
9492 * actually inconsistent settings into consistency, otherwise there
9493 * will be some corner cases where the inconsistency survives
9494 * surprisingly long without getting fixed, especially for snapshots
9495 * as there are no config changes. */
9496 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9497 for (settings::GuestPropertiesList::iterator
9498 it = llGuestProperties.begin();
9499 it != llGuestProperties.end();
9500 /*nothing*/)
9501 {
9502 const settings::GuestProperty &prop = *it;
9503 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9504 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9505 if ( fSkipTransientGuestProperties
9506 && ( fFlags & GUEST_PROP_F_TRANSIENT
9507 || fFlags & GUEST_PROP_F_TRANSRESET))
9508 {
9509 it = llGuestProperties.erase(it);
9510 continue;
9511 }
9512 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9513 mHWData->mGuestProperties[prop.strName] = property;
9514 ++it;
9515 }
9516#endif /* VBOX_WITH_GUEST_PROPS defined */
9517
9518 hrc = i_loadDebugging(pDbg);
9519 if (FAILED(hrc))
9520 return hrc;
9521
9522 mHWData->mAutostart = *pAutostart;
9523
9524 /* default frontend */
9525 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9526 }
9527 catch (std::bad_alloc &)
9528 {
9529 return E_OUTOFMEMORY;
9530 }
9531
9532 AssertComRC(hrc);
9533 return hrc;
9534}
9535
9536/**
9537 * Called from i_loadHardware() to load the debugging settings of the
9538 * machine.
9539 *
9540 * @param pDbg Pointer to the settings.
9541 */
9542HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9543{
9544 mHWData->mDebugging = *pDbg;
9545 /* no more processing currently required, this will probably change. */
9546
9547 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
9548 if (FAILED(hrc)) return hrc;
9549
9550 return S_OK;
9551}
9552
9553/**
9554 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9555 *
9556 * @param data storage settings.
9557 * @param puuidRegistry media registry ID to set media to or NULL;
9558 * see Machine::i_loadMachineDataFromSettings()
9559 * @param puuidSnapshot snapshot ID
9560 * @return
9561 */
9562HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9563 const Guid *puuidRegistry,
9564 const Guid *puuidSnapshot)
9565{
9566 AssertReturn(!i_isSessionMachine(), E_FAIL);
9567
9568 HRESULT hrc = S_OK;
9569
9570 for (settings::StorageControllersList::const_iterator
9571 it = data.llStorageControllers.begin();
9572 it != data.llStorageControllers.end();
9573 ++it)
9574 {
9575 const settings::StorageController &ctlData = *it;
9576
9577 ComObjPtr<StorageController> pCtl;
9578 /* Try to find one with the name first. */
9579 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9580 if (SUCCEEDED(hrc))
9581 return setError(VBOX_E_OBJECT_IN_USE,
9582 tr("Storage controller named '%s' already exists"),
9583 ctlData.strName.c_str());
9584
9585 pCtl.createObject();
9586 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
9587 if (FAILED(hrc)) return hrc;
9588
9589 mStorageControllers->push_back(pCtl);
9590
9591 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9592 if (FAILED(hrc)) return hrc;
9593
9594 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9595 if (FAILED(hrc)) return hrc;
9596
9597 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9598 if (FAILED(hrc)) return hrc;
9599
9600 /* Load the attached devices now. */
9601 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
9602 if (FAILED(hrc)) return hrc;
9603 }
9604
9605 return S_OK;
9606}
9607
9608/**
9609 * Called from i_loadStorageControllers for a controller's devices.
9610 *
9611 * @param aStorageController
9612 * @param data
9613 * @param puuidRegistry media registry ID to set media to or NULL; see
9614 * Machine::i_loadMachineDataFromSettings()
9615 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9616 * @return
9617 */
9618HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9619 const settings::StorageController &data,
9620 const Guid *puuidRegistry,
9621 const Guid *puuidSnapshot)
9622{
9623 HRESULT hrc = S_OK;
9624
9625 /* paranoia: detect duplicate attachments */
9626 for (settings::AttachedDevicesList::const_iterator
9627 it = data.llAttachedDevices.begin();
9628 it != data.llAttachedDevices.end();
9629 ++it)
9630 {
9631 const settings::AttachedDevice &ad = *it;
9632
9633 for (settings::AttachedDevicesList::const_iterator it2 = it;
9634 it2 != data.llAttachedDevices.end();
9635 ++it2)
9636 {
9637 if (it == it2)
9638 continue;
9639
9640 const settings::AttachedDevice &ad2 = *it2;
9641
9642 if ( ad.lPort == ad2.lPort
9643 && ad.lDevice == ad2.lDevice)
9644 {
9645 return setError(E_FAIL,
9646 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9647 aStorageController->i_getName().c_str(),
9648 ad.lPort,
9649 ad.lDevice,
9650 mUserData->s.strName.c_str());
9651 }
9652 }
9653 }
9654
9655 for (settings::AttachedDevicesList::const_iterator
9656 it = data.llAttachedDevices.begin();
9657 it != data.llAttachedDevices.end();
9658 ++it)
9659 {
9660 const settings::AttachedDevice &dev = *it;
9661 ComObjPtr<Medium> medium;
9662
9663 switch (dev.deviceType)
9664 {
9665 case DeviceType_Floppy:
9666 case DeviceType_DVD:
9667 if (dev.strHostDriveSrc.isNotEmpty())
9668 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9669 false /* fRefresh */, medium);
9670 else
9671 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9672 dev.uuid,
9673 false /* fRefresh */,
9674 false /* aSetError */,
9675 medium);
9676 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9677 // This is not an error. The host drive or UUID might have vanished, so just go
9678 // ahead without this removeable medium attachment
9679 hrc = S_OK;
9680 break;
9681
9682 case DeviceType_HardDisk:
9683 {
9684 /* find a hard disk by UUID */
9685 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9686 if (FAILED(hrc))
9687 {
9688 if (i_isSnapshotMachine())
9689 {
9690 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9691 // so the user knows that the bad disk is in a snapshot somewhere
9692 com::ErrorInfo info;
9693 return setError(E_FAIL,
9694 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9695 puuidSnapshot->raw(),
9696 info.getText().raw());
9697 }
9698 return hrc;
9699 }
9700
9701 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9702
9703 if (medium->i_getType() == MediumType_Immutable)
9704 {
9705 if (i_isSnapshotMachine())
9706 return setError(E_FAIL,
9707 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9708 "of the virtual machine '%s' ('%s')"),
9709 medium->i_getLocationFull().c_str(),
9710 dev.uuid.raw(),
9711 puuidSnapshot->raw(),
9712 mUserData->s.strName.c_str(),
9713 mData->m_strConfigFileFull.c_str());
9714
9715 return setError(E_FAIL,
9716 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9717 medium->i_getLocationFull().c_str(),
9718 dev.uuid.raw(),
9719 mUserData->s.strName.c_str(),
9720 mData->m_strConfigFileFull.c_str());
9721 }
9722
9723 if (medium->i_getType() == MediumType_MultiAttach)
9724 {
9725 if (i_isSnapshotMachine())
9726 return setError(E_FAIL,
9727 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9728 "of the virtual machine '%s' ('%s')"),
9729 medium->i_getLocationFull().c_str(),
9730 dev.uuid.raw(),
9731 puuidSnapshot->raw(),
9732 mUserData->s.strName.c_str(),
9733 mData->m_strConfigFileFull.c_str());
9734
9735 return setError(E_FAIL,
9736 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9737 medium->i_getLocationFull().c_str(),
9738 dev.uuid.raw(),
9739 mUserData->s.strName.c_str(),
9740 mData->m_strConfigFileFull.c_str());
9741 }
9742
9743 if ( !i_isSnapshotMachine()
9744 && medium->i_getChildren().size() != 0
9745 )
9746 return setError(E_FAIL,
9747 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9748 "because it has %d differencing child hard disks"),
9749 medium->i_getLocationFull().c_str(),
9750 dev.uuid.raw(),
9751 mUserData->s.strName.c_str(),
9752 mData->m_strConfigFileFull.c_str(),
9753 medium->i_getChildren().size());
9754
9755 if (i_findAttachment(*mMediumAttachments.data(),
9756 medium))
9757 return setError(E_FAIL,
9758 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9759 medium->i_getLocationFull().c_str(),
9760 dev.uuid.raw(),
9761 mUserData->s.strName.c_str(),
9762 mData->m_strConfigFileFull.c_str());
9763
9764 break;
9765 }
9766
9767 default:
9768 return setError(E_FAIL,
9769 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9770 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9771 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9772 }
9773
9774 if (FAILED(hrc))
9775 break;
9776
9777 /* Bandwidth groups are loaded at this point. */
9778 ComObjPtr<BandwidthGroup> pBwGroup;
9779
9780 if (!dev.strBwGroup.isEmpty())
9781 {
9782 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9783 if (FAILED(hrc))
9784 return setError(E_FAIL,
9785 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9786 medium->i_getLocationFull().c_str(),
9787 dev.strBwGroup.c_str(),
9788 mUserData->s.strName.c_str(),
9789 mData->m_strConfigFileFull.c_str());
9790 pBwGroup->i_reference();
9791 }
9792
9793 const Utf8Str controllerName = aStorageController->i_getName();
9794 ComObjPtr<MediumAttachment> pAttachment;
9795 pAttachment.createObject();
9796 hrc = pAttachment->init(this,
9797 medium,
9798 controllerName,
9799 dev.lPort,
9800 dev.lDevice,
9801 dev.deviceType,
9802 false,
9803 dev.fPassThrough,
9804 dev.fTempEject,
9805 dev.fNonRotational,
9806 dev.fDiscard,
9807 dev.fHotPluggable,
9808 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9809 if (FAILED(hrc)) break;
9810
9811 /* associate the medium with this machine and snapshot */
9812 if (!medium.isNull())
9813 {
9814 AutoCaller medCaller(medium);
9815 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9816 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9817
9818 if (i_isSnapshotMachine())
9819 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9820 else
9821 hrc = medium->i_addBackReference(mData->mUuid);
9822 /* If the medium->addBackReference fails it sets an appropriate
9823 * error message, so no need to do any guesswork here. */
9824
9825 if (puuidRegistry)
9826 // caller wants registry ID to be set on all attached media (OVF import case)
9827 medium->i_addRegistry(*puuidRegistry);
9828 }
9829
9830 if (FAILED(hrc))
9831 break;
9832
9833 /* back up mMediumAttachments to let registeredInit() properly rollback
9834 * on failure (= limited accessibility) */
9835 i_setModified(IsModified_Storage);
9836 mMediumAttachments.backup();
9837 mMediumAttachments->push_back(pAttachment);
9838 }
9839
9840 return hrc;
9841}
9842
9843/**
9844 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9845 *
9846 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9847 * @param aSnapshot where to return the found snapshot
9848 * @param aSetError true to set extended error info on failure
9849 */
9850HRESULT Machine::i_findSnapshotById(const Guid &aId,
9851 ComObjPtr<Snapshot> &aSnapshot,
9852 bool aSetError /* = false */)
9853{
9854 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9855
9856 if (!mData->mFirstSnapshot)
9857 {
9858 if (aSetError)
9859 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9860 return E_FAIL;
9861 }
9862
9863 if (aId.isZero())
9864 aSnapshot = mData->mFirstSnapshot;
9865 else
9866 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9867
9868 if (!aSnapshot)
9869 {
9870 if (aSetError)
9871 return setError(E_FAIL,
9872 tr("Could not find a snapshot with UUID {%s}"),
9873 aId.toString().c_str());
9874 return E_FAIL;
9875 }
9876
9877 return S_OK;
9878}
9879
9880/**
9881 * Returns the snapshot with the given name or fails of no such snapshot.
9882 *
9883 * @param strName snapshot name to find
9884 * @param aSnapshot where to return the found snapshot
9885 * @param aSetError true to set extended error info on failure
9886 */
9887HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9888 ComObjPtr<Snapshot> &aSnapshot,
9889 bool aSetError /* = false */)
9890{
9891 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9892
9893 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9894
9895 if (!mData->mFirstSnapshot)
9896 {
9897 if (aSetError)
9898 return setError(VBOX_E_OBJECT_NOT_FOUND,
9899 tr("This machine does not have any snapshots"));
9900 return VBOX_E_OBJECT_NOT_FOUND;
9901 }
9902
9903 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9904
9905 if (!aSnapshot)
9906 {
9907 if (aSetError)
9908 return setError(VBOX_E_OBJECT_NOT_FOUND,
9909 tr("Could not find a snapshot named '%s'"), strName.c_str());
9910 return VBOX_E_OBJECT_NOT_FOUND;
9911 }
9912
9913 return S_OK;
9914}
9915
9916/**
9917 * Returns a storage controller object with the given name.
9918 *
9919 * @param aName storage controller name to find
9920 * @param aStorageController where to return the found storage controller
9921 * @param aSetError true to set extended error info on failure
9922 */
9923HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9924 ComObjPtr<StorageController> &aStorageController,
9925 bool aSetError /* = false */)
9926{
9927 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9928
9929 for (StorageControllerList::const_iterator
9930 it = mStorageControllers->begin();
9931 it != mStorageControllers->end();
9932 ++it)
9933 {
9934 if ((*it)->i_getName() == aName)
9935 {
9936 aStorageController = (*it);
9937 return S_OK;
9938 }
9939 }
9940
9941 if (aSetError)
9942 return setError(VBOX_E_OBJECT_NOT_FOUND,
9943 tr("Could not find a storage controller named '%s'"),
9944 aName.c_str());
9945 return VBOX_E_OBJECT_NOT_FOUND;
9946}
9947
9948/**
9949 * Returns a USB controller object with the given name.
9950 *
9951 * @param aName USB controller name to find
9952 * @param aUSBController where to return the found USB controller
9953 * @param aSetError true to set extended error info on failure
9954 */
9955HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9956 ComObjPtr<USBController> &aUSBController,
9957 bool aSetError /* = false */)
9958{
9959 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9960
9961 for (USBControllerList::const_iterator
9962 it = mUSBControllers->begin();
9963 it != mUSBControllers->end();
9964 ++it)
9965 {
9966 if ((*it)->i_getName() == aName)
9967 {
9968 aUSBController = (*it);
9969 return S_OK;
9970 }
9971 }
9972
9973 if (aSetError)
9974 return setError(VBOX_E_OBJECT_NOT_FOUND,
9975 tr("Could not find a storage controller named '%s'"),
9976 aName.c_str());
9977 return VBOX_E_OBJECT_NOT_FOUND;
9978}
9979
9980/**
9981 * Returns the number of USB controller instance of the given type.
9982 *
9983 * @param enmType USB controller type.
9984 */
9985ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9986{
9987 ULONG cCtrls = 0;
9988
9989 for (USBControllerList::const_iterator
9990 it = mUSBControllers->begin();
9991 it != mUSBControllers->end();
9992 ++it)
9993 {
9994 if ((*it)->i_getControllerType() == enmType)
9995 cCtrls++;
9996 }
9997
9998 return cCtrls;
9999}
10000
10001HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10002 MediumAttachmentList &atts)
10003{
10004 AutoCaller autoCaller(this);
10005 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
10006
10007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10008
10009 for (MediumAttachmentList::const_iterator
10010 it = mMediumAttachments->begin();
10011 it != mMediumAttachments->end();
10012 ++it)
10013 {
10014 const ComObjPtr<MediumAttachment> &pAtt = *it;
10015 // should never happen, but deal with NULL pointers in the list.
10016 AssertContinue(!pAtt.isNull());
10017
10018 // getControllerName() needs caller+read lock
10019 AutoCaller autoAttCaller(pAtt);
10020 if (FAILED(autoAttCaller.hrc()))
10021 {
10022 atts.clear();
10023 return autoAttCaller.hrc();
10024 }
10025 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10026
10027 if (pAtt->i_getControllerName() == aName)
10028 atts.push_back(pAtt);
10029 }
10030
10031 return S_OK;
10032}
10033
10034
10035/**
10036 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10037 * file if the machine name was changed and about creating a new settings file
10038 * if this is a new machine.
10039 *
10040 * @note Must be never called directly but only from #saveSettings().
10041 */
10042HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10043 bool *pfSettingsFileIsNew)
10044{
10045 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10046
10047 HRESULT hrc = S_OK;
10048
10049 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10050 /// @todo need to handle primary group change, too
10051
10052 /* attempt to rename the settings file if machine name is changed */
10053 if ( mUserData->s.fNameSync
10054 && mUserData.isBackedUp()
10055 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10056 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10057 )
10058 {
10059 bool dirRenamed = false;
10060 bool fileRenamed = false;
10061
10062 Utf8Str configFile, newConfigFile;
10063 Utf8Str configFilePrev, newConfigFilePrev;
10064 Utf8Str NVRAMFile, newNVRAMFile;
10065 Utf8Str configDir, newConfigDir;
10066
10067 do
10068 {
10069 int vrc = VINF_SUCCESS;
10070
10071 Utf8Str name = mUserData.backedUpData()->s.strName;
10072 Utf8Str newName = mUserData->s.strName;
10073 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10074 if (group == "/")
10075 group.setNull();
10076 Utf8Str newGroup = mUserData->s.llGroups.front();
10077 if (newGroup == "/")
10078 newGroup.setNull();
10079
10080 configFile = mData->m_strConfigFileFull;
10081
10082 /* first, rename the directory if it matches the group and machine name */
10083 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10084 /** @todo hack, make somehow use of ComposeMachineFilename */
10085 if (mUserData->s.fDirectoryIncludesUUID)
10086 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10087 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10088 /** @todo hack, make somehow use of ComposeMachineFilename */
10089 if (mUserData->s.fDirectoryIncludesUUID)
10090 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10091 configDir = configFile;
10092 configDir.stripFilename();
10093 newConfigDir = configDir;
10094 if ( configDir.length() >= groupPlusName.length()
10095 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10096 groupPlusName.c_str()))
10097 {
10098 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10099 Utf8Str newConfigBaseDir(newConfigDir);
10100 newConfigDir.append(newGroupPlusName);
10101 /* consistency: use \ if appropriate on the platform */
10102 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10103 /* new dir and old dir cannot be equal here because of 'if'
10104 * above and because name != newName */
10105 Assert(configDir != newConfigDir);
10106 if (!fSettingsFileIsNew)
10107 {
10108 /* perform real rename only if the machine is not new */
10109 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10110 if ( vrc == VERR_FILE_NOT_FOUND
10111 || vrc == VERR_PATH_NOT_FOUND)
10112 {
10113 /* create the parent directory, then retry renaming */
10114 Utf8Str parent(newConfigDir);
10115 parent.stripFilename();
10116 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10117 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10118 }
10119 if (RT_FAILURE(vrc))
10120 {
10121 hrc = setErrorBoth(E_FAIL, vrc,
10122 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10123 configDir.c_str(),
10124 newConfigDir.c_str(),
10125 vrc);
10126 break;
10127 }
10128 /* delete subdirectories which are no longer needed */
10129 Utf8Str dir(configDir);
10130 dir.stripFilename();
10131 while (dir != newConfigBaseDir && dir != ".")
10132 {
10133 vrc = RTDirRemove(dir.c_str());
10134 if (RT_FAILURE(vrc))
10135 break;
10136 dir.stripFilename();
10137 }
10138 dirRenamed = true;
10139 }
10140 }
10141
10142 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10143
10144 /* then try to rename the settings file itself */
10145 if (newConfigFile != configFile)
10146 {
10147 /* get the path to old settings file in renamed directory */
10148 Assert(mData->m_strConfigFileFull == configFile);
10149 configFile.printf("%s%c%s",
10150 newConfigDir.c_str(),
10151 RTPATH_DELIMITER,
10152 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10153 if (!fSettingsFileIsNew)
10154 {
10155 /* perform real rename only if the machine is not new */
10156 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10157 if (RT_FAILURE(vrc))
10158 {
10159 hrc = setErrorBoth(E_FAIL, vrc,
10160 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10161 configFile.c_str(),
10162 newConfigFile.c_str(),
10163 vrc);
10164 break;
10165 }
10166 fileRenamed = true;
10167 configFilePrev = configFile;
10168 configFilePrev += "-prev";
10169 newConfigFilePrev = newConfigFile;
10170 newConfigFilePrev += "-prev";
10171 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10172 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10173 if (NVRAMFile.isNotEmpty())
10174 {
10175 // in the NVRAM file path, replace the old directory with the new directory
10176 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10177 {
10178 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10179 NVRAMFile = newConfigDir + strNVRAMFile;
10180 }
10181 newNVRAMFile = newConfigFile;
10182 newNVRAMFile.stripSuffix();
10183 newNVRAMFile += ".nvram";
10184 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10185 }
10186 }
10187 }
10188
10189 // update m_strConfigFileFull amd mConfigFile
10190 mData->m_strConfigFileFull = newConfigFile;
10191 // compute the relative path too
10192 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10193
10194 // store the old and new so that VirtualBox::i_saveSettings() can update
10195 // the media registry
10196 if ( mData->mRegistered
10197 && (configDir != newConfigDir || configFile != newConfigFile))
10198 {
10199 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10200
10201 if (pfNeedsGlobalSaveSettings)
10202 *pfNeedsGlobalSaveSettings = true;
10203 }
10204
10205 // in the saved state file path, replace the old directory with the new directory
10206 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10207 {
10208 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10209 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10210 }
10211 if (newNVRAMFile.isNotEmpty())
10212 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10213
10214 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10215 if (mData->mFirstSnapshot)
10216 {
10217 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10218 newConfigDir.c_str());
10219 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10220 newConfigDir.c_str());
10221 }
10222 }
10223 while (0);
10224
10225 if (FAILED(hrc))
10226 {
10227 /* silently try to rename everything back */
10228 if (fileRenamed)
10229 {
10230 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10231 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10232 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10233 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10234 }
10235 if (dirRenamed)
10236 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10237 }
10238
10239 if (FAILED(hrc)) return hrc;
10240 }
10241
10242 if (fSettingsFileIsNew)
10243 {
10244 /* create a virgin config file */
10245 int vrc = VINF_SUCCESS;
10246
10247 /* ensure the settings directory exists */
10248 Utf8Str path(mData->m_strConfigFileFull);
10249 path.stripFilename();
10250 if (!RTDirExists(path.c_str()))
10251 {
10252 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10253 if (RT_FAILURE(vrc))
10254 {
10255 return setErrorBoth(E_FAIL, vrc,
10256 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10257 path.c_str(),
10258 vrc);
10259 }
10260 }
10261
10262 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10263 path = mData->m_strConfigFileFull;
10264 RTFILE f = NIL_RTFILE;
10265 vrc = RTFileOpen(&f, path.c_str(),
10266 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10267 if (RT_FAILURE(vrc))
10268 return setErrorBoth(E_FAIL, vrc,
10269 tr("Could not create the settings file '%s' (%Rrc)"),
10270 path.c_str(),
10271 vrc);
10272 RTFileClose(f);
10273 }
10274 if (pfSettingsFileIsNew)
10275 *pfSettingsFileIsNew = fSettingsFileIsNew;
10276
10277 return hrc;
10278}
10279
10280/**
10281 * Saves and commits machine data, user data and hardware data.
10282 *
10283 * Note that on failure, the data remains uncommitted.
10284 *
10285 * @a aFlags may combine the following flags:
10286 *
10287 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10288 * Used when saving settings after an operation that makes them 100%
10289 * correspond to the settings from the current snapshot.
10290 * - SaveS_Force: settings will be saved without doing a deep compare of the
10291 * settings structures. This is used when this is called because snapshots
10292 * have changed to avoid the overhead of the deep compare.
10293 *
10294 * @note Must be called from under this object's write lock. Locks children for
10295 * writing.
10296 *
10297 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10298 * initialized to false and that will be set to true by this function if
10299 * the caller must invoke VirtualBox::i_saveSettings() because the global
10300 * settings have changed. This will happen if a machine rename has been
10301 * saved and the global machine and media registries will therefore need
10302 * updating.
10303 * @param alock Reference to the lock for this machine object.
10304 * @param aFlags Flags.
10305 */
10306HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10307 AutoWriteLock &alock,
10308 int aFlags /*= 0*/)
10309{
10310 LogFlowThisFuncEnter();
10311
10312 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10313
10314 /* make sure child objects are unable to modify the settings while we are
10315 * saving them */
10316 i_ensureNoStateDependencies(alock);
10317
10318 AssertReturn(!i_isSnapshotMachine(),
10319 E_FAIL);
10320
10321 if (!mData->mAccessible)
10322 return setError(VBOX_E_INVALID_VM_STATE,
10323 tr("The machine is not accessible, so cannot save settings"));
10324
10325 HRESULT hrc = S_OK;
10326 PCVBOXCRYPTOIF pCryptoIf = NULL;
10327 const char *pszPassword = NULL;
10328 SecretKey *pKey = NULL;
10329
10330#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10331 if (mData->mstrKeyId.isNotEmpty())
10332 {
10333 /* VM is going to be encrypted. */
10334 alock.release(); /** @todo Revise the locking. */
10335 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
10336 alock.acquire();
10337 if (FAILED(hrc)) return hrc; /* Error is set. */
10338
10339 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10340 if (RT_SUCCESS(vrc))
10341 pszPassword = (const char *)pKey->getKeyBuffer();
10342 else
10343 {
10344 mParent->i_releaseCryptoIf(pCryptoIf);
10345 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10346 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10347 mData->mstrKeyId.c_str(), vrc);
10348 }
10349 }
10350#else
10351 RT_NOREF(pKey);
10352#endif
10353
10354 bool fNeedsWrite = false;
10355 bool fSettingsFileIsNew = false;
10356
10357 /* First, prepare to save settings. It will care about renaming the
10358 * settings directory and file if the machine name was changed and about
10359 * creating a new settings file if this is a new machine. */
10360 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
10361 if (FAILED(hrc))
10362 {
10363#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10364 if (pCryptoIf)
10365 {
10366 alock.release(); /** @todo Revise the locking. */
10367 mParent->i_releaseCryptoIf(pCryptoIf);
10368 alock.acquire();
10369 }
10370 if (pKey)
10371 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10372#endif
10373 return hrc;
10374 }
10375
10376 // keep a pointer to the current settings structures
10377 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10378 settings::MachineConfigFile *pNewConfig = NULL;
10379
10380 try
10381 {
10382 // make a fresh one to have everyone write stuff into
10383 pNewConfig = new settings::MachineConfigFile(NULL);
10384 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10385#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10386 pNewConfig->strKeyId = mData->mstrKeyId;
10387 pNewConfig->strKeyStore = mData->mstrKeyStore;
10388#endif
10389
10390 // now go and copy all the settings data from COM to the settings structures
10391 // (this calls i_saveSettings() on all the COM objects in the machine)
10392 i_copyMachineDataToSettings(*pNewConfig);
10393
10394 if (aFlags & SaveS_ResetCurStateModified)
10395 {
10396 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10397 mData->mCurrentStateModified = FALSE;
10398 fNeedsWrite = true; // always, no need to compare
10399 }
10400 else if (aFlags & SaveS_Force)
10401 {
10402 fNeedsWrite = true; // always, no need to compare
10403 }
10404 else
10405 {
10406 if (!mData->mCurrentStateModified)
10407 {
10408 // do a deep compare of the settings that we just saved with the settings
10409 // previously stored in the config file; this invokes MachineConfigFile::operator==
10410 // which does a deep compare of all the settings, which is expensive but less expensive
10411 // than writing out XML in vain
10412 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10413
10414 // could still be modified if any settings changed
10415 mData->mCurrentStateModified = fAnySettingsChanged;
10416
10417 fNeedsWrite = fAnySettingsChanged;
10418 }
10419 else
10420 fNeedsWrite = true;
10421 }
10422
10423 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10424
10425 if (fNeedsWrite)
10426 {
10427 // now spit it all out!
10428 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10429 if (aFlags & SaveS_RemoveBackup)
10430 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10431 }
10432
10433 mData->pMachineConfigFile = pNewConfig;
10434 delete pOldConfig;
10435 i_commit();
10436
10437 // after saving settings, we are no longer different from the XML on disk
10438 mData->flModifications = 0;
10439 }
10440 catch (HRESULT err)
10441 {
10442 // we assume that error info is set by the thrower
10443 hrc = err;
10444
10445 // delete any newly created settings file
10446 if (fSettingsFileIsNew)
10447 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10448
10449 // restore old config
10450 delete pNewConfig;
10451 mData->pMachineConfigFile = pOldConfig;
10452 }
10453 catch (...)
10454 {
10455 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10456 }
10457
10458#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10459 if (pCryptoIf)
10460 {
10461 alock.release(); /** @todo Revise the locking. */
10462 mParent->i_releaseCryptoIf(pCryptoIf);
10463 alock.acquire();
10464 }
10465 if (pKey)
10466 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10467#endif
10468
10469 if (fNeedsWrite)
10470 {
10471 /* Fire the data change event, even on failure (since we've already
10472 * committed all data). This is done only for SessionMachines because
10473 * mutable Machine instances are always not registered (i.e. private
10474 * to the client process that creates them) and thus don't need to
10475 * inform callbacks. */
10476 if (i_isSessionMachine())
10477 mParent->i_onMachineDataChanged(mData->mUuid);
10478 }
10479
10480 LogFlowThisFunc(("hrc=%08X\n", hrc));
10481 LogFlowThisFuncLeave();
10482 return hrc;
10483}
10484
10485/**
10486 * Implementation for saving the machine settings into the given
10487 * settings::MachineConfigFile instance. This copies machine extradata
10488 * from the previous machine config file in the instance data, if any.
10489 *
10490 * This gets called from two locations:
10491 *
10492 * -- Machine::i_saveSettings(), during the regular XML writing;
10493 *
10494 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10495 * exported to OVF and we write the VirtualBox proprietary XML
10496 * into a <vbox:Machine> tag.
10497 *
10498 * This routine fills all the fields in there, including snapshots, *except*
10499 * for the following:
10500 *
10501 * -- fCurrentStateModified. There is some special logic associated with that.
10502 *
10503 * The caller can then call MachineConfigFile::write() or do something else
10504 * with it.
10505 *
10506 * Caller must hold the machine lock!
10507 *
10508 * This throws XML errors and HRESULT, so the caller must have a catch block!
10509 */
10510void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10511{
10512 // deep copy extradata, being extra careful with self assignment (the STL
10513 // map assignment on Mac OS X clang based Xcode isn't checking)
10514 if (&config != mData->pMachineConfigFile)
10515 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10516
10517 config.uuid = mData->mUuid;
10518
10519 // copy name, description, OS type, teleport, UTC etc.
10520 config.machineUserData = mUserData->s;
10521
10522#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10523 config.strStateKeyId = mSSData->strStateKeyId;
10524 config.strStateKeyStore = mSSData->strStateKeyStore;
10525 config.strLogKeyId = mData->mstrLogKeyId;
10526 config.strLogKeyStore = mData->mstrLogKeyStore;
10527#endif
10528
10529 if ( mData->mMachineState == MachineState_Saved
10530 || mData->mMachineState == MachineState_AbortedSaved
10531 || mData->mMachineState == MachineState_Restoring
10532 // when doing certain snapshot operations we may or may not have
10533 // a saved state in the current state, so keep everything as is
10534 || ( ( mData->mMachineState == MachineState_Snapshotting
10535 || mData->mMachineState == MachineState_DeletingSnapshot
10536 || mData->mMachineState == MachineState_RestoringSnapshot)
10537 && (!mSSData->strStateFilePath.isEmpty())
10538 )
10539 )
10540 {
10541 Assert(!mSSData->strStateFilePath.isEmpty());
10542 /* try to make the file name relative to the settings file dir */
10543 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10544 }
10545 else
10546 {
10547 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10548 config.strStateFile.setNull();
10549 }
10550
10551 if (mData->mCurrentSnapshot)
10552 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10553 else
10554 config.uuidCurrentSnapshot.clear();
10555
10556 config.timeLastStateChange = mData->mLastStateChange;
10557 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10558 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10559
10560 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10561 if (FAILED(hrc)) throw hrc;
10562
10563 // save machine's media registry if this is VirtualBox 4.0 or later
10564 if (config.canHaveOwnMediaRegistry())
10565 {
10566 // determine machine folder
10567 Utf8Str strMachineFolder = i_getSettingsFileFull();
10568 strMachineFolder.stripFilename();
10569 mParent->i_saveMediaRegistry(config.mediaRegistry,
10570 i_getId(), // only media with registry ID == machine UUID
10571 strMachineFolder);
10572 // this throws HRESULT
10573 }
10574
10575 // save snapshots
10576 hrc = i_saveAllSnapshots(config);
10577 if (FAILED(hrc)) throw hrc;
10578}
10579
10580/**
10581 * Saves all snapshots of the machine into the given machine config file. Called
10582 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10583 * @param config
10584 * @return
10585 */
10586HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10587{
10588 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10589
10590 HRESULT hrc = S_OK;
10591
10592 try
10593 {
10594 config.llFirstSnapshot.clear();
10595
10596 if (mData->mFirstSnapshot)
10597 {
10598 // the settings use a list for "the first snapshot"
10599 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10600
10601 // get reference to the snapshot on the list and work on that
10602 // element straight in the list to avoid excessive copying later
10603 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10604 if (FAILED(hrc)) throw hrc;
10605 }
10606
10607// if (mType == IsSessionMachine)
10608// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10609
10610 }
10611 catch (HRESULT err)
10612 {
10613 /* we assume that error info is set by the thrower */
10614 hrc = err;
10615 }
10616 catch (...)
10617 {
10618 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10619 }
10620
10621 return hrc;
10622}
10623
10624/**
10625 * Saves the VM hardware configuration. It is assumed that the
10626 * given node is empty.
10627 *
10628 * @param data Reference to the settings object for the hardware config.
10629 * @param pDbg Pointer to the settings object for the debugging config
10630 * which happens to live in mHWData.
10631 * @param pAutostart Pointer to the settings object for the autostart config
10632 * which happens to live in mHWData.
10633 * @param recording Reference to reecording settings.
10634 */
10635HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10636 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10637{
10638 HRESULT hrc = S_OK;
10639
10640 try
10641 {
10642 /* The hardware version attribute (optional).
10643 Automatically upgrade from 1 to current default hardware version
10644 when there is no saved state. (ugly!) */
10645 if ( mHWData->mHWVersion == "1"
10646 && mSSData->strStateFilePath.isEmpty()
10647 )
10648 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10649
10650 data.strVersion = mHWData->mHWVersion;
10651 data.uuid = mHWData->mHardwareUUID;
10652
10653 // CPU
10654 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10655 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10656 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10657 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10658 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10659 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10660 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10661 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10662 data.fPAE = !!mHWData->mPAEEnabled;
10663 data.enmLongMode = mHWData->mLongMode;
10664 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10665 data.fAPIC = !!mHWData->mAPIC;
10666 data.fX2APIC = !!mHWData->mX2APIC;
10667 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10668 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10669 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10670 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10671 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10672 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10673 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10674 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10675 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10676 data.cCPUs = mHWData->mCPUCount;
10677 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10678 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10679 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10680 data.strCpuProfile = mHWData->mCpuProfile;
10681
10682 data.llCpus.clear();
10683 if (data.fCpuHotPlug)
10684 {
10685 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10686 {
10687 if (mHWData->mCPUAttached[idx])
10688 {
10689 settings::Cpu cpu;
10690 cpu.ulId = idx;
10691 data.llCpus.push_back(cpu);
10692 }
10693 }
10694 }
10695
10696 /* Standard and Extended CPUID leafs. */
10697 data.llCpuIdLeafs.clear();
10698 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10699
10700 // memory
10701 data.ulMemorySizeMB = mHWData->mMemorySize;
10702 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10703
10704 // firmware
10705 data.firmwareType = mHWData->mFirmwareType;
10706
10707 // HID
10708 data.pointingHIDType = mHWData->mPointingHIDType;
10709 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10710
10711 // chipset
10712 data.chipsetType = mHWData->mChipsetType;
10713
10714 // iommu
10715 data.iommuType = mHWData->mIommuType;
10716
10717 // paravirt
10718 data.paravirtProvider = mHWData->mParavirtProvider;
10719 data.strParavirtDebug = mHWData->mParavirtDebug;
10720
10721 // emulated USB card reader
10722 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10723
10724 // HPET
10725 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10726
10727 // boot order
10728 data.mapBootOrder.clear();
10729 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10730 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10731
10732 /* VRDEServer settings (optional) */
10733 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10734 if (FAILED(hrc)) throw hrc;
10735
10736 /* BIOS settings (required) */
10737 hrc = mBIOSSettings->i_saveSettings(data.biosSettings);
10738 if (FAILED(hrc)) throw hrc;
10739
10740 /* Recording settings. */
10741 hrc = mRecordingSettings->i_saveSettings(recording);
10742 if (FAILED(hrc)) throw hrc;
10743
10744 /* Trusted Platform Module settings (required) */
10745 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10746 if (FAILED(hrc)) throw hrc;
10747
10748 /* NVRAM settings (required) */
10749 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10750 if (FAILED(hrc)) throw hrc;
10751
10752 /* GraphicsAdapter settings (required) */
10753 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10754 if (FAILED(hrc)) throw hrc;
10755
10756 /* USB Controller (required) */
10757 data.usbSettings.llUSBControllers.clear();
10758 for (USBControllerList::const_iterator
10759 it = mUSBControllers->begin();
10760 it != mUSBControllers->end();
10761 ++it)
10762 {
10763 ComObjPtr<USBController> ctrl = *it;
10764 settings::USBController settingsCtrl;
10765
10766 settingsCtrl.strName = ctrl->i_getName();
10767 settingsCtrl.enmType = ctrl->i_getControllerType();
10768
10769 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10770 }
10771
10772 /* USB device filters (required) */
10773 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10774 if (FAILED(hrc)) throw hrc;
10775
10776 /* Network adapters (required) */
10777 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10778 data.llNetworkAdapters.clear();
10779 /* Write out only the nominal number of network adapters for this
10780 * chipset type. Since Machine::commit() hasn't been called there
10781 * may be extra NIC settings in the vector. */
10782 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10783 {
10784 settings::NetworkAdapter nic;
10785 nic.ulSlot = (uint32_t)slot;
10786 /* paranoia check... must not be NULL, but must not crash either. */
10787 if (mNetworkAdapters[slot])
10788 {
10789 if (mNetworkAdapters[slot]->i_hasDefaults())
10790 continue;
10791
10792 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10793 if (FAILED(hrc)) throw hrc;
10794
10795 data.llNetworkAdapters.push_back(nic);
10796 }
10797 }
10798
10799 /* Serial ports */
10800 data.llSerialPorts.clear();
10801 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10802 {
10803 if (mSerialPorts[slot]->i_hasDefaults())
10804 continue;
10805
10806 settings::SerialPort s;
10807 s.ulSlot = slot;
10808 hrc = mSerialPorts[slot]->i_saveSettings(s);
10809 if (FAILED(hrc)) return hrc;
10810
10811 data.llSerialPorts.push_back(s);
10812 }
10813
10814 /* Parallel ports */
10815 data.llParallelPorts.clear();
10816 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10817 {
10818 if (mParallelPorts[slot]->i_hasDefaults())
10819 continue;
10820
10821 settings::ParallelPort p;
10822 p.ulSlot = slot;
10823 hrc = mParallelPorts[slot]->i_saveSettings(p);
10824 if (FAILED(hrc)) return hrc;
10825
10826 data.llParallelPorts.push_back(p);
10827 }
10828
10829 /* Audio settings */
10830 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10831 if (FAILED(hrc)) return hrc;
10832
10833 hrc = i_saveStorageControllers(data.storage);
10834 if (FAILED(hrc)) return hrc;
10835
10836 /* Shared folders */
10837 data.llSharedFolders.clear();
10838 for (HWData::SharedFolderList::const_iterator
10839 it = mHWData->mSharedFolders.begin();
10840 it != mHWData->mSharedFolders.end();
10841 ++it)
10842 {
10843 SharedFolder *pSF = *it;
10844 AutoCaller sfCaller(pSF);
10845 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10846 settings::SharedFolder sf;
10847 sf.strName = pSF->i_getName();
10848 sf.strHostPath = pSF->i_getHostPath();
10849 sf.fWritable = !!pSF->i_isWritable();
10850 sf.fAutoMount = !!pSF->i_isAutoMounted();
10851 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10852
10853 data.llSharedFolders.push_back(sf);
10854 }
10855
10856 // clipboard
10857 data.clipboardMode = mHWData->mClipboardMode;
10858 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10859
10860 // drag'n'drop
10861 data.dndMode = mHWData->mDnDMode;
10862
10863 /* Guest */
10864 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10865
10866 // IO settings
10867 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10868 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10869
10870 /* BandwidthControl (required) */
10871 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10872 if (FAILED(hrc)) throw hrc;
10873
10874 /* Host PCI devices */
10875 data.pciAttachments.clear();
10876 for (HWData::PCIDeviceAssignmentList::const_iterator
10877 it = mHWData->mPCIDeviceAssignments.begin();
10878 it != mHWData->mPCIDeviceAssignments.end();
10879 ++it)
10880 {
10881 ComObjPtr<PCIDeviceAttachment> pda = *it;
10882 settings::HostPCIDeviceAttachment hpda;
10883
10884 hrc = pda->i_saveSettings(hpda);
10885 if (FAILED(hrc)) throw hrc;
10886
10887 data.pciAttachments.push_back(hpda);
10888 }
10889
10890 // guest properties
10891 data.llGuestProperties.clear();
10892#ifdef VBOX_WITH_GUEST_PROPS
10893 for (HWData::GuestPropertyMap::const_iterator
10894 it = mHWData->mGuestProperties.begin();
10895 it != mHWData->mGuestProperties.end();
10896 ++it)
10897 {
10898 HWData::GuestProperty property = it->second;
10899
10900 /* Remove transient guest properties at shutdown unless we
10901 * are saving state. Note that restoring snapshot intentionally
10902 * keeps them, they will be removed if appropriate once the final
10903 * machine state is set (as crashes etc. need to work). */
10904 if ( ( mData->mMachineState == MachineState_PoweredOff
10905 || mData->mMachineState == MachineState_Aborted
10906 || mData->mMachineState == MachineState_Teleported)
10907 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10908 continue;
10909 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10910 prop.strName = it->first;
10911 prop.strValue = property.strValue;
10912 prop.timestamp = (uint64_t)property.mTimestamp;
10913 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10914 GuestPropWriteFlags(property.mFlags, szFlags);
10915 prop.strFlags = szFlags;
10916
10917 data.llGuestProperties.push_back(prop);
10918 }
10919
10920 /* I presume this doesn't require a backup(). */
10921 mData->mGuestPropertiesModified = FALSE;
10922#endif /* VBOX_WITH_GUEST_PROPS defined */
10923
10924 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10925 if (FAILED(hrc)) throw hrc;
10926
10927 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10928 *pAutostart = mHWData->mAutostart;
10929
10930 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10931 }
10932 catch (std::bad_alloc &)
10933 {
10934 return E_OUTOFMEMORY;
10935 }
10936
10937 AssertComRC(hrc);
10938 return hrc;
10939}
10940
10941/**
10942 * Saves the storage controller configuration.
10943 *
10944 * @param data storage settings.
10945 */
10946HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10947{
10948 data.llStorageControllers.clear();
10949
10950 for (StorageControllerList::const_iterator
10951 it = mStorageControllers->begin();
10952 it != mStorageControllers->end();
10953 ++it)
10954 {
10955 ComObjPtr<StorageController> pCtl = *it;
10956
10957 settings::StorageController ctl;
10958 ctl.strName = pCtl->i_getName();
10959 ctl.controllerType = pCtl->i_getControllerType();
10960 ctl.storageBus = pCtl->i_getStorageBus();
10961 ctl.ulInstance = pCtl->i_getInstance();
10962 ctl.fBootable = pCtl->i_getBootable();
10963
10964 /* Save the port count. */
10965 ULONG portCount;
10966 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10967 ComAssertComRCRet(hrc, hrc);
10968 ctl.ulPortCount = portCount;
10969
10970 /* Save fUseHostIOCache */
10971 BOOL fUseHostIOCache;
10972 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10973 ComAssertComRCRet(hrc, hrc);
10974 ctl.fUseHostIOCache = !!fUseHostIOCache;
10975
10976 /* save the devices now. */
10977 hrc = i_saveStorageDevices(pCtl, ctl);
10978 ComAssertComRCRet(hrc, hrc);
10979
10980 data.llStorageControllers.push_back(ctl);
10981 }
10982
10983 return S_OK;
10984}
10985
10986/**
10987 * Saves the hard disk configuration.
10988 */
10989HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10990 settings::StorageController &data)
10991{
10992 MediumAttachmentList atts;
10993
10994 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10995 if (FAILED(hrc)) return hrc;
10996
10997 data.llAttachedDevices.clear();
10998 for (MediumAttachmentList::const_iterator
10999 it = atts.begin();
11000 it != atts.end();
11001 ++it)
11002 {
11003 settings::AttachedDevice dev;
11004 IMediumAttachment *iA = *it;
11005 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11006 Medium *pMedium = pAttach->i_getMedium();
11007
11008 dev.deviceType = pAttach->i_getType();
11009 dev.lPort = pAttach->i_getPort();
11010 dev.lDevice = pAttach->i_getDevice();
11011 dev.fPassThrough = pAttach->i_getPassthrough();
11012 dev.fHotPluggable = pAttach->i_getHotPluggable();
11013 if (pMedium)
11014 {
11015 if (pMedium->i_isHostDrive())
11016 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11017 else
11018 dev.uuid = pMedium->i_getId();
11019 dev.fTempEject = pAttach->i_getTempEject();
11020 dev.fNonRotational = pAttach->i_getNonRotational();
11021 dev.fDiscard = pAttach->i_getDiscard();
11022 }
11023
11024 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11025
11026 data.llAttachedDevices.push_back(dev);
11027 }
11028
11029 return S_OK;
11030}
11031
11032/**
11033 * Saves machine state settings as defined by aFlags
11034 * (SaveSTS_* values).
11035 *
11036 * @param aFlags Combination of SaveSTS_* flags.
11037 *
11038 * @note Locks objects for writing.
11039 */
11040HRESULT Machine::i_saveStateSettings(int aFlags)
11041{
11042 if (aFlags == 0)
11043 return S_OK;
11044
11045 AutoCaller autoCaller(this);
11046 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11047
11048 /* This object's write lock is also necessary to serialize file access
11049 * (prevent concurrent reads and writes) */
11050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11051
11052 HRESULT hrc = S_OK;
11053
11054 Assert(mData->pMachineConfigFile);
11055
11056 try
11057 {
11058 if (aFlags & SaveSTS_CurStateModified)
11059 mData->pMachineConfigFile->fCurrentStateModified = true;
11060
11061 if (aFlags & SaveSTS_StateFilePath)
11062 {
11063 if (!mSSData->strStateFilePath.isEmpty())
11064 /* try to make the file name relative to the settings file dir */
11065 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11066 else
11067 mData->pMachineConfigFile->strStateFile.setNull();
11068 }
11069
11070 if (aFlags & SaveSTS_StateTimeStamp)
11071 {
11072 Assert( mData->mMachineState != MachineState_Aborted
11073 || mSSData->strStateFilePath.isEmpty());
11074
11075 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11076
11077 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11078 || mData->mMachineState == MachineState_AbortedSaved);
11079/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11080 }
11081
11082 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11083 }
11084 catch (...)
11085 {
11086 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11087 }
11088
11089 return hrc;
11090}
11091
11092/**
11093 * Ensures that the given medium is added to a media registry. If this machine
11094 * was created with 4.0 or later, then the machine registry is used. Otherwise
11095 * the global VirtualBox media registry is used.
11096 *
11097 * Caller must NOT hold machine lock, media tree or any medium locks!
11098 *
11099 * @param pMedium
11100 */
11101void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11102{
11103 /* Paranoia checks: do not hold machine or media tree locks. */
11104 AssertReturnVoid(!isWriteLockOnCurrentThread());
11105 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11106
11107 ComObjPtr<Medium> pBase;
11108 {
11109 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11110 pBase = pMedium->i_getBase();
11111 }
11112
11113 /* Paranoia checks: do not hold medium locks. */
11114 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11115 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11116
11117 // decide which medium registry to use now that the medium is attached:
11118 Guid uuid;
11119 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11120 if (fCanHaveOwnMediaRegistry)
11121 // machine XML is VirtualBox 4.0 or higher:
11122 uuid = i_getId(); // machine UUID
11123 else
11124 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11125
11126 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11127 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11128 if (pMedium->i_addRegistry(uuid))
11129 mParent->i_markRegistryModified(uuid);
11130
11131 /* For more complex hard disk structures it can happen that the base
11132 * medium isn't yet associated with any medium registry. Do that now. */
11133 if (pMedium != pBase)
11134 {
11135 /* Tree lock needed by Medium::addRegistryAll. */
11136 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11137 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11138 {
11139 treeLock.release();
11140 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11141 treeLock.acquire();
11142 }
11143 if (pBase->i_addRegistryAll(uuid))
11144 {
11145 treeLock.release();
11146 mParent->i_markRegistryModified(uuid);
11147 }
11148 }
11149}
11150
11151/**
11152 * Physically deletes a file belonging to a machine.
11153 *
11154 * @returns HRESULT
11155 * @retval VBOX_E_FILE_ERROR on failure.
11156 * @param strFile File to delete.
11157 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11158 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11159 * @param strWhat File hint which will be used when setting an error. Optional.
11160 * @param prc Where to return IPRT's status code on failure.
11161 * Optional and can be NULL.
11162 */
11163HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11164 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11165{
11166 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11167
11168 HRESULT hrc = S_OK;
11169
11170 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11171
11172 int vrc = RTFileDelete(strFile.c_str());
11173 if (RT_FAILURE(vrc))
11174 {
11175 if ( !fIgnoreFailures
11176 /* Don't (externally) bitch about stuff which doesn't exist. */
11177 && ( vrc != VERR_FILE_NOT_FOUND
11178 && vrc != VERR_PATH_NOT_FOUND
11179 )
11180 )
11181 {
11182 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11183
11184 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11185 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11186 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
11187 }
11188 }
11189
11190 if (prc)
11191 *prc = vrc;
11192 return hrc;
11193}
11194
11195/**
11196 * Creates differencing hard disks for all normal hard disks attached to this
11197 * machine and a new set of attachments to refer to created disks.
11198 *
11199 * Used when taking a snapshot or when deleting the current state. Gets called
11200 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11201 *
11202 * This method assumes that mMediumAttachments contains the original hard disk
11203 * attachments it needs to create diffs for. On success, these attachments will
11204 * be replaced with the created diffs.
11205 *
11206 * Attachments with non-normal hard disks are left as is.
11207 *
11208 * If @a aOnline is @c false then the original hard disks that require implicit
11209 * diffs will be locked for reading. Otherwise it is assumed that they are
11210 * already locked for writing (when the VM was started). Note that in the latter
11211 * case it is responsibility of the caller to lock the newly created diffs for
11212 * writing if this method succeeds.
11213 *
11214 * @param aProgress Progress object to run (must contain at least as
11215 * many operations left as the number of hard disks
11216 * attached).
11217 * @param aWeight Weight of this operation.
11218 * @param aOnline Whether the VM was online prior to this operation.
11219 *
11220 * @note The progress object is not marked as completed, neither on success nor
11221 * on failure. This is a responsibility of the caller.
11222 *
11223 * @note Locks this object and the media tree for writing.
11224 */
11225HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11226 ULONG aWeight,
11227 bool aOnline)
11228{
11229 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11230
11231 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11232 AssertReturn(!!pProgressControl, E_INVALIDARG);
11233
11234 AutoCaller autoCaller(this);
11235 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11236
11237 AutoMultiWriteLock2 alock(this->lockHandle(),
11238 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11239
11240 /* must be in a protective state because we release the lock below */
11241 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11242 || mData->mMachineState == MachineState_OnlineSnapshotting
11243 || mData->mMachineState == MachineState_LiveSnapshotting
11244 || mData->mMachineState == MachineState_RestoringSnapshot
11245 || mData->mMachineState == MachineState_DeletingSnapshot
11246 , E_FAIL);
11247
11248 HRESULT hrc = S_OK;
11249
11250 // use appropriate locked media map (online or offline)
11251 MediumLockListMap lockedMediaOffline;
11252 MediumLockListMap *lockedMediaMap;
11253 if (aOnline)
11254 lockedMediaMap = &mData->mSession.mLockedMedia;
11255 else
11256 lockedMediaMap = &lockedMediaOffline;
11257
11258 try
11259 {
11260 if (!aOnline)
11261 {
11262 /* lock all attached hard disks early to detect "in use"
11263 * situations before creating actual diffs */
11264 for (MediumAttachmentList::const_iterator
11265 it = mMediumAttachments->begin();
11266 it != mMediumAttachments->end();
11267 ++it)
11268 {
11269 MediumAttachment *pAtt = *it;
11270 if (pAtt->i_getType() == DeviceType_HardDisk)
11271 {
11272 Medium *pMedium = pAtt->i_getMedium();
11273 Assert(pMedium);
11274
11275 MediumLockList *pMediumLockList(new MediumLockList());
11276 alock.release();
11277 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11278 NULL /* pToLockWrite */,
11279 false /* fMediumLockWriteAll */,
11280 NULL,
11281 *pMediumLockList);
11282 alock.acquire();
11283 if (FAILED(hrc))
11284 {
11285 delete pMediumLockList;
11286 throw hrc;
11287 }
11288 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11289 if (FAILED(hrc))
11290 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
11291 }
11292 }
11293
11294 /* Now lock all media. If this fails, nothing is locked. */
11295 alock.release();
11296 hrc = lockedMediaMap->Lock();
11297 alock.acquire();
11298 if (FAILED(hrc))
11299 throw setError(hrc, tr("Locking of attached media failed"));
11300 }
11301
11302 /* remember the current list (note that we don't use backup() since
11303 * mMediumAttachments may be already backed up) */
11304 MediumAttachmentList atts = *mMediumAttachments.data();
11305
11306 /* start from scratch */
11307 mMediumAttachments->clear();
11308
11309 /* go through remembered attachments and create diffs for normal hard
11310 * disks and attach them */
11311 for (MediumAttachmentList::const_iterator
11312 it = atts.begin();
11313 it != atts.end();
11314 ++it)
11315 {
11316 MediumAttachment *pAtt = *it;
11317
11318 DeviceType_T devType = pAtt->i_getType();
11319 Medium *pMedium = pAtt->i_getMedium();
11320
11321 if ( devType != DeviceType_HardDisk
11322 || pMedium == NULL
11323 || pMedium->i_getType() != MediumType_Normal)
11324 {
11325 /* copy the attachment as is */
11326
11327 /** @todo the progress object created in SessionMachine::TakeSnaphot
11328 * only expects operations for hard disks. Later other
11329 * device types need to show up in the progress as well. */
11330 if (devType == DeviceType_HardDisk)
11331 {
11332 if (pMedium == NULL)
11333 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11334 aWeight); // weight
11335 else
11336 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11337 pMedium->i_getBase()->i_getName().c_str()).raw(),
11338 aWeight); // weight
11339 }
11340
11341 mMediumAttachments->push_back(pAtt);
11342 continue;
11343 }
11344
11345 /* need a diff */
11346 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11347 pMedium->i_getBase()->i_getName().c_str()).raw(),
11348 aWeight); // weight
11349
11350 Utf8Str strFullSnapshotFolder;
11351 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11352
11353 ComObjPtr<Medium> diff;
11354 diff.createObject();
11355 // store the diff in the same registry as the parent
11356 // (this cannot fail here because we can't create implicit diffs for
11357 // unregistered images)
11358 Guid uuidRegistryParent;
11359 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11360 Assert(fInRegistry); NOREF(fInRegistry);
11361 hrc = diff->init(mParent,
11362 pMedium->i_getPreferredDiffFormat(),
11363 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11364 uuidRegistryParent,
11365 DeviceType_HardDisk);
11366 if (FAILED(hrc)) throw hrc;
11367
11368 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11369 * the push_back? Looks like we're going to release medium with the
11370 * wrong kind of lock (general issue with if we fail anywhere at all)
11371 * and an orphaned VDI in the snapshots folder. */
11372
11373 /* update the appropriate lock list */
11374 MediumLockList *pMediumLockList;
11375 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
11376 AssertComRCThrowRC(hrc);
11377 if (aOnline)
11378 {
11379 alock.release();
11380 /* The currently attached medium will be read-only, change
11381 * the lock type to read. */
11382 hrc = pMediumLockList->Update(pMedium, false);
11383 alock.acquire();
11384 AssertComRCThrowRC(hrc);
11385 }
11386
11387 /* release the locks before the potentially lengthy operation */
11388 alock.release();
11389 hrc = pMedium->i_createDiffStorage(diff,
11390 pMedium->i_getPreferredDiffVariant(),
11391 pMediumLockList,
11392 NULL /* aProgress */,
11393 true /* aWait */,
11394 false /* aNotify */);
11395 alock.acquire();
11396 if (FAILED(hrc)) throw hrc;
11397
11398 /* actual lock list update is done in Machine::i_commitMedia */
11399
11400 hrc = diff->i_addBackReference(mData->mUuid);
11401 AssertComRCThrowRC(hrc);
11402
11403 /* add a new attachment */
11404 ComObjPtr<MediumAttachment> attachment;
11405 attachment.createObject();
11406 hrc = attachment->init(this,
11407 diff,
11408 pAtt->i_getControllerName(),
11409 pAtt->i_getPort(),
11410 pAtt->i_getDevice(),
11411 DeviceType_HardDisk,
11412 true /* aImplicit */,
11413 false /* aPassthrough */,
11414 false /* aTempEject */,
11415 pAtt->i_getNonRotational(),
11416 pAtt->i_getDiscard(),
11417 pAtt->i_getHotPluggable(),
11418 pAtt->i_getBandwidthGroup());
11419 if (FAILED(hrc)) throw hrc;
11420
11421 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11422 AssertComRCThrowRC(hrc);
11423 mMediumAttachments->push_back(attachment);
11424 }
11425 }
11426 catch (HRESULT hrcXcpt)
11427 {
11428 hrc = hrcXcpt;
11429 }
11430
11431 /* unlock all hard disks we locked when there is no VM */
11432 if (!aOnline)
11433 {
11434 ErrorInfoKeeper eik;
11435
11436 HRESULT hrc2 = lockedMediaMap->Clear();
11437 AssertComRC(hrc2);
11438 }
11439
11440 return hrc;
11441}
11442
11443/**
11444 * Deletes implicit differencing hard disks created either by
11445 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11446 * mMediumAttachments.
11447 *
11448 * Note that to delete hard disks created by #attachDevice() this method is
11449 * called from #i_rollbackMedia() when the changes are rolled back.
11450 *
11451 * @note Locks this object and the media tree for writing.
11452 */
11453HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11454{
11455 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11456
11457 AutoCaller autoCaller(this);
11458 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11459
11460 AutoMultiWriteLock2 alock(this->lockHandle(),
11461 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11462
11463 /* We absolutely must have backed up state. */
11464 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11465
11466 /* Check if there are any implicitly created diff images. */
11467 bool fImplicitDiffs = false;
11468 for (MediumAttachmentList::const_iterator
11469 it = mMediumAttachments->begin();
11470 it != mMediumAttachments->end();
11471 ++it)
11472 {
11473 const ComObjPtr<MediumAttachment> &pAtt = *it;
11474 if (pAtt->i_isImplicit())
11475 {
11476 fImplicitDiffs = true;
11477 break;
11478 }
11479 }
11480 /* If there is nothing to do, leave early. This saves lots of image locking
11481 * effort. It also avoids a MachineStateChanged event without real reason.
11482 * This is important e.g. when loading a VM config, because there should be
11483 * no events. Otherwise API clients can become thoroughly confused for
11484 * inaccessible VMs (the code for loading VM configs uses this method for
11485 * cleanup if the config makes no sense), as they take such events as an
11486 * indication that the VM is alive, and they would force the VM config to
11487 * be reread, leading to an endless loop. */
11488 if (!fImplicitDiffs)
11489 return S_OK;
11490
11491 HRESULT hrc = S_OK;
11492 MachineState_T oldState = mData->mMachineState;
11493
11494 /* will release the lock before the potentially lengthy operation,
11495 * so protect with the special state (unless already protected) */
11496 if ( oldState != MachineState_Snapshotting
11497 && oldState != MachineState_OnlineSnapshotting
11498 && oldState != MachineState_LiveSnapshotting
11499 && oldState != MachineState_RestoringSnapshot
11500 && oldState != MachineState_DeletingSnapshot
11501 && oldState != MachineState_DeletingSnapshotOnline
11502 && oldState != MachineState_DeletingSnapshotPaused
11503 )
11504 i_setMachineState(MachineState_SettingUp);
11505
11506 // use appropriate locked media map (online or offline)
11507 MediumLockListMap lockedMediaOffline;
11508 MediumLockListMap *lockedMediaMap;
11509 if (aOnline)
11510 lockedMediaMap = &mData->mSession.mLockedMedia;
11511 else
11512 lockedMediaMap = &lockedMediaOffline;
11513
11514 try
11515 {
11516 if (!aOnline)
11517 {
11518 /* lock all attached hard disks early to detect "in use"
11519 * situations before deleting actual diffs */
11520 for (MediumAttachmentList::const_iterator
11521 it = mMediumAttachments->begin();
11522 it != mMediumAttachments->end();
11523 ++it)
11524 {
11525 MediumAttachment *pAtt = *it;
11526 if (pAtt->i_getType() == DeviceType_HardDisk)
11527 {
11528 Medium *pMedium = pAtt->i_getMedium();
11529 Assert(pMedium);
11530
11531 MediumLockList *pMediumLockList(new MediumLockList());
11532 alock.release();
11533 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11534 NULL /* pToLockWrite */,
11535 false /* fMediumLockWriteAll */,
11536 NULL,
11537 *pMediumLockList);
11538 alock.acquire();
11539
11540 if (FAILED(hrc))
11541 {
11542 delete pMediumLockList;
11543 throw hrc;
11544 }
11545
11546 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11547 if (FAILED(hrc))
11548 throw hrc;
11549 }
11550 }
11551
11552 if (FAILED(hrc))
11553 throw hrc;
11554 } // end of offline
11555
11556 /* Lock lists are now up to date and include implicitly created media */
11557
11558 /* Go through remembered attachments and delete all implicitly created
11559 * diffs and fix up the attachment information */
11560 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11561 MediumAttachmentList implicitAtts;
11562 for (MediumAttachmentList::const_iterator
11563 it = mMediumAttachments->begin();
11564 it != mMediumAttachments->end();
11565 ++it)
11566 {
11567 ComObjPtr<MediumAttachment> pAtt = *it;
11568 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11569 if (pMedium.isNull())
11570 continue;
11571
11572 // Implicit attachments go on the list for deletion and back references are removed.
11573 if (pAtt->i_isImplicit())
11574 {
11575 /* Deassociate and mark for deletion */
11576 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11577 hrc = pMedium->i_removeBackReference(mData->mUuid);
11578 if (FAILED(hrc))
11579 throw hrc;
11580 implicitAtts.push_back(pAtt);
11581 continue;
11582 }
11583
11584 /* Was this medium attached before? */
11585 if (!i_findAttachment(oldAtts, pMedium))
11586 {
11587 /* no: de-associate */
11588 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11589 hrc = pMedium->i_removeBackReference(mData->mUuid);
11590 if (FAILED(hrc))
11591 throw hrc;
11592 continue;
11593 }
11594 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11595 }
11596
11597 /* If there are implicit attachments to delete, throw away the lock
11598 * map contents (which will unlock all media) since the medium
11599 * attachments will be rolled back. Below we need to completely
11600 * recreate the lock map anyway since it is infinitely complex to
11601 * do this incrementally (would need reconstructing each attachment
11602 * change, which would be extremely hairy). */
11603 if (implicitAtts.size() != 0)
11604 {
11605 ErrorInfoKeeper eik;
11606
11607 HRESULT hrc2 = lockedMediaMap->Clear();
11608 AssertComRC(hrc2);
11609 }
11610
11611 /* rollback hard disk changes */
11612 mMediumAttachments.rollback();
11613
11614 MultiResult mrc(S_OK);
11615
11616 // Delete unused implicit diffs.
11617 if (implicitAtts.size() != 0)
11618 {
11619 alock.release();
11620
11621 for (MediumAttachmentList::const_iterator
11622 it = implicitAtts.begin();
11623 it != implicitAtts.end();
11624 ++it)
11625 {
11626 // Remove medium associated with this attachment.
11627 ComObjPtr<MediumAttachment> pAtt = *it;
11628 Assert(pAtt);
11629 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11630 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11631 Assert(pMedium);
11632
11633 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11634 // continue on delete failure, just collect error messages
11635 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
11636 pMedium->i_getLocationFull().c_str() ));
11637 mrc = hrc;
11638 }
11639 // Clear the list of deleted implicit attachments now, while not
11640 // holding the lock, as it will ultimately trigger Medium::uninit()
11641 // calls which assume that the media tree lock isn't held.
11642 implicitAtts.clear();
11643
11644 alock.acquire();
11645
11646 /* if there is a VM recreate media lock map as mentioned above,
11647 * otherwise it is a waste of time and we leave things unlocked */
11648 if (aOnline)
11649 {
11650 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11651 /* must never be NULL, but better safe than sorry */
11652 if (!pMachine.isNull())
11653 {
11654 alock.release();
11655 hrc = mData->mSession.mMachine->i_lockMedia();
11656 alock.acquire();
11657 if (FAILED(hrc))
11658 throw hrc;
11659 }
11660 }
11661 }
11662 }
11663 catch (HRESULT hrcXcpt)
11664 {
11665 hrc = hrcXcpt;
11666 }
11667
11668 if (mData->mMachineState == MachineState_SettingUp)
11669 i_setMachineState(oldState);
11670
11671 /* unlock all hard disks we locked when there is no VM */
11672 if (!aOnline)
11673 {
11674 ErrorInfoKeeper eik;
11675
11676 HRESULT hrc2 = lockedMediaMap->Clear();
11677 AssertComRC(hrc2);
11678 }
11679
11680 return hrc;
11681}
11682
11683
11684/**
11685 * Looks through the given list of media attachments for one with the given parameters
11686 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11687 * can be searched as well if needed.
11688 *
11689 * @param ll
11690 * @param aControllerName
11691 * @param aControllerPort
11692 * @param aDevice
11693 * @return
11694 */
11695MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11696 const Utf8Str &aControllerName,
11697 LONG aControllerPort,
11698 LONG aDevice)
11699{
11700 for (MediumAttachmentList::const_iterator
11701 it = ll.begin();
11702 it != ll.end();
11703 ++it)
11704 {
11705 MediumAttachment *pAttach = *it;
11706 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11707 return pAttach;
11708 }
11709
11710 return NULL;
11711}
11712
11713/**
11714 * Looks through the given list of media attachments for one with the given parameters
11715 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11716 * can be searched as well if needed.
11717 *
11718 * @param ll
11719 * @param pMedium
11720 * @return
11721 */
11722MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11723 ComObjPtr<Medium> pMedium)
11724{
11725 for (MediumAttachmentList::const_iterator
11726 it = ll.begin();
11727 it != ll.end();
11728 ++it)
11729 {
11730 MediumAttachment *pAttach = *it;
11731 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11732 if (pMediumThis == pMedium)
11733 return pAttach;
11734 }
11735
11736 return NULL;
11737}
11738
11739/**
11740 * Looks through the given list of media attachments for one with the given parameters
11741 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11742 * can be searched as well if needed.
11743 *
11744 * @param ll
11745 * @param id
11746 * @return
11747 */
11748MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11749 Guid &id)
11750{
11751 for (MediumAttachmentList::const_iterator
11752 it = ll.begin();
11753 it != ll.end();
11754 ++it)
11755 {
11756 MediumAttachment *pAttach = *it;
11757 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11758 if (pMediumThis->i_getId() == id)
11759 return pAttach;
11760 }
11761
11762 return NULL;
11763}
11764
11765/**
11766 * Main implementation for Machine::DetachDevice. This also gets called
11767 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11768 *
11769 * @param pAttach Medium attachment to detach.
11770 * @param writeLock Machine write lock which the caller must have locked once.
11771 * This may be released temporarily in here.
11772 * @param pSnapshot If NULL, then the detachment is for the current machine.
11773 * Otherwise this is for a SnapshotMachine, and this must be
11774 * its snapshot.
11775 * @return
11776 */
11777HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11778 AutoWriteLock &writeLock,
11779 Snapshot *pSnapshot)
11780{
11781 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11782 DeviceType_T mediumType = pAttach->i_getType();
11783
11784 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11785
11786 if (pAttach->i_isImplicit())
11787 {
11788 /* attempt to implicitly delete the implicitly created diff */
11789
11790 /// @todo move the implicit flag from MediumAttachment to Medium
11791 /// and forbid any hard disk operation when it is implicit. Or maybe
11792 /// a special media state for it to make it even more simple.
11793
11794 Assert(mMediumAttachments.isBackedUp());
11795
11796 /* will release the lock before the potentially lengthy operation, so
11797 * protect with the special state */
11798 MachineState_T oldState = mData->mMachineState;
11799 i_setMachineState(MachineState_SettingUp);
11800
11801 writeLock.release();
11802
11803 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11804
11805 writeLock.acquire();
11806
11807 i_setMachineState(oldState);
11808
11809 if (FAILED(hrc)) return hrc;
11810 }
11811
11812 i_setModified(IsModified_Storage);
11813 mMediumAttachments.backup();
11814 mMediumAttachments->remove(pAttach);
11815
11816 if (!oldmedium.isNull())
11817 {
11818 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11819 if (pSnapshot)
11820 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11821 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11822 else if (mediumType != DeviceType_HardDisk)
11823 oldmedium->i_removeBackReference(mData->mUuid);
11824 }
11825
11826 return S_OK;
11827}
11828
11829/**
11830 * Goes thru all media of the given list and
11831 *
11832 * 1) calls i_detachDevice() on each of them for this machine and
11833 * 2) adds all Medium objects found in the process to the given list,
11834 * depending on cleanupMode.
11835 *
11836 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11837 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11838 * media to the list.
11839 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11840 * also removable media if they are located in the VM folder and referenced
11841 * only by this VM (media prepared by unattended installer).
11842 *
11843 * This gets called from Machine::Unregister, both for the actual Machine and
11844 * the SnapshotMachine objects that might be found in the snapshots.
11845 *
11846 * Requires caller and locking. The machine lock must be passed in because it
11847 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11848 *
11849 * @param writeLock Machine lock from top-level caller; this gets passed to
11850 * i_detachDevice.
11851 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11852 * object if called for a SnapshotMachine.
11853 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11854 * added to llMedia; if Full, then all media get added;
11855 * otherwise no media get added.
11856 * @param llMedia Caller's list to receive Medium objects which got detached so
11857 * caller can close() them, depending on cleanupMode.
11858 * @return
11859 */
11860HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11861 Snapshot *pSnapshot,
11862 CleanupMode_T cleanupMode,
11863 MediaList &llMedia)
11864{
11865 Assert(isWriteLockOnCurrentThread());
11866
11867 HRESULT hrc;
11868
11869 // make a temporary list because i_detachDevice invalidates iterators into
11870 // mMediumAttachments
11871 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11872
11873 for (MediumAttachmentList::iterator
11874 it = llAttachments2.begin();
11875 it != llAttachments2.end();
11876 ++it)
11877 {
11878 ComObjPtr<MediumAttachment> &pAttach = *it;
11879 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11880
11881 if (!pMedium.isNull())
11882 {
11883 AutoCaller mac(pMedium);
11884 if (FAILED(mac.hrc())) return mac.hrc();
11885 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11886 DeviceType_T devType = pMedium->i_getDeviceType();
11887 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11888 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11889 strMediumLocation.stripFilename();
11890 Utf8Str strMachineFolder = i_getSettingsFileFull();
11891 strMachineFolder.stripFilename();
11892 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11893 && devType == DeviceType_HardDisk)
11894 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11895 && ( devType == DeviceType_HardDisk
11896 || ( cBackRefs <= 1
11897 && strMediumLocation == strMachineFolder
11898 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11899 || (cleanupMode == CleanupMode_Full)
11900 )
11901 {
11902 llMedia.push_back(pMedium);
11903 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11904 /* Not allowed to keep this lock as below we need the parent
11905 * medium lock, and the lock order is parent to child. */
11906 lock.release();
11907 /*
11908 * Search for media which are not attached to any machine, but
11909 * in the chain to an attached disk. Media are only consided
11910 * if they are:
11911 * - have only one child
11912 * - no references to any machines
11913 * - are of normal medium type
11914 */
11915 while (!pParent.isNull())
11916 {
11917 AutoCaller mac1(pParent);
11918 if (FAILED(mac1.hrc())) return mac1.hrc();
11919 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11920 if (pParent->i_getChildren().size() == 1)
11921 {
11922 if ( pParent->i_getMachineBackRefCount() == 0
11923 && pParent->i_getType() == MediumType_Normal
11924 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11925 llMedia.push_back(pParent);
11926 }
11927 else
11928 break;
11929 pParent = pParent->i_getParent();
11930 }
11931 }
11932 }
11933
11934 // real machine: then we need to use the proper method
11935 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11936
11937 if (FAILED(hrc))
11938 return hrc;
11939 }
11940
11941 return S_OK;
11942}
11943
11944/**
11945 * Perform deferred hard disk detachments.
11946 *
11947 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11948 * changed (not backed up).
11949 *
11950 * If @a aOnline is @c true then this method will also unlock the old hard
11951 * disks for which the new implicit diffs were created and will lock these new
11952 * diffs for writing.
11953 *
11954 * @param aOnline Whether the VM was online prior to this operation.
11955 *
11956 * @note Locks this object for writing!
11957 */
11958void Machine::i_commitMedia(bool aOnline /*= false*/)
11959{
11960 AutoCaller autoCaller(this);
11961 AssertComRCReturnVoid(autoCaller.hrc());
11962
11963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11964
11965 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11966
11967 HRESULT hrc = S_OK;
11968
11969 /* no attach/detach operations -- nothing to do */
11970 if (!mMediumAttachments.isBackedUp())
11971 return;
11972
11973 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11974 bool fMediaNeedsLocking = false;
11975
11976 /* enumerate new attachments */
11977 for (MediumAttachmentList::const_iterator
11978 it = mMediumAttachments->begin();
11979 it != mMediumAttachments->end();
11980 ++it)
11981 {
11982 MediumAttachment *pAttach = *it;
11983
11984 pAttach->i_commit();
11985
11986 Medium *pMedium = pAttach->i_getMedium();
11987 bool fImplicit = pAttach->i_isImplicit();
11988
11989 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11990 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11991 fImplicit));
11992
11993 /** @todo convert all this Machine-based voodoo to MediumAttachment
11994 * based commit logic. */
11995 if (fImplicit)
11996 {
11997 /* convert implicit attachment to normal */
11998 pAttach->i_setImplicit(false);
11999
12000 if ( aOnline
12001 && pMedium
12002 && pAttach->i_getType() == DeviceType_HardDisk
12003 )
12004 {
12005 /* update the appropriate lock list */
12006 MediumLockList *pMediumLockList;
12007 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12008 AssertComRC(hrc);
12009 if (pMediumLockList)
12010 {
12011 /* unlock if there's a need to change the locking */
12012 if (!fMediaNeedsLocking)
12013 {
12014 Assert(mData->mSession.mLockedMedia.IsLocked());
12015 hrc = mData->mSession.mLockedMedia.Unlock();
12016 AssertComRC(hrc);
12017 fMediaNeedsLocking = true;
12018 }
12019 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
12020 AssertComRC(hrc);
12021 hrc = pMediumLockList->Append(pMedium, true);
12022 AssertComRC(hrc);
12023 }
12024 }
12025
12026 continue;
12027 }
12028
12029 if (pMedium)
12030 {
12031 /* was this medium attached before? */
12032 for (MediumAttachmentList::iterator
12033 oldIt = oldAtts.begin();
12034 oldIt != oldAtts.end();
12035 ++oldIt)
12036 {
12037 MediumAttachment *pOldAttach = *oldIt;
12038 if (pOldAttach->i_getMedium() == pMedium)
12039 {
12040 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12041
12042 /* yes: remove from old to avoid de-association */
12043 oldAtts.erase(oldIt);
12044 break;
12045 }
12046 }
12047 }
12048 }
12049
12050 /* enumerate remaining old attachments and de-associate from the
12051 * current machine state */
12052 for (MediumAttachmentList::const_iterator
12053 it = oldAtts.begin();
12054 it != oldAtts.end();
12055 ++it)
12056 {
12057 MediumAttachment *pAttach = *it;
12058 Medium *pMedium = pAttach->i_getMedium();
12059
12060 /* Detach only hard disks, since DVD/floppy media is detached
12061 * instantly in MountMedium. */
12062 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12063 {
12064 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12065
12066 /* now de-associate from the current machine state */
12067 hrc = pMedium->i_removeBackReference(mData->mUuid);
12068 AssertComRC(hrc);
12069
12070 if (aOnline)
12071 {
12072 /* unlock since medium is not used anymore */
12073 MediumLockList *pMediumLockList;
12074 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12075 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
12076 {
12077 /* this happens for online snapshots, there the attachment
12078 * is changing, but only to a diff image created under
12079 * the old one, so there is no separate lock list */
12080 Assert(!pMediumLockList);
12081 }
12082 else
12083 {
12084 AssertComRC(hrc);
12085 if (pMediumLockList)
12086 {
12087 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
12088 AssertComRC(hrc);
12089 }
12090 }
12091 }
12092 }
12093 }
12094
12095 /* take media locks again so that the locking state is consistent */
12096 if (fMediaNeedsLocking)
12097 {
12098 Assert(aOnline);
12099 hrc = mData->mSession.mLockedMedia.Lock();
12100 AssertComRC(hrc);
12101 }
12102
12103 /* commit the hard disk changes */
12104 mMediumAttachments.commit();
12105
12106 if (i_isSessionMachine())
12107 {
12108 /*
12109 * Update the parent machine to point to the new owner.
12110 * This is necessary because the stored parent will point to the
12111 * session machine otherwise and cause crashes or errors later
12112 * when the session machine gets invalid.
12113 */
12114 /** @todo Change the MediumAttachment class to behave like any other
12115 * class in this regard by creating peer MediumAttachment
12116 * objects for session machines and share the data with the peer
12117 * machine.
12118 */
12119 for (MediumAttachmentList::const_iterator
12120 it = mMediumAttachments->begin();
12121 it != mMediumAttachments->end();
12122 ++it)
12123 (*it)->i_updateParentMachine(mPeer);
12124
12125 /* attach new data to the primary machine and reshare it */
12126 mPeer->mMediumAttachments.attach(mMediumAttachments);
12127 }
12128
12129 return;
12130}
12131
12132/**
12133 * Perform deferred deletion of implicitly created diffs.
12134 *
12135 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12136 * changed (not backed up).
12137 *
12138 * @note Locks this object for writing!
12139 */
12140void Machine::i_rollbackMedia()
12141{
12142 AutoCaller autoCaller(this);
12143 AssertComRCReturnVoid(autoCaller.hrc());
12144
12145 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12146 LogFlowThisFunc(("Entering rollbackMedia\n"));
12147
12148 HRESULT hrc = S_OK;
12149
12150 /* no attach/detach operations -- nothing to do */
12151 if (!mMediumAttachments.isBackedUp())
12152 return;
12153
12154 /* enumerate new attachments */
12155 for (MediumAttachmentList::const_iterator
12156 it = mMediumAttachments->begin();
12157 it != mMediumAttachments->end();
12158 ++it)
12159 {
12160 MediumAttachment *pAttach = *it;
12161 /* Fix up the backrefs for DVD/floppy media. */
12162 if (pAttach->i_getType() != DeviceType_HardDisk)
12163 {
12164 Medium *pMedium = pAttach->i_getMedium();
12165 if (pMedium)
12166 {
12167 hrc = pMedium->i_removeBackReference(mData->mUuid);
12168 AssertComRC(hrc);
12169 }
12170 }
12171
12172 (*it)->i_rollback();
12173
12174 pAttach = *it;
12175 /* Fix up the backrefs for DVD/floppy media. */
12176 if (pAttach->i_getType() != DeviceType_HardDisk)
12177 {
12178 Medium *pMedium = pAttach->i_getMedium();
12179 if (pMedium)
12180 {
12181 hrc = pMedium->i_addBackReference(mData->mUuid);
12182 AssertComRC(hrc);
12183 }
12184 }
12185 }
12186
12187 /** @todo convert all this Machine-based voodoo to MediumAttachment
12188 * based rollback logic. */
12189 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12190
12191 return;
12192}
12193
12194/**
12195 * Returns true if the settings file is located in the directory named exactly
12196 * as the machine; this means, among other things, that the machine directory
12197 * should be auto-renamed.
12198 *
12199 * @param aSettingsDir if not NULL, the full machine settings file directory
12200 * name will be assigned there.
12201 *
12202 * @note Doesn't lock anything.
12203 * @note Not thread safe (must be called from this object's lock).
12204 */
12205bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12206{
12207 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12208 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12209 if (aSettingsDir)
12210 *aSettingsDir = strMachineDirName;
12211 strMachineDirName.stripPath(); // vmname
12212 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12213 strConfigFileOnly.stripPath() // vmname.vbox
12214 .stripSuffix(); // vmname
12215 /** @todo hack, make somehow use of ComposeMachineFilename */
12216 if (mUserData->s.fDirectoryIncludesUUID)
12217 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12218
12219 AssertReturn(!strMachineDirName.isEmpty(), false);
12220 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12221
12222 return strMachineDirName == strConfigFileOnly;
12223}
12224
12225/**
12226 * Discards all changes to machine settings.
12227 *
12228 * @param aNotify Whether to notify the direct session about changes or not.
12229 *
12230 * @note Locks objects for writing!
12231 */
12232void Machine::i_rollback(bool aNotify)
12233{
12234 AutoCaller autoCaller(this);
12235 AssertComRCReturn(autoCaller.hrc(), (void)0);
12236
12237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12238
12239 if (!mStorageControllers.isNull())
12240 {
12241 if (mStorageControllers.isBackedUp())
12242 {
12243 /* unitialize all new devices (absent in the backed up list). */
12244 StorageControllerList *backedList = mStorageControllers.backedUpData();
12245 for (StorageControllerList::const_iterator
12246 it = mStorageControllers->begin();
12247 it != mStorageControllers->end();
12248 ++it)
12249 {
12250 if ( std::find(backedList->begin(), backedList->end(), *it)
12251 == backedList->end()
12252 )
12253 {
12254 (*it)->uninit();
12255 }
12256 }
12257
12258 /* restore the list */
12259 mStorageControllers.rollback();
12260 }
12261
12262 /* rollback any changes to devices after restoring the list */
12263 if (mData->flModifications & IsModified_Storage)
12264 {
12265 for (StorageControllerList::const_iterator
12266 it = mStorageControllers->begin();
12267 it != mStorageControllers->end();
12268 ++it)
12269 {
12270 (*it)->i_rollback();
12271 }
12272 }
12273 }
12274
12275 if (!mUSBControllers.isNull())
12276 {
12277 if (mUSBControllers.isBackedUp())
12278 {
12279 /* unitialize all new devices (absent in the backed up list). */
12280 USBControllerList *backedList = mUSBControllers.backedUpData();
12281 for (USBControllerList::const_iterator
12282 it = mUSBControllers->begin();
12283 it != mUSBControllers->end();
12284 ++it)
12285 {
12286 if ( std::find(backedList->begin(), backedList->end(), *it)
12287 == backedList->end()
12288 )
12289 {
12290 (*it)->uninit();
12291 }
12292 }
12293
12294 /* restore the list */
12295 mUSBControllers.rollback();
12296 }
12297
12298 /* rollback any changes to devices after restoring the list */
12299 if (mData->flModifications & IsModified_USB)
12300 {
12301 for (USBControllerList::const_iterator
12302 it = mUSBControllers->begin();
12303 it != mUSBControllers->end();
12304 ++it)
12305 {
12306 (*it)->i_rollback();
12307 }
12308 }
12309 }
12310
12311 mUserData.rollback();
12312
12313 mHWData.rollback();
12314
12315 if (mData->flModifications & IsModified_Storage)
12316 i_rollbackMedia();
12317
12318 if (mBIOSSettings)
12319 mBIOSSettings->i_rollback();
12320
12321 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12322 mRecordingSettings->i_rollback();
12323
12324 if (mTrustedPlatformModule)
12325 mTrustedPlatformModule->i_rollback();
12326
12327 if (mNvramStore)
12328 mNvramStore->i_rollback();
12329
12330 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12331 mGraphicsAdapter->i_rollback();
12332
12333 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12334 mVRDEServer->i_rollback();
12335
12336 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12337 mAudioSettings->i_rollback();
12338
12339 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12340 mUSBDeviceFilters->i_rollback();
12341
12342 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12343 mBandwidthControl->i_rollback();
12344
12345 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12346 mGuestDebugControl->i_rollback();
12347
12348 if (!mHWData.isNull())
12349 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12350 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12351 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12352 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12353
12354 if (mData->flModifications & IsModified_NetworkAdapters)
12355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12356 if ( mNetworkAdapters[slot]
12357 && mNetworkAdapters[slot]->i_isModified())
12358 {
12359 mNetworkAdapters[slot]->i_rollback();
12360 networkAdapters[slot] = mNetworkAdapters[slot];
12361 }
12362
12363 if (mData->flModifications & IsModified_SerialPorts)
12364 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12365 if ( mSerialPorts[slot]
12366 && mSerialPorts[slot]->i_isModified())
12367 {
12368 mSerialPorts[slot]->i_rollback();
12369 serialPorts[slot] = mSerialPorts[slot];
12370 }
12371
12372 if (mData->flModifications & IsModified_ParallelPorts)
12373 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12374 if ( mParallelPorts[slot]
12375 && mParallelPorts[slot]->i_isModified())
12376 {
12377 mParallelPorts[slot]->i_rollback();
12378 parallelPorts[slot] = mParallelPorts[slot];
12379 }
12380
12381 if (aNotify)
12382 {
12383 /* inform the direct session about changes */
12384
12385 ComObjPtr<Machine> that = this;
12386 uint32_t flModifications = mData->flModifications;
12387 alock.release();
12388
12389 if (flModifications & IsModified_SharedFolders)
12390 that->i_onSharedFolderChange();
12391
12392 if (flModifications & IsModified_VRDEServer)
12393 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12394 if (flModifications & IsModified_USB)
12395 that->i_onUSBControllerChange();
12396
12397 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12398 if (networkAdapters[slot])
12399 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12400 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12401 if (serialPorts[slot])
12402 that->i_onSerialPortChange(serialPorts[slot]);
12403 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12404 if (parallelPorts[slot])
12405 that->i_onParallelPortChange(parallelPorts[slot]);
12406
12407 if (flModifications & IsModified_Storage)
12408 {
12409 for (StorageControllerList::const_iterator
12410 it = mStorageControllers->begin();
12411 it != mStorageControllers->end();
12412 ++it)
12413 {
12414 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12415 }
12416 }
12417
12418 if (flModifications & IsModified_GuestDebugControl)
12419 that->i_onGuestDebugControlChange(mGuestDebugControl);
12420
12421#if 0
12422 if (flModifications & IsModified_BandwidthControl)
12423 that->onBandwidthControlChange();
12424#endif
12425 }
12426}
12427
12428/**
12429 * Commits all the changes to machine settings.
12430 *
12431 * Note that this operation is supposed to never fail.
12432 *
12433 * @note Locks this object and children for writing.
12434 */
12435void Machine::i_commit()
12436{
12437 AutoCaller autoCaller(this);
12438 AssertComRCReturnVoid(autoCaller.hrc());
12439
12440 AutoCaller peerCaller(mPeer);
12441 AssertComRCReturnVoid(peerCaller.hrc());
12442
12443 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12444
12445 /*
12446 * use safe commit to ensure Snapshot machines (that share mUserData)
12447 * will still refer to a valid memory location
12448 */
12449 mUserData.commitCopy();
12450
12451 mHWData.commit();
12452
12453 if (mMediumAttachments.isBackedUp())
12454 i_commitMedia(Global::IsOnline(mData->mMachineState));
12455
12456 mBIOSSettings->i_commit();
12457 mRecordingSettings->i_commit();
12458 mTrustedPlatformModule->i_commit();
12459 mNvramStore->i_commit();
12460 mGraphicsAdapter->i_commit();
12461 mVRDEServer->i_commit();
12462 mAudioSettings->i_commit();
12463 mUSBDeviceFilters->i_commit();
12464 mBandwidthControl->i_commit();
12465 mGuestDebugControl->i_commit();
12466
12467 /* Since mNetworkAdapters is a list which might have been changed (resized)
12468 * without using the Backupable<> template we need to handle the copying
12469 * of the list entries manually, including the creation of peers for the
12470 * new objects. */
12471 bool commitNetworkAdapters = false;
12472 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12473 if (mPeer)
12474 {
12475 /* commit everything, even the ones which will go away */
12476 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12477 mNetworkAdapters[slot]->i_commit();
12478 /* copy over the new entries, creating a peer and uninit the original */
12479 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12480 for (size_t slot = 0; slot < newSize; slot++)
12481 {
12482 /* look if this adapter has a peer device */
12483 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12484 if (!peer)
12485 {
12486 /* no peer means the adapter is a newly created one;
12487 * create a peer owning data this data share it with */
12488 peer.createObject();
12489 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12490 }
12491 mPeer->mNetworkAdapters[slot] = peer;
12492 }
12493 /* uninit any no longer needed network adapters */
12494 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12495 mNetworkAdapters[slot]->uninit();
12496 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12497 {
12498 if (mPeer->mNetworkAdapters[slot])
12499 mPeer->mNetworkAdapters[slot]->uninit();
12500 }
12501 /* Keep the original network adapter count until this point, so that
12502 * discarding a chipset type change will not lose settings. */
12503 mNetworkAdapters.resize(newSize);
12504 mPeer->mNetworkAdapters.resize(newSize);
12505 }
12506 else
12507 {
12508 /* we have no peer (our parent is the newly created machine);
12509 * just commit changes to the network adapters */
12510 commitNetworkAdapters = true;
12511 }
12512 if (commitNetworkAdapters)
12513 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12514 mNetworkAdapters[slot]->i_commit();
12515
12516 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12517 mSerialPorts[slot]->i_commit();
12518 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12519 mParallelPorts[slot]->i_commit();
12520
12521 bool commitStorageControllers = false;
12522
12523 if (mStorageControllers.isBackedUp())
12524 {
12525 mStorageControllers.commit();
12526
12527 if (mPeer)
12528 {
12529 /* Commit all changes to new controllers (this will reshare data with
12530 * peers for those who have peers) */
12531 StorageControllerList *newList = new StorageControllerList();
12532 for (StorageControllerList::const_iterator
12533 it = mStorageControllers->begin();
12534 it != mStorageControllers->end();
12535 ++it)
12536 {
12537 (*it)->i_commit();
12538
12539 /* look if this controller has a peer device */
12540 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12541 if (!peer)
12542 {
12543 /* no peer means the device is a newly created one;
12544 * create a peer owning data this device share it with */
12545 peer.createObject();
12546 peer->init(mPeer, *it, true /* aReshare */);
12547 }
12548 else
12549 {
12550 /* remove peer from the old list */
12551 mPeer->mStorageControllers->remove(peer);
12552 }
12553 /* and add it to the new list */
12554 newList->push_back(peer);
12555 }
12556
12557 /* uninit old peer's controllers that are left */
12558 for (StorageControllerList::const_iterator
12559 it = mPeer->mStorageControllers->begin();
12560 it != mPeer->mStorageControllers->end();
12561 ++it)
12562 {
12563 (*it)->uninit();
12564 }
12565
12566 /* attach new list of controllers to our peer */
12567 mPeer->mStorageControllers.attach(newList);
12568 }
12569 else
12570 {
12571 /* we have no peer (our parent is the newly created machine);
12572 * just commit changes to devices */
12573 commitStorageControllers = true;
12574 }
12575 }
12576 else
12577 {
12578 /* the list of controllers itself is not changed,
12579 * just commit changes to controllers themselves */
12580 commitStorageControllers = true;
12581 }
12582
12583 if (commitStorageControllers)
12584 {
12585 for (StorageControllerList::const_iterator
12586 it = mStorageControllers->begin();
12587 it != mStorageControllers->end();
12588 ++it)
12589 {
12590 (*it)->i_commit();
12591 }
12592 }
12593
12594 bool commitUSBControllers = false;
12595
12596 if (mUSBControllers.isBackedUp())
12597 {
12598 mUSBControllers.commit();
12599
12600 if (mPeer)
12601 {
12602 /* Commit all changes to new controllers (this will reshare data with
12603 * peers for those who have peers) */
12604 USBControllerList *newList = new USBControllerList();
12605 for (USBControllerList::const_iterator
12606 it = mUSBControllers->begin();
12607 it != mUSBControllers->end();
12608 ++it)
12609 {
12610 (*it)->i_commit();
12611
12612 /* look if this controller has a peer device */
12613 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12614 if (!peer)
12615 {
12616 /* no peer means the device is a newly created one;
12617 * create a peer owning data this device share it with */
12618 peer.createObject();
12619 peer->init(mPeer, *it, true /* aReshare */);
12620 }
12621 else
12622 {
12623 /* remove peer from the old list */
12624 mPeer->mUSBControllers->remove(peer);
12625 }
12626 /* and add it to the new list */
12627 newList->push_back(peer);
12628 }
12629
12630 /* uninit old peer's controllers that are left */
12631 for (USBControllerList::const_iterator
12632 it = mPeer->mUSBControllers->begin();
12633 it != mPeer->mUSBControllers->end();
12634 ++it)
12635 {
12636 (*it)->uninit();
12637 }
12638
12639 /* attach new list of controllers to our peer */
12640 mPeer->mUSBControllers.attach(newList);
12641 }
12642 else
12643 {
12644 /* we have no peer (our parent is the newly created machine);
12645 * just commit changes to devices */
12646 commitUSBControllers = true;
12647 }
12648 }
12649 else
12650 {
12651 /* the list of controllers itself is not changed,
12652 * just commit changes to controllers themselves */
12653 commitUSBControllers = true;
12654 }
12655
12656 if (commitUSBControllers)
12657 {
12658 for (USBControllerList::const_iterator
12659 it = mUSBControllers->begin();
12660 it != mUSBControllers->end();
12661 ++it)
12662 {
12663 (*it)->i_commit();
12664 }
12665 }
12666
12667 if (i_isSessionMachine())
12668 {
12669 /* attach new data to the primary machine and reshare it */
12670 mPeer->mUserData.attach(mUserData);
12671 mPeer->mHWData.attach(mHWData);
12672 /* mmMediumAttachments is reshared by fixupMedia */
12673 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12674 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12675 }
12676}
12677
12678/**
12679 * Copies all the hardware data from the given machine.
12680 *
12681 * Currently, only called when the VM is being restored from a snapshot. In
12682 * particular, this implies that the VM is not running during this method's
12683 * call.
12684 *
12685 * @note This method must be called from under this object's lock.
12686 *
12687 * @note This method doesn't call #i_commit(), so all data remains backed up and
12688 * unsaved.
12689 */
12690void Machine::i_copyFrom(Machine *aThat)
12691{
12692 AssertReturnVoid(!i_isSnapshotMachine());
12693 AssertReturnVoid(aThat->i_isSnapshotMachine());
12694
12695 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12696
12697 mHWData.assignCopy(aThat->mHWData);
12698
12699 // create copies of all shared folders (mHWData after attaching a copy
12700 // contains just references to original objects)
12701 for (HWData::SharedFolderList::iterator
12702 it = mHWData->mSharedFolders.begin();
12703 it != mHWData->mSharedFolders.end();
12704 ++it)
12705 {
12706 ComObjPtr<SharedFolder> folder;
12707 folder.createObject();
12708 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12709 AssertComRC(hrc);
12710 *it = folder;
12711 }
12712
12713 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12714 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12715 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12716 mNvramStore->i_copyFrom(aThat->mNvramStore);
12717 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12718 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12719 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12720 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12721 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12722 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12723
12724 /* create private copies of all controllers */
12725 mStorageControllers.backup();
12726 mStorageControllers->clear();
12727 for (StorageControllerList::const_iterator
12728 it = aThat->mStorageControllers->begin();
12729 it != aThat->mStorageControllers->end();
12730 ++it)
12731 {
12732 ComObjPtr<StorageController> ctrl;
12733 ctrl.createObject();
12734 ctrl->initCopy(this, *it);
12735 mStorageControllers->push_back(ctrl);
12736 }
12737
12738 /* create private copies of all USB controllers */
12739 mUSBControllers.backup();
12740 mUSBControllers->clear();
12741 for (USBControllerList::const_iterator
12742 it = aThat->mUSBControllers->begin();
12743 it != aThat->mUSBControllers->end();
12744 ++it)
12745 {
12746 ComObjPtr<USBController> ctrl;
12747 ctrl.createObject();
12748 ctrl->initCopy(this, *it);
12749 mUSBControllers->push_back(ctrl);
12750 }
12751
12752 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12753 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12754 {
12755 if (mNetworkAdapters[slot].isNotNull())
12756 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12757 else
12758 {
12759 unconst(mNetworkAdapters[slot]).createObject();
12760 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12761 }
12762 }
12763 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12764 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12766 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12767}
12768
12769/**
12770 * Returns whether the given storage controller is hotplug capable.
12771 *
12772 * @returns true if the controller supports hotplugging
12773 * false otherwise.
12774 * @param enmCtrlType The controller type to check for.
12775 */
12776bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12777{
12778 ComPtr<ISystemProperties> systemProperties;
12779 HRESULT hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12780 if (FAILED(hrc))
12781 return false;
12782
12783 BOOL aHotplugCapable = FALSE;
12784 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12785
12786 return RT_BOOL(aHotplugCapable);
12787}
12788
12789#ifdef VBOX_WITH_RESOURCE_USAGE_API
12790
12791void Machine::i_getDiskList(MediaList &list)
12792{
12793 for (MediumAttachmentList::const_iterator
12794 it = mMediumAttachments->begin();
12795 it != mMediumAttachments->end();
12796 ++it)
12797 {
12798 MediumAttachment *pAttach = *it;
12799 /* just in case */
12800 AssertContinue(pAttach);
12801
12802 AutoCaller localAutoCallerA(pAttach);
12803 if (FAILED(localAutoCallerA.hrc())) continue;
12804
12805 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12806
12807 if (pAttach->i_getType() == DeviceType_HardDisk)
12808 list.push_back(pAttach->i_getMedium());
12809 }
12810}
12811
12812void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12813{
12814 AssertReturnVoid(isWriteLockOnCurrentThread());
12815 AssertPtrReturnVoid(aCollector);
12816
12817 pm::CollectorHAL *hal = aCollector->getHAL();
12818 /* Create sub metrics */
12819 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12820 "Percentage of processor time spent in user mode by the VM process.");
12821 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12822 "Percentage of processor time spent in kernel mode by the VM process.");
12823 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12824 "Size of resident portion of VM process in memory.");
12825 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12826 "Actual size of all VM disks combined.");
12827 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12828 "Network receive rate.");
12829 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12830 "Network transmit rate.");
12831 /* Create and register base metrics */
12832 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12833 cpuLoadUser, cpuLoadKernel);
12834 aCollector->registerBaseMetric(cpuLoad);
12835 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12836 ramUsageUsed);
12837 aCollector->registerBaseMetric(ramUsage);
12838 MediaList disks;
12839 i_getDiskList(disks);
12840 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12841 diskUsageUsed);
12842 aCollector->registerBaseMetric(diskUsage);
12843
12844 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12845 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12846 new pm::AggregateAvg()));
12847 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12848 new pm::AggregateMin()));
12849 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12850 new pm::AggregateMax()));
12851 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12852 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12853 new pm::AggregateAvg()));
12854 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12855 new pm::AggregateMin()));
12856 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12857 new pm::AggregateMax()));
12858
12859 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12860 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12861 new pm::AggregateAvg()));
12862 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12863 new pm::AggregateMin()));
12864 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12865 new pm::AggregateMax()));
12866
12867 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12868 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12869 new pm::AggregateAvg()));
12870 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12871 new pm::AggregateMin()));
12872 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12873 new pm::AggregateMax()));
12874
12875
12876 /* Guest metrics collector */
12877 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12878 aCollector->registerGuest(mCollectorGuest);
12879 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12880
12881 /* Create sub metrics */
12882 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12883 "Percentage of processor time spent in user mode as seen by the guest.");
12884 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12885 "Percentage of processor time spent in kernel mode as seen by the guest.");
12886 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12887 "Percentage of processor time spent idling as seen by the guest.");
12888
12889 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12890 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12891 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12892 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12893 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12894 pm::SubMetric *guestMemCache = new pm::SubMetric(
12895 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12896
12897 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12898 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12899
12900 /* Create and register base metrics */
12901 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12902 machineNetRx, machineNetTx);
12903 aCollector->registerBaseMetric(machineNetRate);
12904
12905 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12906 guestLoadUser, guestLoadKernel, guestLoadIdle);
12907 aCollector->registerBaseMetric(guestCpuLoad);
12908
12909 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12910 guestMemTotal, guestMemFree,
12911 guestMemBalloon, guestMemShared,
12912 guestMemCache, guestPagedTotal);
12913 aCollector->registerBaseMetric(guestCpuMem);
12914
12915 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12916 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12917 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12918 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12919
12920 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12921 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12922 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12923 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12924
12925 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12926 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12927 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12928 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12929
12930 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12931 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12932 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12933 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12934
12935 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12936 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12937 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12938 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12939
12940 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12941 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12942 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12943 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12944
12945 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12946 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12947 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12948 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12949
12950 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12951 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12952 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12953 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12954
12955 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12956 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12957 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12958 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12959
12960 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12961 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12962 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12963 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12964
12965 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12966 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12967 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12968 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12969}
12970
12971void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12972{
12973 AssertReturnVoid(isWriteLockOnCurrentThread());
12974
12975 if (aCollector)
12976 {
12977 aCollector->unregisterMetricsFor(aMachine);
12978 aCollector->unregisterBaseMetricsFor(aMachine);
12979 }
12980}
12981
12982#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12983
12984
12985////////////////////////////////////////////////////////////////////////////////
12986
12987DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12988
12989HRESULT SessionMachine::FinalConstruct()
12990{
12991 LogFlowThisFunc(("\n"));
12992
12993 mClientToken = NULL;
12994
12995 return BaseFinalConstruct();
12996}
12997
12998void SessionMachine::FinalRelease()
12999{
13000 LogFlowThisFunc(("\n"));
13001
13002 Assert(!mClientToken);
13003 /* paranoia, should not hang around any more */
13004 if (mClientToken)
13005 {
13006 delete mClientToken;
13007 mClientToken = NULL;
13008 }
13009
13010 uninit(Uninit::Unexpected);
13011
13012 BaseFinalRelease();
13013}
13014
13015/**
13016 * @note Must be called only by Machine::LockMachine() from its own write lock.
13017 */
13018HRESULT SessionMachine::init(Machine *aMachine)
13019{
13020 LogFlowThisFuncEnter();
13021 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13022
13023 AssertReturn(aMachine, E_INVALIDARG);
13024
13025 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13026
13027 /* Enclose the state transition NotReady->InInit->Ready */
13028 AutoInitSpan autoInitSpan(this);
13029 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13030
13031 HRESULT hrc = S_OK;
13032
13033 RT_ZERO(mAuthLibCtx);
13034
13035 /* create the machine client token */
13036 try
13037 {
13038 mClientToken = new ClientToken(aMachine, this);
13039 if (!mClientToken->isReady())
13040 {
13041 delete mClientToken;
13042 mClientToken = NULL;
13043 hrc = E_FAIL;
13044 }
13045 }
13046 catch (std::bad_alloc &)
13047 {
13048 hrc = E_OUTOFMEMORY;
13049 }
13050 if (FAILED(hrc))
13051 return hrc;
13052
13053 /* memorize the peer Machine */
13054 unconst(mPeer) = aMachine;
13055 /* share the parent pointer */
13056 unconst(mParent) = aMachine->mParent;
13057
13058 /* take the pointers to data to share */
13059 mData.share(aMachine->mData);
13060 mSSData.share(aMachine->mSSData);
13061
13062 mUserData.share(aMachine->mUserData);
13063 mHWData.share(aMachine->mHWData);
13064 mMediumAttachments.share(aMachine->mMediumAttachments);
13065
13066 mStorageControllers.allocate();
13067 for (StorageControllerList::const_iterator
13068 it = aMachine->mStorageControllers->begin();
13069 it != aMachine->mStorageControllers->end();
13070 ++it)
13071 {
13072 ComObjPtr<StorageController> ctl;
13073 ctl.createObject();
13074 ctl->init(this, *it);
13075 mStorageControllers->push_back(ctl);
13076 }
13077
13078 mUSBControllers.allocate();
13079 for (USBControllerList::const_iterator
13080 it = aMachine->mUSBControllers->begin();
13081 it != aMachine->mUSBControllers->end();
13082 ++it)
13083 {
13084 ComObjPtr<USBController> ctl;
13085 ctl.createObject();
13086 ctl->init(this, *it);
13087 mUSBControllers->push_back(ctl);
13088 }
13089
13090 unconst(mBIOSSettings).createObject();
13091 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13092
13093 unconst(mRecordingSettings).createObject();
13094 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13095
13096 unconst(mTrustedPlatformModule).createObject();
13097 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13098
13099 unconst(mNvramStore).createObject();
13100 mNvramStore->init(this, aMachine->mNvramStore);
13101
13102 /* create another GraphicsAdapter object that will be mutable */
13103 unconst(mGraphicsAdapter).createObject();
13104 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13105 /* create another VRDEServer object that will be mutable */
13106 unconst(mVRDEServer).createObject();
13107 mVRDEServer->init(this, aMachine->mVRDEServer);
13108 /* create another audio settings object that will be mutable */
13109 unconst(mAudioSettings).createObject();
13110 mAudioSettings->init(this, aMachine->mAudioSettings);
13111 /* create a list of serial ports that will be mutable */
13112 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13113 {
13114 unconst(mSerialPorts[slot]).createObject();
13115 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13116 }
13117 /* create a list of parallel ports that will be mutable */
13118 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13119 {
13120 unconst(mParallelPorts[slot]).createObject();
13121 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13122 }
13123
13124 /* create another USB device filters object that will be mutable */
13125 unconst(mUSBDeviceFilters).createObject();
13126 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13127
13128 /* create a list of network adapters that will be mutable */
13129 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13130 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13131 {
13132 unconst(mNetworkAdapters[slot]).createObject();
13133 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13134 }
13135
13136 /* create another bandwidth control object that will be mutable */
13137 unconst(mBandwidthControl).createObject();
13138 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13139
13140 unconst(mGuestDebugControl).createObject();
13141 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13142
13143 /* default is to delete saved state on Saved -> PoweredOff transition */
13144 mRemoveSavedState = true;
13145
13146 /* Confirm a successful initialization when it's the case */
13147 autoInitSpan.setSucceeded();
13148
13149 miNATNetworksStarted = 0;
13150
13151 LogFlowThisFuncLeave();
13152 return hrc;
13153}
13154
13155/**
13156 * Uninitializes this session object. If the reason is other than
13157 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13158 * or the client watcher code.
13159 *
13160 * @param aReason uninitialization reason
13161 *
13162 * @note Locks mParent + this object for writing.
13163 */
13164void SessionMachine::uninit(Uninit::Reason aReason)
13165{
13166 LogFlowThisFuncEnter();
13167 LogFlowThisFunc(("reason=%d\n", aReason));
13168
13169 /*
13170 * Strongly reference ourselves to prevent this object deletion after
13171 * mData->mSession.mMachine.setNull() below (which can release the last
13172 * reference and call the destructor). Important: this must be done before
13173 * accessing any members (and before AutoUninitSpan that does it as well).
13174 * This self reference will be released as the very last step on return.
13175 */
13176 ComObjPtr<SessionMachine> selfRef;
13177 if (aReason != Uninit::Unexpected)
13178 selfRef = this;
13179
13180 /* Enclose the state transition Ready->InUninit->NotReady */
13181 AutoUninitSpan autoUninitSpan(this);
13182 if (autoUninitSpan.uninitDone())
13183 {
13184 LogFlowThisFunc(("Already uninitialized\n"));
13185 LogFlowThisFuncLeave();
13186 return;
13187 }
13188
13189 if (autoUninitSpan.initFailed())
13190 {
13191 /* We've been called by init() because it's failed. It's not really
13192 * necessary (nor it's safe) to perform the regular uninit sequence
13193 * below, the following is enough.
13194 */
13195 LogFlowThisFunc(("Initialization failed.\n"));
13196 /* destroy the machine client token */
13197 if (mClientToken)
13198 {
13199 delete mClientToken;
13200 mClientToken = NULL;
13201 }
13202 uninitDataAndChildObjects();
13203 mData.free();
13204 unconst(mParent) = NULL;
13205 unconst(mPeer) = NULL;
13206 LogFlowThisFuncLeave();
13207 return;
13208 }
13209
13210 MachineState_T lastState;
13211 {
13212 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13213 lastState = mData->mMachineState;
13214 }
13215 NOREF(lastState);
13216
13217#ifdef VBOX_WITH_USB
13218 // release all captured USB devices, but do this before requesting the locks below
13219 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13220 {
13221 /* Console::captureUSBDevices() is called in the VM process only after
13222 * setting the machine state to Starting or Restoring.
13223 * Console::detachAllUSBDevices() will be called upon successful
13224 * termination. So, we need to release USB devices only if there was
13225 * an abnormal termination of a running VM.
13226 *
13227 * This is identical to SessionMachine::DetachAllUSBDevices except
13228 * for the aAbnormal argument. */
13229 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13230 AssertComRC(hrc);
13231 NOREF(hrc);
13232
13233 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13234 if (service)
13235 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13236 }
13237#endif /* VBOX_WITH_USB */
13238
13239 // we need to lock this object in uninit() because the lock is shared
13240 // with mPeer (as well as data we modify below). mParent lock is needed
13241 // by several calls to it.
13242 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13243
13244#ifdef VBOX_WITH_RESOURCE_USAGE_API
13245 /*
13246 * It is safe to call Machine::i_unregisterMetrics() here because
13247 * PerformanceCollector::samplerCallback no longer accesses guest methods
13248 * holding the lock.
13249 */
13250 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13251 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13252 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13253 if (mCollectorGuest)
13254 {
13255 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13256 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13257 mCollectorGuest = NULL;
13258 }
13259#endif
13260
13261 if (aReason == Uninit::Abnormal)
13262 {
13263 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13264
13265 /*
13266 * Move the VM to the 'Aborted' machine state unless we are restoring a
13267 * VM that was in the 'Saved' machine state. In that case, if the VM
13268 * fails before reaching either the 'Restoring' machine state or the
13269 * 'Running' machine state then we set the machine state to
13270 * 'AbortedSaved' in order to preserve the saved state file so that the
13271 * VM can be restored in the future.
13272 */
13273 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13274 i_setMachineState(MachineState_AbortedSaved);
13275 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13276 i_setMachineState(MachineState_Aborted);
13277 }
13278
13279 // any machine settings modified?
13280 if (mData->flModifications)
13281 {
13282 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13283 i_rollback(false /* aNotify */);
13284 }
13285
13286 mData->mSession.mPID = NIL_RTPROCESS;
13287
13288 if (aReason == Uninit::Unexpected)
13289 {
13290 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13291 * client watcher thread to update the set of machines that have open
13292 * sessions. */
13293 mParent->i_updateClientWatcher();
13294 }
13295
13296 /* uninitialize all remote controls */
13297 if (mData->mSession.mRemoteControls.size())
13298 {
13299 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13300 mData->mSession.mRemoteControls.size()));
13301
13302 /* Always restart a the beginning, since the iterator is invalidated
13303 * by using erase(). */
13304 for (Data::Session::RemoteControlList::iterator
13305 it = mData->mSession.mRemoteControls.begin();
13306 it != mData->mSession.mRemoteControls.end();
13307 it = mData->mSession.mRemoteControls.begin())
13308 {
13309 ComPtr<IInternalSessionControl> pControl = *it;
13310 mData->mSession.mRemoteControls.erase(it);
13311 multilock.release();
13312 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13313 HRESULT hrc = pControl->Uninitialize();
13314 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
13315 if (FAILED(hrc))
13316 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13317 multilock.acquire();
13318 }
13319 mData->mSession.mRemoteControls.clear();
13320 }
13321
13322 /* Remove all references to the NAT network service. The service will stop
13323 * if all references (also from other VMs) are removed. */
13324 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13325 {
13326 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13327 {
13328 BOOL enabled;
13329 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13330 if ( FAILED(hrc)
13331 || !enabled)
13332 continue;
13333
13334 NetworkAttachmentType_T type;
13335 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13336 if ( SUCCEEDED(hrc)
13337 && type == NetworkAttachmentType_NATNetwork)
13338 {
13339 Bstr name;
13340 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13341 if (SUCCEEDED(hrc))
13342 {
13343 multilock.release();
13344 Utf8Str strName(name);
13345 LogRel(("VM '%s' stops using NAT network '%s'\n",
13346 mUserData->s.strName.c_str(), strName.c_str()));
13347 mParent->i_natNetworkRefDec(strName);
13348 multilock.acquire();
13349 }
13350 }
13351 }
13352 }
13353
13354 /*
13355 * An expected uninitialization can come only from #i_checkForDeath().
13356 * Otherwise it means that something's gone really wrong (for example,
13357 * the Session implementation has released the VirtualBox reference
13358 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13359 * etc). However, it's also possible, that the client releases the IPC
13360 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13361 * but the VirtualBox release event comes first to the server process.
13362 * This case is practically possible, so we should not assert on an
13363 * unexpected uninit, just log a warning.
13364 */
13365
13366 if (aReason == Uninit::Unexpected)
13367 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13368
13369 if (aReason != Uninit::Normal)
13370 {
13371 mData->mSession.mDirectControl.setNull();
13372 }
13373 else
13374 {
13375 /* this must be null here (see #OnSessionEnd()) */
13376 Assert(mData->mSession.mDirectControl.isNull());
13377 Assert(mData->mSession.mState == SessionState_Unlocking);
13378 Assert(!mData->mSession.mProgress.isNull());
13379 }
13380 if (mData->mSession.mProgress)
13381 {
13382 if (aReason == Uninit::Normal)
13383 mData->mSession.mProgress->i_notifyComplete(S_OK);
13384 else
13385 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13386 COM_IIDOF(ISession),
13387 getComponentName(),
13388 tr("The VM session was aborted"));
13389 mData->mSession.mProgress.setNull();
13390 }
13391
13392 if (mConsoleTaskData.mProgress)
13393 {
13394 Assert(aReason == Uninit::Abnormal);
13395 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13396 COM_IIDOF(ISession),
13397 getComponentName(),
13398 tr("The VM session was aborted"));
13399 mConsoleTaskData.mProgress.setNull();
13400 }
13401
13402 /* remove the association between the peer machine and this session machine */
13403 Assert( (SessionMachine*)mData->mSession.mMachine == this
13404 || aReason == Uninit::Unexpected);
13405
13406 /* reset the rest of session data */
13407 mData->mSession.mLockType = LockType_Null;
13408 mData->mSession.mMachine.setNull();
13409 mData->mSession.mState = SessionState_Unlocked;
13410 mData->mSession.mName.setNull();
13411
13412 /* destroy the machine client token before leaving the exclusive lock */
13413 if (mClientToken)
13414 {
13415 delete mClientToken;
13416 mClientToken = NULL;
13417 }
13418
13419 /* fire an event */
13420 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13421
13422 uninitDataAndChildObjects();
13423
13424 /* free the essential data structure last */
13425 mData.free();
13426
13427 /* release the exclusive lock before setting the below two to NULL */
13428 multilock.release();
13429
13430 unconst(mParent) = NULL;
13431 unconst(mPeer) = NULL;
13432
13433 AuthLibUnload(&mAuthLibCtx);
13434
13435 LogFlowThisFuncLeave();
13436}
13437
13438// util::Lockable interface
13439////////////////////////////////////////////////////////////////////////////////
13440
13441/**
13442 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13443 * with the primary Machine instance (mPeer).
13444 */
13445RWLockHandle *SessionMachine::lockHandle() const
13446{
13447 AssertReturn(mPeer != NULL, NULL);
13448 return mPeer->lockHandle();
13449}
13450
13451// IInternalMachineControl methods
13452////////////////////////////////////////////////////////////////////////////////
13453
13454/**
13455 * Passes collected guest statistics to performance collector object
13456 */
13457HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13458 ULONG aCpuKernel, ULONG aCpuIdle,
13459 ULONG aMemTotal, ULONG aMemFree,
13460 ULONG aMemBalloon, ULONG aMemShared,
13461 ULONG aMemCache, ULONG aPageTotal,
13462 ULONG aAllocVMM, ULONG aFreeVMM,
13463 ULONG aBalloonedVMM, ULONG aSharedVMM,
13464 ULONG aVmNetRx, ULONG aVmNetTx)
13465{
13466#ifdef VBOX_WITH_RESOURCE_USAGE_API
13467 if (mCollectorGuest)
13468 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13469 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13470 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13471 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13472
13473 return S_OK;
13474#else
13475 NOREF(aValidStats);
13476 NOREF(aCpuUser);
13477 NOREF(aCpuKernel);
13478 NOREF(aCpuIdle);
13479 NOREF(aMemTotal);
13480 NOREF(aMemFree);
13481 NOREF(aMemBalloon);
13482 NOREF(aMemShared);
13483 NOREF(aMemCache);
13484 NOREF(aPageTotal);
13485 NOREF(aAllocVMM);
13486 NOREF(aFreeVMM);
13487 NOREF(aBalloonedVMM);
13488 NOREF(aSharedVMM);
13489 NOREF(aVmNetRx);
13490 NOREF(aVmNetTx);
13491 return E_NOTIMPL;
13492#endif
13493}
13494
13495////////////////////////////////////////////////////////////////////////////////
13496//
13497// SessionMachine task records
13498//
13499////////////////////////////////////////////////////////////////////////////////
13500
13501/**
13502 * Task record for saving the machine state.
13503 */
13504class SessionMachine::SaveStateTask
13505 : public Machine::Task
13506{
13507public:
13508 SaveStateTask(SessionMachine *m,
13509 Progress *p,
13510 const Utf8Str &t,
13511 Reason_T enmReason,
13512 const Utf8Str &strStateFilePath)
13513 : Task(m, p, t),
13514 m_enmReason(enmReason),
13515 m_strStateFilePath(strStateFilePath)
13516 {}
13517
13518private:
13519 void handler()
13520 {
13521 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13522 }
13523
13524 Reason_T m_enmReason;
13525 Utf8Str m_strStateFilePath;
13526
13527 friend class SessionMachine;
13528};
13529
13530/**
13531 * Task thread implementation for SessionMachine::SaveState(), called from
13532 * SessionMachine::taskHandler().
13533 *
13534 * @note Locks this object for writing.
13535 *
13536 * @param task
13537 */
13538void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13539{
13540 LogFlowThisFuncEnter();
13541
13542 AutoCaller autoCaller(this);
13543 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13544 if (FAILED(autoCaller.hrc()))
13545 {
13546 /* we might have been uninitialized because the session was accidentally
13547 * closed by the client, so don't assert */
13548 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
13549 task.m_pProgress->i_notifyComplete(hrc);
13550 LogFlowThisFuncLeave();
13551 return;
13552 }
13553
13554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13555
13556 HRESULT hrc = S_OK;
13557
13558 try
13559 {
13560 ComPtr<IInternalSessionControl> directControl;
13561 if (mData->mSession.mLockType == LockType_VM)
13562 directControl = mData->mSession.mDirectControl;
13563 if (directControl.isNull())
13564 throw setError(VBOX_E_INVALID_VM_STATE,
13565 tr("Trying to save state without a running VM"));
13566 alock.release();
13567 BOOL fSuspendedBySave;
13568 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13569 Assert(!fSuspendedBySave);
13570 alock.acquire();
13571
13572 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
13573 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
13574 throw E_FAIL);
13575
13576 if (SUCCEEDED(hrc))
13577 {
13578 mSSData->strStateFilePath = task.m_strStateFilePath;
13579
13580 /* save all VM settings */
13581 hrc = i_saveSettings(NULL, alock);
13582 // no need to check whether VirtualBox.xml needs saving also since
13583 // we can't have a name change pending at this point
13584 }
13585 else
13586 {
13587 // On failure, set the state to the state we had at the beginning.
13588 i_setMachineState(task.m_machineStateBackup);
13589 i_updateMachineStateOnClient();
13590
13591 // Delete the saved state file (might have been already created).
13592 // No need to check whether this is shared with a snapshot here
13593 // because we certainly created a fresh saved state file here.
13594 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13595 }
13596 }
13597 catch (HRESULT hrcXcpt)
13598 {
13599 hrc = hrcXcpt;
13600 }
13601
13602 task.m_pProgress->i_notifyComplete(hrc);
13603
13604 LogFlowThisFuncLeave();
13605}
13606
13607/**
13608 * @note Locks this object for writing.
13609 */
13610HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13611{
13612 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13613}
13614
13615HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13616{
13617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13618
13619 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13620 if (FAILED(hrc)) return hrc;
13621
13622 if ( mData->mMachineState != MachineState_Running
13623 && mData->mMachineState != MachineState_Paused
13624 )
13625 return setError(VBOX_E_INVALID_VM_STATE,
13626 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13627 Global::stringifyMachineState(mData->mMachineState));
13628
13629 ComObjPtr<Progress> pProgress;
13630 pProgress.createObject();
13631 hrc = pProgress->init(i_getVirtualBox(),
13632 static_cast<IMachine *>(this) /* aInitiator */,
13633 tr("Saving the execution state of the virtual machine"),
13634 FALSE /* aCancelable */);
13635 if (FAILED(hrc))
13636 return hrc;
13637
13638 Utf8Str strStateFilePath;
13639 i_composeSavedStateFilename(strStateFilePath);
13640
13641 /* create and start the task on a separate thread (note that it will not
13642 * start working until we release alock) */
13643 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13644 hrc = pTask->createThread();
13645 if (FAILED(hrc))
13646 return hrc;
13647
13648 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13649 i_setMachineState(MachineState_Saving);
13650 i_updateMachineStateOnClient();
13651
13652 pProgress.queryInterfaceTo(aProgress.asOutParam());
13653
13654 return S_OK;
13655}
13656
13657/**
13658 * @note Locks this object for writing.
13659 */
13660HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13661{
13662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13663
13664 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13665 if (FAILED(hrc)) return hrc;
13666
13667 if ( mData->mMachineState != MachineState_PoweredOff
13668 && mData->mMachineState != MachineState_Teleported
13669 && mData->mMachineState != MachineState_Aborted
13670 )
13671 return setError(VBOX_E_INVALID_VM_STATE,
13672 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13673 Global::stringifyMachineState(mData->mMachineState));
13674
13675 com::Utf8Str stateFilePathFull;
13676 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13677 if (RT_FAILURE(vrc))
13678 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13679 tr("Invalid saved state file path '%s' (%Rrc)"),
13680 aSavedStateFile.c_str(),
13681 vrc);
13682
13683 mSSData->strStateFilePath = stateFilePathFull;
13684
13685 /* The below i_setMachineState() will detect the state transition and will
13686 * update the settings file */
13687
13688 return i_setMachineState(MachineState_Saved);
13689}
13690
13691/**
13692 * @note Locks this object for writing.
13693 */
13694HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13695{
13696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13697
13698 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13699 if (FAILED(hrc)) return hrc;
13700
13701 if ( mData->mMachineState != MachineState_Saved
13702 && mData->mMachineState != MachineState_AbortedSaved)
13703 return setError(VBOX_E_INVALID_VM_STATE,
13704 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13705 Global::stringifyMachineState(mData->mMachineState));
13706
13707 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13708
13709 /*
13710 * Saved -> PoweredOff transition will be detected in the SessionMachine
13711 * and properly handled.
13712 */
13713 hrc = i_setMachineState(MachineState_PoweredOff);
13714 return hrc;
13715}
13716
13717
13718/**
13719 * @note Locks the same as #i_setMachineState() does.
13720 */
13721HRESULT SessionMachine::updateState(MachineState_T aState)
13722{
13723 return i_setMachineState(aState);
13724}
13725
13726/**
13727 * @note Locks this object for writing.
13728 */
13729HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13730{
13731 IProgress *pProgress(aProgress);
13732
13733 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13734
13735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13736
13737 if (mData->mSession.mState != SessionState_Locked)
13738 return VBOX_E_INVALID_OBJECT_STATE;
13739
13740 if (!mData->mSession.mProgress.isNull())
13741 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13742
13743 /* If we didn't reference the NAT network service yet, add a reference to
13744 * force a start */
13745 if (miNATNetworksStarted < 1)
13746 {
13747 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13748 {
13749 BOOL enabled;
13750 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13751 if ( FAILED(hrc)
13752 || !enabled)
13753 continue;
13754
13755 NetworkAttachmentType_T type;
13756 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13757 if ( SUCCEEDED(hrc)
13758 && type == NetworkAttachmentType_NATNetwork)
13759 {
13760 Bstr name;
13761 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13762 if (SUCCEEDED(hrc))
13763 {
13764 Utf8Str strName(name);
13765 LogRel(("VM '%s' starts using NAT network '%s'\n",
13766 mUserData->s.strName.c_str(), strName.c_str()));
13767 mPeer->lockHandle()->unlockWrite();
13768 mParent->i_natNetworkRefInc(strName);
13769#ifdef RT_LOCK_STRICT
13770 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13771#else
13772 mPeer->lockHandle()->lockWrite();
13773#endif
13774 }
13775 }
13776 }
13777 miNATNetworksStarted++;
13778 }
13779
13780 LogFlowThisFunc(("returns S_OK.\n"));
13781 return S_OK;
13782}
13783
13784/**
13785 * @note Locks this object for writing.
13786 */
13787HRESULT SessionMachine::endPowerUp(LONG aResult)
13788{
13789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13790
13791 if (mData->mSession.mState != SessionState_Locked)
13792 return VBOX_E_INVALID_OBJECT_STATE;
13793
13794 /* Finalize the LaunchVMProcess progress object. */
13795 if (mData->mSession.mProgress)
13796 {
13797 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13798 mData->mSession.mProgress.setNull();
13799 }
13800
13801 if (SUCCEEDED((HRESULT)aResult))
13802 {
13803#ifdef VBOX_WITH_RESOURCE_USAGE_API
13804 /* The VM has been powered up successfully, so it makes sense
13805 * now to offer the performance metrics for a running machine
13806 * object. Doing it earlier wouldn't be safe. */
13807 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13808 mData->mSession.mPID);
13809#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13810 }
13811
13812 return S_OK;
13813}
13814
13815/**
13816 * @note Locks this object for writing.
13817 */
13818HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13819{
13820 LogFlowThisFuncEnter();
13821
13822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13823
13824 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13825 E_FAIL);
13826
13827 /* create a progress object to track operation completion */
13828 ComObjPtr<Progress> pProgress;
13829 pProgress.createObject();
13830 pProgress->init(i_getVirtualBox(),
13831 static_cast<IMachine *>(this) /* aInitiator */,
13832 tr("Stopping the virtual machine"),
13833 FALSE /* aCancelable */);
13834
13835 /* fill in the console task data */
13836 mConsoleTaskData.mLastState = mData->mMachineState;
13837 mConsoleTaskData.mProgress = pProgress;
13838
13839 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13840 i_setMachineState(MachineState_Stopping);
13841
13842 pProgress.queryInterfaceTo(aProgress.asOutParam());
13843
13844 return S_OK;
13845}
13846
13847/**
13848 * @note Locks this object for writing.
13849 */
13850HRESULT SessionMachine::endPoweringDown(LONG aResult,
13851 const com::Utf8Str &aErrMsg)
13852{
13853 HRESULT const hrcResult = (HRESULT)aResult;
13854 LogFlowThisFuncEnter();
13855
13856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13857
13858 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13859 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13860 && mConsoleTaskData.mLastState != MachineState_Null,
13861 E_FAIL);
13862
13863 /*
13864 * On failure, set the state to the state we had when BeginPoweringDown()
13865 * was called (this is expected by Console::PowerDown() and the associated
13866 * task). On success the VM process already changed the state to
13867 * MachineState_PoweredOff, so no need to do anything.
13868 */
13869 if (FAILED(hrcResult))
13870 i_setMachineState(mConsoleTaskData.mLastState);
13871
13872 /* notify the progress object about operation completion */
13873 Assert(mConsoleTaskData.mProgress);
13874 if (SUCCEEDED(hrcResult))
13875 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13876 else
13877 {
13878 if (aErrMsg.length())
13879 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13880 COM_IIDOF(ISession),
13881 getComponentName(),
13882 aErrMsg.c_str());
13883 else
13884 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13885 }
13886
13887 /* clear out the temporary saved state data */
13888 mConsoleTaskData.mLastState = MachineState_Null;
13889 mConsoleTaskData.mProgress.setNull();
13890
13891 LogFlowThisFuncLeave();
13892 return S_OK;
13893}
13894
13895
13896/**
13897 * Goes through the USB filters of the given machine to see if the given
13898 * device matches any filter or not.
13899 *
13900 * @note Locks the same as USBController::hasMatchingFilter() does.
13901 */
13902HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13903 BOOL *aMatched,
13904 ULONG *aMaskedInterfaces)
13905{
13906 LogFlowThisFunc(("\n"));
13907
13908#ifdef VBOX_WITH_USB
13909 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13910#else
13911 NOREF(aDevice);
13912 NOREF(aMaskedInterfaces);
13913 *aMatched = FALSE;
13914#endif
13915
13916 return S_OK;
13917}
13918
13919/**
13920 * @note Locks the same as Host::captureUSBDevice() does.
13921 */
13922HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926#ifdef VBOX_WITH_USB
13927 /* if captureDeviceForVM() fails, it must have set extended error info */
13928 clearError();
13929 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13930 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13931 return hrc;
13932
13933 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13934 AssertReturn(service, E_FAIL);
13935 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13936#else
13937 RT_NOREF(aId, aCaptureFilename);
13938 return E_NOTIMPL;
13939#endif
13940}
13941
13942/**
13943 * @note Locks the same as Host::detachUSBDevice() does.
13944 */
13945HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13946 BOOL aDone)
13947{
13948 LogFlowThisFunc(("\n"));
13949
13950#ifdef VBOX_WITH_USB
13951 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13952 AssertReturn(service, E_FAIL);
13953 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13954#else
13955 NOREF(aId);
13956 NOREF(aDone);
13957 return E_NOTIMPL;
13958#endif
13959}
13960
13961/**
13962 * Inserts all machine filters to the USB proxy service and then calls
13963 * Host::autoCaptureUSBDevices().
13964 *
13965 * Called by Console from the VM process upon VM startup.
13966 *
13967 * @note Locks what called methods lock.
13968 */
13969HRESULT SessionMachine::autoCaptureUSBDevices()
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973#ifdef VBOX_WITH_USB
13974 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13975 AssertComRC(hrc);
13976 NOREF(hrc);
13977
13978 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13979 AssertReturn(service, E_FAIL);
13980 return service->autoCaptureDevicesForVM(this);
13981#else
13982 return S_OK;
13983#endif
13984}
13985
13986/**
13987 * Removes all machine filters from the USB proxy service and then calls
13988 * Host::detachAllUSBDevices().
13989 *
13990 * Called by Console from the VM process upon normal VM termination or by
13991 * SessionMachine::uninit() upon abnormal VM termination (from under the
13992 * Machine/SessionMachine lock).
13993 *
13994 * @note Locks what called methods lock.
13995 */
13996HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13997{
13998 LogFlowThisFunc(("\n"));
13999
14000#ifdef VBOX_WITH_USB
14001 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14002 AssertComRC(hrc);
14003 NOREF(hrc);
14004
14005 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14006 AssertReturn(service, E_FAIL);
14007 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14008#else
14009 NOREF(aDone);
14010 return S_OK;
14011#endif
14012}
14013
14014/**
14015 * @note Locks this object for writing.
14016 */
14017HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14018 ComPtr<IProgress> &aProgress)
14019{
14020 LogFlowThisFuncEnter();
14021
14022 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14023 /*
14024 * We don't assert below because it might happen that a non-direct session
14025 * informs us it is closed right after we've been uninitialized -- it's ok.
14026 */
14027
14028 /* get IInternalSessionControl interface */
14029 ComPtr<IInternalSessionControl> control(aSession);
14030
14031 ComAssertRet(!control.isNull(), E_INVALIDARG);
14032
14033 /* Creating a Progress object requires the VirtualBox lock, and
14034 * thus locking it here is required by the lock order rules. */
14035 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14036
14037 if (control == mData->mSession.mDirectControl)
14038 {
14039 /* The direct session is being normally closed by the client process
14040 * ----------------------------------------------------------------- */
14041
14042 /* go to the closing state (essential for all open*Session() calls and
14043 * for #i_checkForDeath()) */
14044 Assert(mData->mSession.mState == SessionState_Locked);
14045 mData->mSession.mState = SessionState_Unlocking;
14046
14047 /* set direct control to NULL to release the remote instance */
14048 mData->mSession.mDirectControl.setNull();
14049 LogFlowThisFunc(("Direct control is set to NULL\n"));
14050
14051 if (mData->mSession.mProgress)
14052 {
14053 /* finalize the progress, someone might wait if a frontend
14054 * closes the session before powering on the VM. */
14055 mData->mSession.mProgress->notifyComplete(E_FAIL,
14056 COM_IIDOF(ISession),
14057 getComponentName(),
14058 tr("The VM session was closed before any attempt to power it on"));
14059 mData->mSession.mProgress.setNull();
14060 }
14061
14062 /* Create the progress object the client will use to wait until
14063 * #i_checkForDeath() is called to uninitialize this session object after
14064 * it releases the IPC semaphore.
14065 * Note! Because we're "reusing" mProgress here, this must be a proxy
14066 * object just like for LaunchVMProcess. */
14067 Assert(mData->mSession.mProgress.isNull());
14068 ComObjPtr<ProgressProxy> progress;
14069 progress.createObject();
14070 ComPtr<IUnknown> pPeer(mPeer);
14071 progress->init(mParent, pPeer,
14072 Bstr(tr("Closing session")).raw(),
14073 FALSE /* aCancelable */);
14074 progress.queryInterfaceTo(aProgress.asOutParam());
14075 mData->mSession.mProgress = progress;
14076 }
14077 else
14078 {
14079 /* the remote session is being normally closed */
14080 bool found = false;
14081 for (Data::Session::RemoteControlList::iterator
14082 it = mData->mSession.mRemoteControls.begin();
14083 it != mData->mSession.mRemoteControls.end();
14084 ++it)
14085 {
14086 if (control == *it)
14087 {
14088 found = true;
14089 // This MUST be erase(it), not remove(*it) as the latter
14090 // triggers a very nasty use after free due to the place where
14091 // the value "lives".
14092 mData->mSession.mRemoteControls.erase(it);
14093 break;
14094 }
14095 }
14096 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14097 E_INVALIDARG);
14098 }
14099
14100 /* signal the client watcher thread, because the client is going away */
14101 mParent->i_updateClientWatcher();
14102
14103 LogFlowThisFuncLeave();
14104 return S_OK;
14105}
14106
14107HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14108 std::vector<com::Utf8Str> &aValues,
14109 std::vector<LONG64> &aTimestamps,
14110 std::vector<com::Utf8Str> &aFlags)
14111{
14112 LogFlowThisFunc(("\n"));
14113
14114#ifdef VBOX_WITH_GUEST_PROPS
14115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14116
14117 size_t cEntries = mHWData->mGuestProperties.size();
14118 aNames.resize(cEntries);
14119 aValues.resize(cEntries);
14120 aTimestamps.resize(cEntries);
14121 aFlags.resize(cEntries);
14122
14123 size_t i = 0;
14124 for (HWData::GuestPropertyMap::const_iterator
14125 it = mHWData->mGuestProperties.begin();
14126 it != mHWData->mGuestProperties.end();
14127 ++it, ++i)
14128 {
14129 aNames[i] = it->first;
14130 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14131 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14132
14133 aValues[i] = it->second.strValue;
14134 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14135 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14136
14137 aTimestamps[i] = it->second.mTimestamp;
14138
14139 /* If it is NULL, keep it NULL. */
14140 if (it->second.mFlags)
14141 {
14142 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14143 GuestPropWriteFlags(it->second.mFlags, szFlags);
14144 aFlags[i] = szFlags;
14145 }
14146 else
14147 aFlags[i] = "";
14148 }
14149 return S_OK;
14150#else
14151 ReturnComNotImplemented();
14152#endif
14153}
14154
14155HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14156 const com::Utf8Str &aValue,
14157 LONG64 aTimestamp,
14158 const com::Utf8Str &aFlags,
14159 BOOL fWasDeleted)
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163#ifdef VBOX_WITH_GUEST_PROPS
14164 try
14165 {
14166 /*
14167 * Convert input up front.
14168 */
14169 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14170 if (aFlags.length())
14171 {
14172 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14173 AssertRCReturn(vrc, E_INVALIDARG);
14174 }
14175
14176 /*
14177 * Now grab the object lock, validate the state and do the update.
14178 */
14179
14180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14181
14182 if (!Global::IsOnline(mData->mMachineState))
14183 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14184
14185 i_setModified(IsModified_MachineData);
14186 mHWData.backup();
14187
14188 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14189 if (it != mHWData->mGuestProperties.end())
14190 {
14191 if (!fWasDeleted)
14192 {
14193 it->second.strValue = aValue;
14194 it->second.mTimestamp = aTimestamp;
14195 it->second.mFlags = fFlags;
14196 }
14197 else
14198 mHWData->mGuestProperties.erase(it);
14199
14200 mData->mGuestPropertiesModified = TRUE;
14201 }
14202 else if (!fWasDeleted)
14203 {
14204 HWData::GuestProperty prop;
14205 prop.strValue = aValue;
14206 prop.mTimestamp = aTimestamp;
14207 prop.mFlags = fFlags;
14208
14209 mHWData->mGuestProperties[aName] = prop;
14210 mData->mGuestPropertiesModified = TRUE;
14211 }
14212
14213 alock.release();
14214
14215 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14216 }
14217 catch (...)
14218 {
14219 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14220 }
14221 return S_OK;
14222#else
14223 ReturnComNotImplemented();
14224#endif
14225}
14226
14227
14228HRESULT SessionMachine::lockMedia()
14229{
14230 AutoMultiWriteLock2 alock(this->lockHandle(),
14231 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14232
14233 AssertReturn( mData->mMachineState == MachineState_Starting
14234 || mData->mMachineState == MachineState_Restoring
14235 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14236
14237 clearError();
14238 alock.release();
14239 return i_lockMedia();
14240}
14241
14242HRESULT SessionMachine::unlockMedia()
14243{
14244 HRESULT hrc = i_unlockMedia();
14245 return hrc;
14246}
14247
14248HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14249 ComPtr<IMediumAttachment> &aNewAttachment)
14250{
14251 // request the host lock first, since might be calling Host methods for getting host drives;
14252 // next, protect the media tree all the while we're in here, as well as our member variables
14253 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14254 this->lockHandle(),
14255 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14256
14257 IMediumAttachment *iAttach = aAttachment;
14258 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14259
14260 Utf8Str ctrlName;
14261 LONG lPort;
14262 LONG lDevice;
14263 bool fTempEject;
14264 {
14265 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14266
14267 /* Need to query the details first, as the IMediumAttachment reference
14268 * might be to the original settings, which we are going to change. */
14269 ctrlName = pAttach->i_getControllerName();
14270 lPort = pAttach->i_getPort();
14271 lDevice = pAttach->i_getDevice();
14272 fTempEject = pAttach->i_getTempEject();
14273 }
14274
14275 if (!fTempEject)
14276 {
14277 /* Remember previously mounted medium. The medium before taking the
14278 * backup is not necessarily the same thing. */
14279 ComObjPtr<Medium> oldmedium;
14280 oldmedium = pAttach->i_getMedium();
14281
14282 i_setModified(IsModified_Storage);
14283 mMediumAttachments.backup();
14284
14285 // The backup operation makes the pAttach reference point to the
14286 // old settings. Re-get the correct reference.
14287 pAttach = i_findAttachment(*mMediumAttachments.data(),
14288 ctrlName,
14289 lPort,
14290 lDevice);
14291
14292 {
14293 AutoCaller autoAttachCaller(this);
14294 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
14295
14296 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14297 if (!oldmedium.isNull())
14298 oldmedium->i_removeBackReference(mData->mUuid);
14299
14300 pAttach->i_updateMedium(NULL);
14301 pAttach->i_updateEjected();
14302 }
14303
14304 i_setModified(IsModified_Storage);
14305 }
14306 else
14307 {
14308 {
14309 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14310 pAttach->i_updateEjected();
14311 }
14312 }
14313
14314 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14315
14316 return S_OK;
14317}
14318
14319HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14320 com::Utf8Str &aResult)
14321{
14322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14323
14324 HRESULT hrc = S_OK;
14325
14326 if (!mAuthLibCtx.hAuthLibrary)
14327 {
14328 /* Load the external authentication library. */
14329 Bstr authLibrary;
14330 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14331
14332 Utf8Str filename = authLibrary;
14333
14334 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14335 if (RT_FAILURE(vrc))
14336 hrc = setErrorBoth(E_FAIL, vrc,
14337 tr("Could not load the external authentication library '%s' (%Rrc)"),
14338 filename.c_str(), vrc);
14339 }
14340
14341 /* The auth library might need the machine lock. */
14342 alock.release();
14343
14344 if (FAILED(hrc))
14345 return hrc;
14346
14347 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14348 {
14349 enum VRDEAuthParams
14350 {
14351 parmUuid = 1,
14352 parmGuestJudgement,
14353 parmUser,
14354 parmPassword,
14355 parmDomain,
14356 parmClientId
14357 };
14358
14359 AuthResult result = AuthResultAccessDenied;
14360
14361 Guid uuid(aAuthParams[parmUuid]);
14362 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14363 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14364
14365 result = AuthLibAuthenticate(&mAuthLibCtx,
14366 uuid.raw(), guestJudgement,
14367 aAuthParams[parmUser].c_str(),
14368 aAuthParams[parmPassword].c_str(),
14369 aAuthParams[parmDomain].c_str(),
14370 u32ClientId);
14371
14372 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14373 size_t cbPassword = aAuthParams[parmPassword].length();
14374 if (cbPassword)
14375 {
14376 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14377 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14378 }
14379
14380 if (result == AuthResultAccessGranted)
14381 aResult = "granted";
14382 else
14383 aResult = "denied";
14384
14385 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14386 aAuthParams[parmUser].c_str(), aResult.c_str()));
14387 }
14388 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14389 {
14390 enum VRDEAuthDisconnectParams
14391 {
14392 parmUuid = 1,
14393 parmClientId
14394 };
14395
14396 Guid uuid(aAuthParams[parmUuid]);
14397 uint32_t u32ClientId = 0;
14398 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14399 }
14400 else
14401 {
14402 hrc = E_INVALIDARG;
14403 }
14404
14405 return hrc;
14406}
14407
14408// public methods only for internal purposes
14409/////////////////////////////////////////////////////////////////////////////
14410
14411#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14412/**
14413 * Called from the client watcher thread to check for expected or unexpected
14414 * death of the client process that has a direct session to this machine.
14415 *
14416 * On Win32 and on OS/2, this method is called only when we've got the
14417 * mutex (i.e. the client has either died or terminated normally) so it always
14418 * returns @c true (the client is terminated, the session machine is
14419 * uninitialized).
14420 *
14421 * On other platforms, the method returns @c true if the client process has
14422 * terminated normally or abnormally and the session machine was uninitialized,
14423 * and @c false if the client process is still alive.
14424 *
14425 * @note Locks this object for writing.
14426 */
14427bool SessionMachine::i_checkForDeath()
14428{
14429 Uninit::Reason reason;
14430 bool terminated = false;
14431
14432 /* Enclose autoCaller with a block because calling uninit() from under it
14433 * will deadlock. */
14434 {
14435 AutoCaller autoCaller(this);
14436 if (!autoCaller.isOk())
14437 {
14438 /* return true if not ready, to cause the client watcher to exclude
14439 * the corresponding session from watching */
14440 LogFlowThisFunc(("Already uninitialized!\n"));
14441 return true;
14442 }
14443
14444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14445
14446 /* Determine the reason of death: if the session state is Closing here,
14447 * everything is fine. Otherwise it means that the client did not call
14448 * OnSessionEnd() before it released the IPC semaphore. This may happen
14449 * either because the client process has abnormally terminated, or
14450 * because it simply forgot to call ISession::Close() before exiting. We
14451 * threat the latter also as an abnormal termination (see
14452 * Session::uninit() for details). */
14453 reason = mData->mSession.mState == SessionState_Unlocking ?
14454 Uninit::Normal :
14455 Uninit::Abnormal;
14456
14457 if (mClientToken)
14458 terminated = mClientToken->release();
14459 } /* AutoCaller block */
14460
14461 if (terminated)
14462 uninit(reason);
14463
14464 return terminated;
14465}
14466
14467void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14468{
14469 LogFlowThisFunc(("\n"));
14470
14471 strTokenId.setNull();
14472
14473 AutoCaller autoCaller(this);
14474 AssertComRCReturnVoid(autoCaller.hrc());
14475
14476 Assert(mClientToken);
14477 if (mClientToken)
14478 mClientToken->getId(strTokenId);
14479}
14480#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14481IToken *SessionMachine::i_getToken()
14482{
14483 LogFlowThisFunc(("\n"));
14484
14485 AutoCaller autoCaller(this);
14486 AssertComRCReturn(autoCaller.hrc(), NULL);
14487
14488 Assert(mClientToken);
14489 if (mClientToken)
14490 return mClientToken->getToken();
14491 else
14492 return NULL;
14493}
14494#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14495
14496Machine::ClientToken *SessionMachine::i_getClientToken()
14497{
14498 LogFlowThisFunc(("\n"));
14499
14500 AutoCaller autoCaller(this);
14501 AssertComRCReturn(autoCaller.hrc(), NULL);
14502
14503 return mClientToken;
14504}
14505
14506
14507/**
14508 * @note Locks this object for reading.
14509 */
14510HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14511{
14512 LogFlowThisFunc(("\n"));
14513
14514 AutoCaller autoCaller(this);
14515 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14516
14517 ComPtr<IInternalSessionControl> directControl;
14518 {
14519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14520 if (mData->mSession.mLockType == LockType_VM)
14521 directControl = mData->mSession.mDirectControl;
14522 }
14523
14524 /* ignore notifications sent after #OnSessionEnd() is called */
14525 if (!directControl)
14526 return S_OK;
14527
14528 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14529}
14530
14531/**
14532 * @note Locks this object for reading.
14533 */
14534HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14535 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14536 const Utf8Str &aGuestIp, LONG aGuestPort)
14537{
14538 LogFlowThisFunc(("\n"));
14539
14540 AutoCaller autoCaller(this);
14541 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14542
14543 ComPtr<IInternalSessionControl> directControl;
14544 {
14545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14546 if (mData->mSession.mLockType == LockType_VM)
14547 directControl = mData->mSession.mDirectControl;
14548 }
14549
14550 /* ignore notifications sent after #OnSessionEnd() is called */
14551 if (!directControl)
14552 return S_OK;
14553 /*
14554 * instead acting like callback we ask IVirtualBox deliver corresponding event
14555 */
14556
14557 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14558 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14559 return S_OK;
14560}
14561
14562/**
14563 * @note Locks this object for reading.
14564 */
14565HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14566{
14567 LogFlowThisFunc(("\n"));
14568
14569 AutoCaller autoCaller(this);
14570 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14571
14572 ComPtr<IInternalSessionControl> directControl;
14573 {
14574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14575 if (mData->mSession.mLockType == LockType_VM)
14576 directControl = mData->mSession.mDirectControl;
14577 }
14578
14579 /* ignore notifications sent after #OnSessionEnd() is called */
14580 if (!directControl)
14581 return S_OK;
14582
14583 return directControl->OnAudioAdapterChange(audioAdapter);
14584}
14585
14586/**
14587 * @note Locks this object for reading.
14588 */
14589HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14590{
14591 LogFlowThisFunc(("\n"));
14592
14593 AutoCaller autoCaller(this);
14594 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14595
14596 ComPtr<IInternalSessionControl> directControl;
14597 {
14598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14599 if (mData->mSession.mLockType == LockType_VM)
14600 directControl = mData->mSession.mDirectControl;
14601 }
14602
14603 /* ignore notifications sent after #OnSessionEnd() is called */
14604 if (!directControl)
14605 return S_OK;
14606
14607 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14608}
14609
14610/**
14611 * @note Locks this object for reading.
14612 */
14613HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14614{
14615 LogFlowThisFunc(("\n"));
14616
14617 AutoCaller autoCaller(this);
14618 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14619
14620 ComPtr<IInternalSessionControl> directControl;
14621 {
14622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14623 if (mData->mSession.mLockType == LockType_VM)
14624 directControl = mData->mSession.mDirectControl;
14625 }
14626
14627 /* ignore notifications sent after #OnSessionEnd() is called */
14628 if (!directControl)
14629 return S_OK;
14630
14631 return directControl->OnSerialPortChange(serialPort);
14632}
14633
14634/**
14635 * @note Locks this object for reading.
14636 */
14637HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14638{
14639 LogFlowThisFunc(("\n"));
14640
14641 AutoCaller autoCaller(this);
14642 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14643
14644 ComPtr<IInternalSessionControl> directControl;
14645 {
14646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14647 if (mData->mSession.mLockType == LockType_VM)
14648 directControl = mData->mSession.mDirectControl;
14649 }
14650
14651 /* ignore notifications sent after #OnSessionEnd() is called */
14652 if (!directControl)
14653 return S_OK;
14654
14655 return directControl->OnParallelPortChange(parallelPort);
14656}
14657
14658/**
14659 * @note Locks this object for reading.
14660 */
14661HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14662{
14663 LogFlowThisFunc(("\n"));
14664
14665 AutoCaller autoCaller(this);
14666 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14667
14668 ComPtr<IInternalSessionControl> directControl;
14669 {
14670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14671 if (mData->mSession.mLockType == LockType_VM)
14672 directControl = mData->mSession.mDirectControl;
14673 }
14674
14675 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14676
14677 /* ignore notifications sent after #OnSessionEnd() is called */
14678 if (!directControl)
14679 return S_OK;
14680
14681 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14682}
14683
14684/**
14685 * @note Locks this object for reading.
14686 */
14687HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14688{
14689 LogFlowThisFunc(("\n"));
14690
14691 AutoCaller autoCaller(this);
14692 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14693
14694 ComPtr<IInternalSessionControl> directControl;
14695 {
14696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14697 if (mData->mSession.mLockType == LockType_VM)
14698 directControl = mData->mSession.mDirectControl;
14699 }
14700
14701 mParent->i_onMediumChanged(aAttachment);
14702
14703 /* ignore notifications sent after #OnSessionEnd() is called */
14704 if (!directControl)
14705 return S_OK;
14706
14707 return directControl->OnMediumChange(aAttachment, aForce);
14708}
14709
14710HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14711{
14712 LogFlowThisFunc(("\n"));
14713
14714 AutoCaller autoCaller(this);
14715 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14716
14717 ComPtr<IInternalSessionControl> directControl;
14718 {
14719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14720 if (mData->mSession.mLockType == LockType_VM)
14721 directControl = mData->mSession.mDirectControl;
14722 }
14723
14724 /* ignore notifications sent after #OnSessionEnd() is called */
14725 if (!directControl)
14726 return S_OK;
14727
14728 return directControl->OnVMProcessPriorityChange(aPriority);
14729}
14730
14731/**
14732 * @note Locks this object for reading.
14733 */
14734HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14735{
14736 LogFlowThisFunc(("\n"));
14737
14738 AutoCaller autoCaller(this);
14739 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14740
14741 ComPtr<IInternalSessionControl> directControl;
14742 {
14743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14744 if (mData->mSession.mLockType == LockType_VM)
14745 directControl = mData->mSession.mDirectControl;
14746 }
14747
14748 /* ignore notifications sent after #OnSessionEnd() is called */
14749 if (!directControl)
14750 return S_OK;
14751
14752 return directControl->OnCPUChange(aCPU, aRemove);
14753}
14754
14755HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14756{
14757 LogFlowThisFunc(("\n"));
14758
14759 AutoCaller autoCaller(this);
14760 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14761
14762 ComPtr<IInternalSessionControl> directControl;
14763 {
14764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14765 if (mData->mSession.mLockType == LockType_VM)
14766 directControl = mData->mSession.mDirectControl;
14767 }
14768
14769 /* ignore notifications sent after #OnSessionEnd() is called */
14770 if (!directControl)
14771 return S_OK;
14772
14773 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14774}
14775
14776/**
14777 * @note Locks this object for reading.
14778 */
14779HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14780{
14781 LogFlowThisFunc(("\n"));
14782
14783 AutoCaller autoCaller(this);
14784 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14785
14786 ComPtr<IInternalSessionControl> directControl;
14787 {
14788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14789 if (mData->mSession.mLockType == LockType_VM)
14790 directControl = mData->mSession.mDirectControl;
14791 }
14792
14793 /* ignore notifications sent after #OnSessionEnd() is called */
14794 if (!directControl)
14795 return S_OK;
14796
14797 return directControl->OnVRDEServerChange(aRestart);
14798}
14799
14800/**
14801 * @note Locks this object for reading.
14802 */
14803HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14804{
14805 LogFlowThisFunc(("\n"));
14806
14807 AutoCaller autoCaller(this);
14808 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14809
14810 ComPtr<IInternalSessionControl> directControl;
14811 {
14812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14813 if (mData->mSession.mLockType == LockType_VM)
14814 directControl = mData->mSession.mDirectControl;
14815 }
14816
14817 /* ignore notifications sent after #OnSessionEnd() is called */
14818 if (!directControl)
14819 return S_OK;
14820
14821 return directControl->OnRecordingChange(aEnable);
14822}
14823
14824/**
14825 * @note Locks this object for reading.
14826 */
14827HRESULT SessionMachine::i_onUSBControllerChange()
14828{
14829 LogFlowThisFunc(("\n"));
14830
14831 AutoCaller autoCaller(this);
14832 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14833
14834 ComPtr<IInternalSessionControl> directControl;
14835 {
14836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14837 if (mData->mSession.mLockType == LockType_VM)
14838 directControl = mData->mSession.mDirectControl;
14839 }
14840
14841 /* ignore notifications sent after #OnSessionEnd() is called */
14842 if (!directControl)
14843 return S_OK;
14844
14845 return directControl->OnUSBControllerChange();
14846}
14847
14848/**
14849 * @note Locks this object for reading.
14850 */
14851HRESULT SessionMachine::i_onSharedFolderChange()
14852{
14853 LogFlowThisFunc(("\n"));
14854
14855 AutoCaller autoCaller(this);
14856 AssertComRCReturnRC(autoCaller.hrc());
14857
14858 ComPtr<IInternalSessionControl> directControl;
14859 {
14860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14861 if (mData->mSession.mLockType == LockType_VM)
14862 directControl = mData->mSession.mDirectControl;
14863 }
14864
14865 /* ignore notifications sent after #OnSessionEnd() is called */
14866 if (!directControl)
14867 return S_OK;
14868
14869 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14870}
14871
14872/**
14873 * @note Locks this object for reading.
14874 */
14875HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14876{
14877 LogFlowThisFunc(("\n"));
14878
14879 AutoCaller autoCaller(this);
14880 AssertComRCReturnRC(autoCaller.hrc());
14881
14882 ComPtr<IInternalSessionControl> directControl;
14883 {
14884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14885 if (mData->mSession.mLockType == LockType_VM)
14886 directControl = mData->mSession.mDirectControl;
14887 }
14888
14889 /* ignore notifications sent after #OnSessionEnd() is called */
14890 if (!directControl)
14891 return S_OK;
14892
14893 return directControl->OnClipboardModeChange(aClipboardMode);
14894}
14895
14896/**
14897 * @note Locks this object for reading.
14898 */
14899HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14900{
14901 LogFlowThisFunc(("\n"));
14902
14903 AutoCaller autoCaller(this);
14904 AssertComRCReturnRC(autoCaller.hrc());
14905
14906 ComPtr<IInternalSessionControl> directControl;
14907 {
14908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14909 if (mData->mSession.mLockType == LockType_VM)
14910 directControl = mData->mSession.mDirectControl;
14911 }
14912
14913 /* ignore notifications sent after #OnSessionEnd() is called */
14914 if (!directControl)
14915 return S_OK;
14916
14917 return directControl->OnClipboardFileTransferModeChange(aEnable);
14918}
14919
14920/**
14921 * @note Locks this object for reading.
14922 */
14923HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14924{
14925 LogFlowThisFunc(("\n"));
14926
14927 AutoCaller autoCaller(this);
14928 AssertComRCReturnRC(autoCaller.hrc());
14929
14930 ComPtr<IInternalSessionControl> directControl;
14931 {
14932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14933 if (mData->mSession.mLockType == LockType_VM)
14934 directControl = mData->mSession.mDirectControl;
14935 }
14936
14937 /* ignore notifications sent after #OnSessionEnd() is called */
14938 if (!directControl)
14939 return S_OK;
14940
14941 return directControl->OnDnDModeChange(aDnDMode);
14942}
14943
14944/**
14945 * @note Locks this object for reading.
14946 */
14947HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14948{
14949 LogFlowThisFunc(("\n"));
14950
14951 AutoCaller autoCaller(this);
14952 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14953
14954 ComPtr<IInternalSessionControl> directControl;
14955 {
14956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14957 if (mData->mSession.mLockType == LockType_VM)
14958 directControl = mData->mSession.mDirectControl;
14959 }
14960
14961 /* ignore notifications sent after #OnSessionEnd() is called */
14962 if (!directControl)
14963 return S_OK;
14964
14965 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14966}
14967
14968/**
14969 * @note Locks this object for reading.
14970 */
14971HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14972{
14973 LogFlowThisFunc(("\n"));
14974
14975 AutoCaller autoCaller(this);
14976 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14977
14978 ComPtr<IInternalSessionControl> directControl;
14979 {
14980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14981 if (mData->mSession.mLockType == LockType_VM)
14982 directControl = mData->mSession.mDirectControl;
14983 }
14984
14985 /* ignore notifications sent after #OnSessionEnd() is called */
14986 if (!directControl)
14987 return S_OK;
14988
14989 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14990}
14991
14992/**
14993 * @note Locks this object for reading.
14994 */
14995HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14996{
14997 LogFlowThisFunc(("\n"));
14998
14999 AutoCaller autoCaller(this);
15000 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15001
15002 ComPtr<IInternalSessionControl> directControl;
15003 {
15004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15005 if (mData->mSession.mLockType == LockType_VM)
15006 directControl = mData->mSession.mDirectControl;
15007 }
15008
15009 /* ignore notifications sent after #OnSessionEnd() is called */
15010 if (!directControl)
15011 return S_OK;
15012
15013 return directControl->OnGuestDebugControlChange(guestDebugControl);
15014}
15015
15016/**
15017 * Returns @c true if this machine's USB controller reports it has a matching
15018 * filter for the given USB device and @c false otherwise.
15019 *
15020 * @note locks this object for reading.
15021 */
15022bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15023{
15024 AutoCaller autoCaller(this);
15025 /* silently return if not ready -- this method may be called after the
15026 * direct machine session has been called */
15027 if (!autoCaller.isOk())
15028 return false;
15029
15030#ifdef VBOX_WITH_USB
15031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15032
15033 switch (mData->mMachineState)
15034 {
15035 case MachineState_Starting:
15036 case MachineState_Restoring:
15037 case MachineState_TeleportingIn:
15038 case MachineState_Paused:
15039 case MachineState_Running:
15040 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15041 * elsewhere... */
15042 alock.release();
15043 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15044 default: break;
15045 }
15046#else
15047 NOREF(aDevice);
15048 NOREF(aMaskedIfs);
15049#endif
15050 return false;
15051}
15052
15053/**
15054 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15055 */
15056HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15057 IVirtualBoxErrorInfo *aError,
15058 ULONG aMaskedIfs,
15059 const com::Utf8Str &aCaptureFilename)
15060{
15061 LogFlowThisFunc(("\n"));
15062
15063 AutoCaller autoCaller(this);
15064
15065 /* This notification may happen after the machine object has been
15066 * uninitialized (the session was closed), so don't assert. */
15067 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15068
15069 ComPtr<IInternalSessionControl> directControl;
15070 {
15071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15072 if (mData->mSession.mLockType == LockType_VM)
15073 directControl = mData->mSession.mDirectControl;
15074 }
15075
15076 /* fail on notifications sent after #OnSessionEnd() is called, it is
15077 * expected by the caller */
15078 if (!directControl)
15079 return E_FAIL;
15080
15081 /* No locks should be held at this point. */
15082 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15083 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15084
15085 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15086}
15087
15088/**
15089 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15090 */
15091HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15092 IVirtualBoxErrorInfo *aError)
15093{
15094 LogFlowThisFunc(("\n"));
15095
15096 AutoCaller autoCaller(this);
15097
15098 /* This notification may happen after the machine object has been
15099 * uninitialized (the session was closed), so don't assert. */
15100 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15101
15102 ComPtr<IInternalSessionControl> directControl;
15103 {
15104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15105 if (mData->mSession.mLockType == LockType_VM)
15106 directControl = mData->mSession.mDirectControl;
15107 }
15108
15109 /* fail on notifications sent after #OnSessionEnd() is called, it is
15110 * expected by the caller */
15111 if (!directControl)
15112 return E_FAIL;
15113
15114 /* No locks should be held at this point. */
15115 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15116 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15117
15118 return directControl->OnUSBDeviceDetach(aId, aError);
15119}
15120
15121// protected methods
15122/////////////////////////////////////////////////////////////////////////////
15123
15124/**
15125 * Deletes the given file if it is no longer in use by either the current machine state
15126 * (if the machine is "saved") or any of the machine's snapshots.
15127 *
15128 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15129 * but is different for each SnapshotMachine. When calling this, the order of calling this
15130 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15131 * is therefore critical. I know, it's all rather messy.
15132 *
15133 * @param strStateFile
15134 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15135 * the test for whether the saved state file is in use.
15136 */
15137void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15138 Snapshot *pSnapshotToIgnore)
15139{
15140 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15141 if ( (strStateFile.isNotEmpty())
15142 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15143 )
15144 // ... and it must also not be shared with other snapshots
15145 if ( !mData->mFirstSnapshot
15146 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15147 // this checks the SnapshotMachine's state file paths
15148 )
15149 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15150}
15151
15152/**
15153 * Locks the attached media.
15154 *
15155 * All attached hard disks are locked for writing and DVD/floppy are locked for
15156 * reading. Parents of attached hard disks (if any) are locked for reading.
15157 *
15158 * This method also performs accessibility check of all media it locks: if some
15159 * media is inaccessible, the method will return a failure and a bunch of
15160 * extended error info objects per each inaccessible medium.
15161 *
15162 * Note that this method is atomic: if it returns a success, all media are
15163 * locked as described above; on failure no media is locked at all (all
15164 * succeeded individual locks will be undone).
15165 *
15166 * The caller is responsible for doing the necessary state sanity checks.
15167 *
15168 * The locks made by this method must be undone by calling #unlockMedia() when
15169 * no more needed.
15170 */
15171HRESULT SessionMachine::i_lockMedia()
15172{
15173 AutoCaller autoCaller(this);
15174 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15175
15176 AutoMultiWriteLock2 alock(this->lockHandle(),
15177 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15178
15179 /* bail out if trying to lock things with already set up locking */
15180 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15181
15182 MultiResult hrcMult(S_OK);
15183
15184 /* Collect locking information for all medium objects attached to the VM. */
15185 for (MediumAttachmentList::const_iterator
15186 it = mMediumAttachments->begin();
15187 it != mMediumAttachments->end();
15188 ++it)
15189 {
15190 MediumAttachment *pAtt = *it;
15191 DeviceType_T devType = pAtt->i_getType();
15192 Medium *pMedium = pAtt->i_getMedium();
15193
15194 MediumLockList *pMediumLockList(new MediumLockList());
15195 // There can be attachments without a medium (floppy/dvd), and thus
15196 // it's impossible to create a medium lock list. It still makes sense
15197 // to have the empty medium lock list in the map in case a medium is
15198 // attached later.
15199 if (pMedium != NULL)
15200 {
15201 MediumType_T mediumType = pMedium->i_getType();
15202 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15203 || mediumType == MediumType_Shareable;
15204 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15205
15206 alock.release();
15207 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15208 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15209 false /* fMediumLockWriteAll */,
15210 NULL,
15211 *pMediumLockList);
15212 alock.acquire();
15213 if (FAILED(hrcMult))
15214 {
15215 delete pMediumLockList;
15216 mData->mSession.mLockedMedia.Clear();
15217 break;
15218 }
15219 }
15220
15221 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15222 if (FAILED(hrc))
15223 {
15224 mData->mSession.mLockedMedia.Clear();
15225 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
15226 break;
15227 }
15228 }
15229
15230 if (SUCCEEDED(hrcMult))
15231 {
15232 /* Now lock all media. If this fails, nothing is locked. */
15233 alock.release();
15234 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
15235 alock.acquire();
15236 if (FAILED(hrc))
15237 hrcMult = setError(hrc,
15238 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15239 }
15240
15241 return hrcMult;
15242}
15243
15244/**
15245 * Undoes the locks made by by #lockMedia().
15246 */
15247HRESULT SessionMachine::i_unlockMedia()
15248{
15249 AutoCaller autoCaller(this);
15250 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15251
15252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15253
15254 /* we may be holding important error info on the current thread;
15255 * preserve it */
15256 ErrorInfoKeeper eik;
15257
15258 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
15259 AssertComRC(hrc);
15260 return hrc;
15261}
15262
15263/**
15264 * Helper to change the machine state (reimplementation).
15265 *
15266 * @note Locks this object for writing.
15267 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15268 * it can cause crashes in random places due to unexpectedly committing
15269 * the current settings. The caller is responsible for that. The call
15270 * to saveStateSettings is fine, because this method does not commit.
15271 */
15272HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15273{
15274 LogFlowThisFuncEnter();
15275
15276 AutoCaller autoCaller(this);
15277 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15278
15279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15280
15281 MachineState_T oldMachineState = mData->mMachineState;
15282
15283 AssertMsgReturn(oldMachineState != aMachineState,
15284 ("oldMachineState=%s, aMachineState=%s\n",
15285 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15286 E_FAIL);
15287
15288 HRESULT hrc = S_OK;
15289
15290 int stsFlags = 0;
15291 bool deleteSavedState = false;
15292
15293 /* detect some state transitions */
15294
15295 if ( ( ( oldMachineState == MachineState_Saved
15296 || oldMachineState == MachineState_AbortedSaved
15297 )
15298 && aMachineState == MachineState_Restoring
15299 )
15300 || ( ( oldMachineState == MachineState_PoweredOff
15301 || oldMachineState == MachineState_Teleported
15302 || oldMachineState == MachineState_Aborted
15303 )
15304 && ( aMachineState == MachineState_TeleportingIn
15305 || aMachineState == MachineState_Starting
15306 )
15307 )
15308 )
15309 {
15310 /* The EMT thread is about to start */
15311
15312 /* Nothing to do here for now... */
15313
15314 /// @todo NEWMEDIA don't let mDVDDrive and other children
15315 /// change anything when in the Starting/Restoring state
15316 }
15317 else if ( ( oldMachineState == MachineState_Running
15318 || oldMachineState == MachineState_Paused
15319 || oldMachineState == MachineState_Teleporting
15320 || oldMachineState == MachineState_OnlineSnapshotting
15321 || oldMachineState == MachineState_LiveSnapshotting
15322 || oldMachineState == MachineState_Stuck
15323 || oldMachineState == MachineState_Starting
15324 || oldMachineState == MachineState_Stopping
15325 || oldMachineState == MachineState_Saving
15326 || oldMachineState == MachineState_Restoring
15327 || oldMachineState == MachineState_TeleportingPausedVM
15328 || oldMachineState == MachineState_TeleportingIn
15329 )
15330 && ( aMachineState == MachineState_PoweredOff
15331 || aMachineState == MachineState_Saved
15332 || aMachineState == MachineState_Teleported
15333 || aMachineState == MachineState_Aborted
15334 || aMachineState == MachineState_AbortedSaved
15335 )
15336 )
15337 {
15338 /* The EMT thread has just stopped, unlock attached media. Note that as
15339 * opposed to locking that is done from Console, we do unlocking here
15340 * because the VM process may have aborted before having a chance to
15341 * properly unlock all media it locked. */
15342
15343 unlockMedia();
15344 }
15345
15346 if (oldMachineState == MachineState_Restoring)
15347 {
15348 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15349 {
15350 /*
15351 * delete the saved state file once the machine has finished
15352 * restoring from it (note that Console sets the state from
15353 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15354 * to give the user an ability to fix an error and retry --
15355 * we keep the saved state file in this case)
15356 */
15357 deleteSavedState = true;
15358 }
15359 }
15360 else if ( ( oldMachineState == MachineState_Saved
15361 || oldMachineState == MachineState_AbortedSaved
15362 )
15363 && ( aMachineState == MachineState_PoweredOff
15364 || aMachineState == MachineState_Teleported
15365 )
15366 )
15367 {
15368 /* delete the saved state after SessionMachine::discardSavedState() is called */
15369 deleteSavedState = true;
15370 mData->mCurrentStateModified = TRUE;
15371 stsFlags |= SaveSTS_CurStateModified;
15372 }
15373 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15374 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15375
15376 if ( aMachineState == MachineState_Starting
15377 || aMachineState == MachineState_Restoring
15378 || aMachineState == MachineState_TeleportingIn
15379 )
15380 {
15381 /* set the current state modified flag to indicate that the current
15382 * state is no more identical to the state in the
15383 * current snapshot */
15384 if (!mData->mCurrentSnapshot.isNull())
15385 {
15386 mData->mCurrentStateModified = TRUE;
15387 stsFlags |= SaveSTS_CurStateModified;
15388 }
15389 }
15390
15391 if (deleteSavedState)
15392 {
15393 if (mRemoveSavedState)
15394 {
15395 Assert(!mSSData->strStateFilePath.isEmpty());
15396
15397 // it is safe to delete the saved state file if ...
15398 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15399 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15400 // ... none of the snapshots share the saved state file
15401 )
15402 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15403 }
15404
15405 mSSData->strStateFilePath.setNull();
15406 stsFlags |= SaveSTS_StateFilePath;
15407 }
15408
15409 /* redirect to the underlying peer machine */
15410 mPeer->i_setMachineState(aMachineState);
15411
15412 if ( oldMachineState != MachineState_RestoringSnapshot
15413 && ( aMachineState == MachineState_PoweredOff
15414 || aMachineState == MachineState_Teleported
15415 || aMachineState == MachineState_Aborted
15416 || aMachineState == MachineState_AbortedSaved
15417 || aMachineState == MachineState_Saved))
15418 {
15419 /* the machine has stopped execution
15420 * (or the saved state file was adopted) */
15421 stsFlags |= SaveSTS_StateTimeStamp;
15422 }
15423
15424 if ( ( oldMachineState == MachineState_PoweredOff
15425 || oldMachineState == MachineState_Aborted
15426 || oldMachineState == MachineState_Teleported
15427 )
15428 && aMachineState == MachineState_Saved)
15429 {
15430 /* the saved state file was adopted */
15431 Assert(!mSSData->strStateFilePath.isEmpty());
15432 stsFlags |= SaveSTS_StateFilePath;
15433 }
15434
15435#ifdef VBOX_WITH_GUEST_PROPS
15436 if ( aMachineState == MachineState_PoweredOff
15437 || aMachineState == MachineState_Aborted
15438 || aMachineState == MachineState_Teleported)
15439 {
15440 /* Make sure any transient guest properties get removed from the
15441 * property store on shutdown. */
15442 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15443
15444 /* remove it from the settings representation */
15445 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15446 for (settings::GuestPropertiesList::iterator
15447 it = llGuestProperties.begin();
15448 it != llGuestProperties.end();
15449 /*nothing*/)
15450 {
15451 const settings::GuestProperty &prop = *it;
15452 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15453 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15454 {
15455 it = llGuestProperties.erase(it);
15456 fNeedsSaving = true;
15457 }
15458 else
15459 {
15460 ++it;
15461 }
15462 }
15463
15464 /* Additionally remove it from the HWData representation. Required to
15465 * keep everything in sync, as this is what the API keeps using. */
15466 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15467 for (HWData::GuestPropertyMap::iterator
15468 it = llHWGuestProperties.begin();
15469 it != llHWGuestProperties.end();
15470 /*nothing*/)
15471 {
15472 uint32_t fFlags = it->second.mFlags;
15473 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15474 {
15475 /* iterator where we need to continue after the erase call
15476 * (C++03 is a fact still, and it doesn't return the iterator
15477 * which would allow continuing) */
15478 HWData::GuestPropertyMap::iterator it2 = it;
15479 ++it2;
15480 llHWGuestProperties.erase(it);
15481 it = it2;
15482 fNeedsSaving = true;
15483 }
15484 else
15485 {
15486 ++it;
15487 }
15488 }
15489
15490 if (fNeedsSaving)
15491 {
15492 mData->mCurrentStateModified = TRUE;
15493 stsFlags |= SaveSTS_CurStateModified;
15494 }
15495 }
15496#endif /* VBOX_WITH_GUEST_PROPS */
15497
15498 hrc = i_saveStateSettings(stsFlags);
15499
15500 if ( ( oldMachineState != MachineState_PoweredOff
15501 && oldMachineState != MachineState_Aborted
15502 && oldMachineState != MachineState_Teleported
15503 )
15504 && ( aMachineState == MachineState_PoweredOff
15505 || aMachineState == MachineState_Aborted
15506 || aMachineState == MachineState_Teleported
15507 )
15508 )
15509 {
15510 /* we've been shut down for any reason */
15511 /* no special action so far */
15512 }
15513
15514 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
15515 LogFlowThisFuncLeave();
15516 return hrc;
15517}
15518
15519/**
15520 * Sends the current machine state value to the VM process.
15521 *
15522 * @note Locks this object for reading, then calls a client process.
15523 */
15524HRESULT SessionMachine::i_updateMachineStateOnClient()
15525{
15526 AutoCaller autoCaller(this);
15527 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15528
15529 ComPtr<IInternalSessionControl> directControl;
15530 {
15531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15532 AssertReturn(!!mData, E_FAIL);
15533 if (mData->mSession.mLockType == LockType_VM)
15534 directControl = mData->mSession.mDirectControl;
15535
15536 /* directControl may be already set to NULL here in #OnSessionEnd()
15537 * called too early by the direct session process while there is still
15538 * some operation (like deleting the snapshot) in progress. The client
15539 * process in this case is waiting inside Session::close() for the
15540 * "end session" process object to complete, while #uninit() called by
15541 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15542 * operation to complete. For now, we accept this inconsistent behavior
15543 * and simply do nothing here. */
15544
15545 if (mData->mSession.mState == SessionState_Unlocking)
15546 return S_OK;
15547 }
15548
15549 /* ignore notifications sent after #OnSessionEnd() is called */
15550 if (!directControl)
15551 return S_OK;
15552
15553 return directControl->UpdateMachineState(mData->mMachineState);
15554}
15555
15556
15557/*static*/
15558HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15559{
15560 va_list args;
15561 va_start(args, pcszMsg);
15562 HRESULT hrc = setErrorInternalV(aResultCode,
15563 getStaticClassIID(),
15564 getStaticComponentName(),
15565 pcszMsg, args,
15566 false /* aWarning */,
15567 true /* aLogIt */);
15568 va_end(args);
15569 return hrc;
15570}
15571
15572
15573HRESULT Machine::updateState(MachineState_T aState)
15574{
15575 NOREF(aState);
15576 ReturnComNotImplemented();
15577}
15578
15579HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15580{
15581 NOREF(aProgress);
15582 ReturnComNotImplemented();
15583}
15584
15585HRESULT Machine::endPowerUp(LONG aResult)
15586{
15587 NOREF(aResult);
15588 ReturnComNotImplemented();
15589}
15590
15591HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15592{
15593 NOREF(aProgress);
15594 ReturnComNotImplemented();
15595}
15596
15597HRESULT Machine::endPoweringDown(LONG aResult,
15598 const com::Utf8Str &aErrMsg)
15599{
15600 NOREF(aResult);
15601 NOREF(aErrMsg);
15602 ReturnComNotImplemented();
15603}
15604
15605HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15606 BOOL *aMatched,
15607 ULONG *aMaskedInterfaces)
15608{
15609 NOREF(aDevice);
15610 NOREF(aMatched);
15611 NOREF(aMaskedInterfaces);
15612 ReturnComNotImplemented();
15613
15614}
15615
15616HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15617{
15618 NOREF(aId); NOREF(aCaptureFilename);
15619 ReturnComNotImplemented();
15620}
15621
15622HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15623 BOOL aDone)
15624{
15625 NOREF(aId);
15626 NOREF(aDone);
15627 ReturnComNotImplemented();
15628}
15629
15630HRESULT Machine::autoCaptureUSBDevices()
15631{
15632 ReturnComNotImplemented();
15633}
15634
15635HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15636{
15637 NOREF(aDone);
15638 ReturnComNotImplemented();
15639}
15640
15641HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15642 ComPtr<IProgress> &aProgress)
15643{
15644 NOREF(aSession);
15645 NOREF(aProgress);
15646 ReturnComNotImplemented();
15647}
15648
15649HRESULT Machine::finishOnlineMergeMedium()
15650{
15651 ReturnComNotImplemented();
15652}
15653
15654HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15655 std::vector<com::Utf8Str> &aValues,
15656 std::vector<LONG64> &aTimestamps,
15657 std::vector<com::Utf8Str> &aFlags)
15658{
15659 NOREF(aNames);
15660 NOREF(aValues);
15661 NOREF(aTimestamps);
15662 NOREF(aFlags);
15663 ReturnComNotImplemented();
15664}
15665
15666HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15667 const com::Utf8Str &aValue,
15668 LONG64 aTimestamp,
15669 const com::Utf8Str &aFlags,
15670 BOOL fWasDeleted)
15671{
15672 NOREF(aName);
15673 NOREF(aValue);
15674 NOREF(aTimestamp);
15675 NOREF(aFlags);
15676 NOREF(fWasDeleted);
15677 ReturnComNotImplemented();
15678}
15679
15680HRESULT Machine::lockMedia()
15681{
15682 ReturnComNotImplemented();
15683}
15684
15685HRESULT Machine::unlockMedia()
15686{
15687 ReturnComNotImplemented();
15688}
15689
15690HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15691 ComPtr<IMediumAttachment> &aNewAttachment)
15692{
15693 NOREF(aAttachment);
15694 NOREF(aNewAttachment);
15695 ReturnComNotImplemented();
15696}
15697
15698HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15699 ULONG aCpuUser,
15700 ULONG aCpuKernel,
15701 ULONG aCpuIdle,
15702 ULONG aMemTotal,
15703 ULONG aMemFree,
15704 ULONG aMemBalloon,
15705 ULONG aMemShared,
15706 ULONG aMemCache,
15707 ULONG aPagedTotal,
15708 ULONG aMemAllocTotal,
15709 ULONG aMemFreeTotal,
15710 ULONG aMemBalloonTotal,
15711 ULONG aMemSharedTotal,
15712 ULONG aVmNetRx,
15713 ULONG aVmNetTx)
15714{
15715 NOREF(aValidStats);
15716 NOREF(aCpuUser);
15717 NOREF(aCpuKernel);
15718 NOREF(aCpuIdle);
15719 NOREF(aMemTotal);
15720 NOREF(aMemFree);
15721 NOREF(aMemBalloon);
15722 NOREF(aMemShared);
15723 NOREF(aMemCache);
15724 NOREF(aPagedTotal);
15725 NOREF(aMemAllocTotal);
15726 NOREF(aMemFreeTotal);
15727 NOREF(aMemBalloonTotal);
15728 NOREF(aMemSharedTotal);
15729 NOREF(aVmNetRx);
15730 NOREF(aVmNetTx);
15731 ReturnComNotImplemented();
15732}
15733
15734HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15735 com::Utf8Str &aResult)
15736{
15737 NOREF(aAuthParams);
15738 NOREF(aResult);
15739 ReturnComNotImplemented();
15740}
15741
15742com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15743{
15744 com::Utf8Str strControllerName = "Unknown";
15745 switch (aBusType)
15746 {
15747 case StorageBus_IDE:
15748 {
15749 strControllerName = "IDE";
15750 break;
15751 }
15752 case StorageBus_SATA:
15753 {
15754 strControllerName = "SATA";
15755 break;
15756 }
15757 case StorageBus_SCSI:
15758 {
15759 strControllerName = "SCSI";
15760 break;
15761 }
15762 case StorageBus_Floppy:
15763 {
15764 strControllerName = "Floppy";
15765 break;
15766 }
15767 case StorageBus_SAS:
15768 {
15769 strControllerName = "SAS";
15770 break;
15771 }
15772 case StorageBus_USB:
15773 {
15774 strControllerName = "USB";
15775 break;
15776 }
15777 default:
15778 break;
15779 }
15780 return strControllerName;
15781}
15782
15783HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15784{
15785 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15786
15787 AutoCaller autoCaller(this);
15788 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15789
15790 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15791 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15792 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15793 if (FAILED(hrc)) return hrc;
15794
15795 NOREF(aFlags);
15796 com::Utf8Str osTypeId;
15797 ComObjPtr<GuestOSType> osType = NULL;
15798
15799 /* Get the guest os type as a string from the VB. */
15800 hrc = getOSTypeId(osTypeId);
15801 if (FAILED(hrc)) return hrc;
15802
15803 /* Get the os type obj that coresponds, can be used to get
15804 * the defaults for this guest OS. */
15805 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15806 if (FAILED(hrc)) return hrc;
15807
15808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15809
15810 /* Let the OS type select 64-bit ness. */
15811 mHWData->mLongMode = osType->i_is64Bit()
15812 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15813
15814 /* Let the OS type enable the X2APIC */
15815 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15816
15817 /* This one covers IOAPICEnabled. */
15818 mBIOSSettings->i_applyDefaults(osType);
15819
15820 /* Initialize default record settings. */
15821 mRecordingSettings->i_applyDefaults();
15822
15823 /* Initialize default BIOS settings here */
15824 /* Hardware virtualization must be ON by default */
15825 mHWData->mAPIC = true;
15826 mHWData->mHWVirtExEnabled = true;
15827
15828 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15829 if (FAILED(hrc)) return hrc;
15830
15831 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15832 if (FAILED(hrc)) return hrc;
15833
15834 /* Graphics stuff. */
15835 GraphicsControllerType_T graphicsController;
15836 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15837 if (FAILED(hrc)) return hrc;
15838
15839 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15840 if (FAILED(hrc)) return hrc;
15841
15842 ULONG vramSize;
15843 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15844 if (FAILED(hrc)) return hrc;
15845
15846 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15847 if (FAILED(hrc)) return hrc;
15848
15849 BOOL fAccelerate2DVideoEnabled;
15850 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15851 if (FAILED(hrc)) return hrc;
15852
15853 hrc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15854 if (FAILED(hrc)) return hrc;
15855
15856 BOOL fAccelerate3DEnabled;
15857 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15858 if (FAILED(hrc)) return hrc;
15859
15860 hrc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15861 if (FAILED(hrc)) return hrc;
15862
15863 hrc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15864 if (FAILED(hrc)) return hrc;
15865
15866 hrc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15867 if (FAILED(hrc)) return hrc;
15868
15869 hrc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15870 if (FAILED(hrc)) return hrc;
15871
15872 BOOL mRTCUseUTC;
15873 hrc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15874 if (FAILED(hrc)) return hrc;
15875
15876 setRTCUseUTC(mRTCUseUTC);
15877 if (FAILED(hrc)) return hrc;
15878
15879 /* the setter does more than just the assignment, so use it */
15880 ChipsetType_T enmChipsetType;
15881 hrc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15882 if (FAILED(hrc)) return hrc;
15883
15884 hrc = COMSETTER(ChipsetType)(enmChipsetType);
15885 if (FAILED(hrc)) return hrc;
15886
15887 hrc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15888 if (FAILED(hrc)) return hrc;
15889
15890 /* Apply IOMMU defaults. */
15891 IommuType_T enmIommuType;
15892 hrc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15893 if (FAILED(hrc)) return hrc;
15894
15895 hrc = COMSETTER(IommuType)(enmIommuType);
15896 if (FAILED(hrc)) return hrc;
15897
15898 /* Apply network adapters defaults */
15899 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15900 mNetworkAdapters[slot]->i_applyDefaults(osType);
15901
15902 /* Apply serial port defaults */
15903 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15904 mSerialPorts[slot]->i_applyDefaults(osType);
15905
15906 /* Apply parallel port defaults - not OS dependent*/
15907 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15908 mParallelPorts[slot]->i_applyDefaults();
15909
15910 /* This one covers the TPM type. */
15911 mTrustedPlatformModule->i_applyDefaults(osType);
15912
15913 /* This one covers secure boot. */
15914 hrc = mNvramStore->i_applyDefaults(osType);
15915 if (FAILED(hrc)) return hrc;
15916
15917 /* Audio stuff. */
15918 hrc = mAudioSettings->i_applyDefaults(osType);
15919 if (FAILED(hrc)) return hrc;
15920
15921 /* Storage Controllers */
15922 StorageControllerType_T hdStorageControllerType;
15923 StorageBus_T hdStorageBusType;
15924 StorageControllerType_T dvdStorageControllerType;
15925 StorageBus_T dvdStorageBusType;
15926 BOOL recommendedFloppy;
15927 ComPtr<IStorageController> floppyController;
15928 ComPtr<IStorageController> hdController;
15929 ComPtr<IStorageController> dvdController;
15930 Utf8Str strFloppyName, strDVDName, strHDName;
15931
15932 /* GUI auto generates controller names using bus type. Do the same*/
15933 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15934
15935 /* Floppy recommended? add one. */
15936 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15937 if (FAILED(hrc)) return hrc;
15938 if (recommendedFloppy)
15939 {
15940 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15941 if (FAILED(hrc)) return hrc;
15942 }
15943
15944 /* Setup one DVD storage controller. */
15945 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15946 if (FAILED(hrc)) return hrc;
15947
15948 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15949 if (FAILED(hrc)) return hrc;
15950
15951 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15952
15953 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15954 if (FAILED(hrc)) return hrc;
15955
15956 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15957 if (FAILED(hrc)) return hrc;
15958
15959 /* Setup one HDD storage controller. */
15960 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15961 if (FAILED(hrc)) return hrc;
15962
15963 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15964 if (FAILED(hrc)) return hrc;
15965
15966 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15967
15968 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15969 {
15970 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15971 if (FAILED(hrc)) return hrc;
15972
15973 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15974 if (FAILED(hrc)) return hrc;
15975 }
15976 else
15977 {
15978 /* The HD controller is the same as DVD: */
15979 hdController = dvdController;
15980 }
15981
15982 /* Limit the AHCI port count if it's used because windows has trouble with
15983 * too many ports and other guest (OS X in particular) may take extra long
15984 * boot: */
15985
15986 // pParent = static_cast<Medium*>(aP)
15987 IStorageController *temp = hdController;
15988 ComObjPtr<StorageController> storageController;
15989 storageController = static_cast<StorageController *>(temp);
15990
15991 // tempHDController = aHDController;
15992 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15993 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15994 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15995 storageController->COMSETTER(PortCount)(1);
15996
15997 /* USB stuff */
15998
15999 bool ohciEnabled = false;
16000
16001 ComPtr<IUSBController> usbController;
16002 BOOL recommendedUSB3;
16003 BOOL recommendedUSB;
16004 BOOL usbProxyAvailable;
16005
16006 getUSBProxyAvailable(&usbProxyAvailable);
16007 if (FAILED(hrc)) return hrc;
16008
16009 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16010 if (FAILED(hrc)) return hrc;
16011 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16012 if (FAILED(hrc)) return hrc;
16013
16014 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16015 {
16016 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16017 if (FAILED(hrc)) return hrc;
16018
16019 /* xHci includes OHCI */
16020 ohciEnabled = true;
16021 }
16022 if ( !ohciEnabled
16023 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16024 {
16025 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16026 if (FAILED(hrc)) return hrc;
16027 ohciEnabled = true;
16028
16029 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16030 if (FAILED(hrc)) return hrc;
16031 }
16032
16033 /* Set recommended human interface device types: */
16034 BOOL recommendedUSBHID;
16035 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16036 if (FAILED(hrc)) return hrc;
16037
16038 if (recommendedUSBHID)
16039 {
16040 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16041 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16042 if (!ohciEnabled && !usbDeviceFilters.isNull())
16043 {
16044 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16045 if (FAILED(hrc)) return hrc;
16046 }
16047 }
16048
16049 BOOL recommendedUSBTablet;
16050 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16051 if (FAILED(hrc)) return hrc;
16052
16053 if (recommendedUSBTablet)
16054 {
16055 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16056 if (!ohciEnabled && !usbDeviceFilters.isNull())
16057 {
16058 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16059 if (FAILED(hrc)) return hrc;
16060 }
16061 }
16062
16063 /* Enable the VMMDev testing feature for bootsector VMs: */
16064 if (osTypeId == "VBoxBS_64")
16065 {
16066 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16067 if (FAILED(hrc))
16068 return hrc;
16069 }
16070
16071 return S_OK;
16072}
16073
16074#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16075/**
16076 * Task record for change encryption settins.
16077 */
16078class Machine::ChangeEncryptionTask
16079 : public Machine::Task
16080{
16081public:
16082 ChangeEncryptionTask(Machine *m,
16083 Progress *p,
16084 const Utf8Str &t,
16085 const com::Utf8Str &aCurrentPassword,
16086 const com::Utf8Str &aCipher,
16087 const com::Utf8Str &aNewPassword,
16088 const com::Utf8Str &aNewPasswordId,
16089 const BOOL aForce,
16090 const MediaList &llMedia)
16091 : Task(m, p, t),
16092 mstrNewPassword(aNewPassword),
16093 mstrCurrentPassword(aCurrentPassword),
16094 mstrCipher(aCipher),
16095 mstrNewPasswordId(aNewPasswordId),
16096 mForce(aForce),
16097 mllMedia(llMedia)
16098 {}
16099
16100 ~ChangeEncryptionTask()
16101 {
16102 if (mstrNewPassword.length())
16103 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16104 if (mstrCurrentPassword.length())
16105 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16106 if (m_pCryptoIf)
16107 {
16108 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16109 m_pCryptoIf = NULL;
16110 }
16111 }
16112
16113 Utf8Str mstrNewPassword;
16114 Utf8Str mstrCurrentPassword;
16115 Utf8Str mstrCipher;
16116 Utf8Str mstrNewPasswordId;
16117 BOOL mForce;
16118 MediaList mllMedia;
16119 PCVBOXCRYPTOIF m_pCryptoIf;
16120private:
16121 void handler()
16122 {
16123 try
16124 {
16125 m_pMachine->i_changeEncryptionHandler(*this);
16126 }
16127 catch (...)
16128 {
16129 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16130 }
16131 }
16132
16133 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16134};
16135
16136/**
16137 * Scans specified directory and fills list by files found
16138 *
16139 * @returns VBox status code.
16140 * @param lstFiles
16141 * @param strDir
16142 * @param filePattern
16143 */
16144int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16145 const com::Utf8Str &strPattern)
16146{
16147 /* To get all entries including subdirectories. */
16148 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16149 if (!pszFilePattern)
16150 return VERR_NO_STR_MEMORY;
16151
16152 PRTDIRENTRYEX pDirEntry = NULL;
16153 RTDIR hDir;
16154 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16155 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16156 if (RT_SUCCESS(vrc))
16157 {
16158 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16159 if (pDirEntry)
16160 {
16161 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16162 != VERR_NO_MORE_FILES)
16163 {
16164 char *pszFilePath = NULL;
16165
16166 if (vrc == VERR_BUFFER_OVERFLOW)
16167 {
16168 /* allocate new buffer. */
16169 RTMemFree(pDirEntry);
16170 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16171 if (!pDirEntry)
16172 {
16173 vrc = VERR_NO_MEMORY;
16174 break;
16175 }
16176 /* Retry. */
16177 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16178 if (RT_FAILURE(vrc))
16179 break;
16180 }
16181 else if (RT_FAILURE(vrc))
16182 break;
16183
16184 /* Exclude . and .. */
16185 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16186 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16187 continue;
16188 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16189 {
16190 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16191 if (!pszSubDirPath)
16192 {
16193 vrc = VERR_NO_STR_MEMORY;
16194 break;
16195 }
16196 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16197 RTMemFree(pszSubDirPath);
16198 if (RT_FAILURE(vrc))
16199 break;
16200 continue;
16201 }
16202
16203 /* We got the new entry. */
16204 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16205 continue;
16206
16207 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16208 continue;
16209
16210 /* Prepend the path to the libraries. */
16211 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16212 if (!pszFilePath)
16213 {
16214 vrc = VERR_NO_STR_MEMORY;
16215 break;
16216 }
16217
16218 lstFiles.push_back(pszFilePath);
16219 RTStrFree(pszFilePath);
16220 }
16221
16222 RTMemFree(pDirEntry);
16223 }
16224 else
16225 vrc = VERR_NO_MEMORY;
16226
16227 RTDirClose(hDir);
16228 }
16229 else
16230 {
16231 /* On Windows the above immediately signals that there are no
16232 * files matching, while on other platforms enumerating the
16233 * files below fails. Either way: stop searching. */
16234 }
16235
16236 if ( vrc == VERR_NO_MORE_FILES
16237 || vrc == VERR_FILE_NOT_FOUND
16238 || vrc == VERR_PATH_NOT_FOUND)
16239 vrc = VINF_SUCCESS;
16240 RTStrFree(pszFilePattern);
16241 return vrc;
16242}
16243
16244/**
16245 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16246 *
16247 * @returns VBox status code.
16248 * @param pszFilename The file to open.
16249 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16250 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16251 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16252 * @param fOpen The open flags for the file.
16253 * @param phVfsIos Where to store the handle to the I/O stream on success.
16254 */
16255int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16256 const char *pszKeyStore, const char *pszPassword,
16257 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16258{
16259 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16260 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16261 if (RT_SUCCESS(vrc))
16262 {
16263 if (pCryptoIf)
16264 {
16265 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16266 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16267 if (RT_SUCCESS(vrc))
16268 {
16269 RTVfsFileRelease(hVfsFile);
16270 hVfsFile = hVfsFileCrypto;
16271 }
16272 }
16273
16274 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16275 RTVfsFileRelease(hVfsFile);
16276 }
16277
16278 return vrc;
16279}
16280
16281/**
16282 * Helper function processing all actions for one component (saved state files,
16283 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16284 *
16285 * @param task
16286 * @param strDirectory
16287 * @param strFilePattern
16288 * @param strMagic
16289 * @param strKeyStore
16290 * @param strKeyId
16291 * @return
16292 */
16293HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16294 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16295 com::Utf8Str &strKeyId, int iCipherMode)
16296{
16297 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16298 && task.mstrCipher.isEmpty()
16299 && task.mstrNewPassword.isEmpty()
16300 && task.mstrNewPasswordId.isEmpty();
16301 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16302 && task.mstrCipher.isNotEmpty()
16303 && task.mstrNewPassword.isNotEmpty()
16304 && task.mstrNewPasswordId.isNotEmpty();
16305
16306 /* check if the cipher is changed which causes the reencryption*/
16307
16308 const char *pszTaskCipher = NULL;
16309 if (task.mstrCipher.isNotEmpty())
16310 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16311
16312 if (!task.mForce && !fDecrypt && !fEncrypt)
16313 {
16314 char *pszCipher = NULL;
16315 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16316 NULL /*pszPassword*/,
16317 NULL /*ppbKey*/,
16318 NULL /*pcbKey*/,
16319 &pszCipher);
16320 if (RT_SUCCESS(vrc))
16321 {
16322 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16323 RTMemFree(pszCipher);
16324 }
16325 else
16326 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16327 strFilePattern.c_str(), vrc);
16328 }
16329
16330 /* Only the password needs to be changed */
16331 if (!task.mForce && !fDecrypt && !fEncrypt)
16332 {
16333 Assert(task.m_pCryptoIf);
16334
16335 VBOXCRYPTOCTX hCryptoCtx;
16336 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16337 if (RT_FAILURE(vrc))
16338 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16339 strFilePattern.c_str(), vrc);
16340 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16341 if (RT_FAILURE(vrc))
16342 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16343 strFilePattern.c_str(), vrc);
16344
16345 char *pszKeyStore = NULL;
16346 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16347 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16348 if (RT_FAILURE(vrc))
16349 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16350 strFilePattern.c_str(), vrc);
16351 strKeyStore = pszKeyStore;
16352 RTMemFree(pszKeyStore);
16353 strKeyId = task.mstrNewPasswordId;
16354 return S_OK;
16355 }
16356
16357 /* Reencryption required */
16358 HRESULT hrc = S_OK;
16359 int vrc = VINF_SUCCESS;
16360
16361 std::list<com::Utf8Str> lstFiles;
16362 if (SUCCEEDED(hrc))
16363 {
16364 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16365 if (RT_FAILURE(vrc))
16366 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
16367 }
16368 com::Utf8Str strNewKeyStore;
16369 if (SUCCEEDED(hrc))
16370 {
16371 if (!fDecrypt)
16372 {
16373 VBOXCRYPTOCTX hCryptoCtx;
16374 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16375 if (RT_FAILURE(vrc))
16376 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16377 strFilePattern.c_str(), vrc);
16378
16379 char *pszKeyStore = NULL;
16380 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16381 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16382 if (RT_FAILURE(vrc))
16383 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16384 strFilePattern.c_str(), vrc);
16385 strNewKeyStore = pszKeyStore;
16386 RTMemFree(pszKeyStore);
16387 }
16388
16389 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16390 it != lstFiles.end();
16391 ++it)
16392 {
16393 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16394 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16395
16396 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16397 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16398
16399 vrc = i_createIoStreamForFile((*it).c_str(),
16400 fEncrypt ? NULL : task.m_pCryptoIf,
16401 fEncrypt ? NULL : strKeyStore.c_str(),
16402 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16403 fOpenForRead, &hVfsIosOld);
16404 if (RT_SUCCESS(vrc))
16405 {
16406 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16407 fDecrypt ? NULL : task.m_pCryptoIf,
16408 fDecrypt ? NULL : strNewKeyStore.c_str(),
16409 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16410 fOpenForWrite, &hVfsIosNew);
16411 if (RT_FAILURE(vrc))
16412 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16413 (*it + ".tmp").c_str(), vrc);
16414 }
16415 else
16416 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
16417
16418 if (RT_SUCCESS(vrc))
16419 {
16420 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16421 if (RT_FAILURE(vrc))
16422 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16423 (*it).c_str(), vrc);
16424 }
16425
16426 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16427 RTVfsIoStrmRelease(hVfsIosOld);
16428 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16429 RTVfsIoStrmRelease(hVfsIosNew);
16430 }
16431 }
16432
16433 if (SUCCEEDED(hrc))
16434 {
16435 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16436 it != lstFiles.end();
16437 ++it)
16438 {
16439 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16440 if (RT_FAILURE(vrc))
16441 {
16442 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
16443 break;
16444 }
16445 }
16446 }
16447
16448 if (SUCCEEDED(hrc))
16449 {
16450 strKeyStore = strNewKeyStore;
16451 strKeyId = task.mstrNewPasswordId;
16452 }
16453
16454 return hrc;
16455}
16456
16457/**
16458 * Task thread implementation for Machine::changeEncryption(), called from
16459 * Machine::taskHandler().
16460 *
16461 * @note Locks this object for writing.
16462 *
16463 * @param task
16464 * @return
16465 */
16466void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16467{
16468 LogFlowThisFuncEnter();
16469
16470 AutoCaller autoCaller(this);
16471 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16472 if (FAILED(autoCaller.hrc()))
16473 {
16474 /* we might have been uninitialized because the session was accidentally
16475 * closed by the client, so don't assert */
16476 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
16477 task.m_pProgress->i_notifyComplete(hrc);
16478 LogFlowThisFuncLeave();
16479 return;
16480 }
16481
16482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16483
16484 HRESULT hrc = S_OK;
16485 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16486 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16487 try
16488 {
16489 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16490 if (FAILED(hrc))
16491 throw hrc;
16492
16493 if (task.mstrCurrentPassword.isEmpty())
16494 {
16495 if (mData->mstrKeyStore.isNotEmpty())
16496 throw setError(VBOX_E_PASSWORD_INCORRECT,
16497 tr("The password given for the encrypted VM is incorrect"));
16498 }
16499 else
16500 {
16501 if (mData->mstrKeyStore.isEmpty())
16502 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16503 tr("The VM is not configured for encryption"));
16504 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
16505 if (hrc == VBOX_E_PASSWORD_INCORRECT)
16506 throw setError(VBOX_E_PASSWORD_INCORRECT,
16507 tr("The password to decrypt the VM is incorrect"));
16508 }
16509
16510 if (task.mstrCipher.isNotEmpty())
16511 {
16512 if ( task.mstrNewPassword.isEmpty()
16513 && task.mstrNewPasswordId.isEmpty()
16514 && task.mstrCurrentPassword.isNotEmpty())
16515 {
16516 /* An empty password and password ID will default to the current password. */
16517 task.mstrNewPassword = task.mstrCurrentPassword;
16518 }
16519 else if (task.mstrNewPassword.isEmpty())
16520 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16521 tr("A password must be given for the VM encryption"));
16522 else if (task.mstrNewPasswordId.isEmpty())
16523 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16524 tr("A valid identifier for the password must be given"));
16525 }
16526 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16527 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16528 tr("The password and password identifier must be empty if the output should be unencrypted"));
16529
16530 /*
16531 * Save config.
16532 * Must be first operation to prevent making encrypted copies
16533 * for old version of the config file.
16534 */
16535 int fSave = Machine::SaveS_Force;
16536 if (task.mstrNewPassword.isNotEmpty())
16537 {
16538 VBOXCRYPTOCTX hCryptoCtx;
16539
16540 int vrc = VINF_SUCCESS;
16541 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16542 {
16543 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16544 task.mstrNewPassword.c_str(), &hCryptoCtx);
16545 if (RT_FAILURE(vrc))
16546 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16547 }
16548 else
16549 {
16550 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16551 task.mstrCurrentPassword.c_str(),
16552 &hCryptoCtx);
16553 if (RT_FAILURE(vrc))
16554 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16555 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16556 if (RT_FAILURE(vrc))
16557 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16558 }
16559
16560 char *pszKeyStore;
16561 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16562 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16563 if (RT_FAILURE(vrc))
16564 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16565 mData->mstrKeyStore = pszKeyStore;
16566 RTStrFree(pszKeyStore);
16567 mData->mstrKeyId = task.mstrNewPasswordId;
16568 size_t cbPassword = task.mstrNewPassword.length() + 1;
16569 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16570 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16571 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16572 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16573
16574 /*
16575 * Remove backuped config after saving because it can contain
16576 * unencrypted version of the config
16577 */
16578 fSave |= Machine::SaveS_RemoveBackup;
16579 }
16580 else
16581 {
16582 mData->mstrKeyId.setNull();
16583 mData->mstrKeyStore.setNull();
16584 }
16585
16586 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16587 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16588 Bstr bstrNewPassword(task.mstrNewPassword);
16589 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16590 /* encrypt media */
16591 alock.release();
16592 for (MediaList::iterator it = task.mllMedia.begin();
16593 it != task.mllMedia.end();
16594 ++it)
16595 {
16596 ComPtr<IProgress> pProgress1;
16597 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16598 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16599 pProgress1.asOutParam());
16600 if (FAILED(hrc)) throw hrc;
16601 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16602 if (FAILED(hrc)) throw hrc;
16603 }
16604 alock.acquire();
16605
16606 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16607
16608 Utf8Str strFullSnapshotFolder;
16609 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16610
16611 /* .sav files (main and snapshots) */
16612 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16613 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16614 if (FAILED(hrc))
16615 /* the helper function already sets error object */
16616 throw hrc;
16617
16618 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16619
16620 /* .nvram files */
16621 com::Utf8Str strNVRAMKeyId;
16622 com::Utf8Str strNVRAMKeyStore;
16623 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16624 if (FAILED(hrc))
16625 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16626
16627 Utf8Str strMachineFolder;
16628 i_calculateFullPath(".", strMachineFolder);
16629
16630 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16631 if (FAILED(hrc))
16632 /* the helper function already sets error object */
16633 throw hrc;
16634
16635 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16636 if (FAILED(hrc))
16637 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16638
16639 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16640
16641 /* .log files */
16642 com::Utf8Str strLogFolder;
16643 i_getLogFolder(strLogFolder);
16644 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16645 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16646 if (FAILED(hrc))
16647 /* the helper function already sets error object */
16648 throw hrc;
16649
16650 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16651
16652 i_saveSettings(NULL, alock, fSave);
16653 }
16654 catch (HRESULT hrcXcpt)
16655 {
16656 hrc = hrcXcpt;
16657 mData->mstrKeyId = strOldKeyId;
16658 mData->mstrKeyStore = strOldKeyStore;
16659 }
16660
16661 task.m_pProgress->i_notifyComplete(hrc);
16662
16663 LogFlowThisFuncLeave();
16664}
16665#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16666
16667HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16668 const com::Utf8Str &aCipher,
16669 const com::Utf8Str &aNewPassword,
16670 const com::Utf8Str &aNewPasswordId,
16671 BOOL aForce,
16672 ComPtr<IProgress> &aProgress)
16673{
16674 LogFlowFuncEnter();
16675
16676#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16677 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16678 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16679#else
16680 /* make the VM accessible */
16681 if (!mData->mAccessible)
16682 {
16683 if ( aCurrentPassword.isEmpty()
16684 || mData->mstrKeyId.isEmpty())
16685 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16686
16687 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16688 if (FAILED(hrc))
16689 return hrc;
16690 }
16691
16692 AutoLimitedCaller autoCaller(this);
16693 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16694
16695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16696
16697 /* define media to be change encryption */
16698
16699 MediaList llMedia;
16700 for (MediumAttachmentList::iterator
16701 it = mMediumAttachments->begin();
16702 it != mMediumAttachments->end();
16703 ++it)
16704 {
16705 ComObjPtr<MediumAttachment> &pAttach = *it;
16706 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16707
16708 if (!pMedium.isNull())
16709 {
16710 AutoCaller mac(pMedium);
16711 if (FAILED(mac.hrc())) return mac.hrc();
16712 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16713 DeviceType_T devType = pMedium->i_getDeviceType();
16714 if (devType == DeviceType_HardDisk)
16715 {
16716 /*
16717 * We need to move to last child because the Medium::changeEncryption
16718 * encrypts all chain of specified medium with its parents.
16719 * Also we perform cheking of back reference and children for
16720 * all media in the chain to raise error before we start any action.
16721 * So, we first move into root parent and then we will move to last child
16722 * keeping latter in the list for encryption.
16723 */
16724
16725 /* move to root parent */
16726 ComObjPtr<Medium> pTmpMedium = pMedium;
16727 while (pTmpMedium.isNotNull())
16728 {
16729 AutoCaller mediumAC(pTmpMedium);
16730 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16731 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16732
16733 /* Cannot encrypt media which are attached to more than one virtual machine. */
16734 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16735 if (cBackRefs > 1)
16736 return setError(VBOX_E_INVALID_OBJECT_STATE,
16737 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16738 pTmpMedium->i_getName().c_str(), cBackRefs);
16739
16740 size_t cChildren = pTmpMedium->i_getChildren().size();
16741 if (cChildren > 1)
16742 return setError(VBOX_E_INVALID_OBJECT_STATE,
16743 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16744 pTmpMedium->i_getName().c_str(), cChildren);
16745
16746 pTmpMedium = pTmpMedium->i_getParent();
16747 }
16748 /* move to last child */
16749 pTmpMedium = pMedium;
16750 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16751 {
16752 AutoCaller mediumAC(pTmpMedium);
16753 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16754 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16755
16756 /* Cannot encrypt media which are attached to more than one virtual machine. */
16757 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16758 if (cBackRefs > 1)
16759 return setError(VBOX_E_INVALID_OBJECT_STATE,
16760 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16761 pTmpMedium->i_getName().c_str(), cBackRefs);
16762
16763 size_t cChildren = pTmpMedium->i_getChildren().size();
16764 if (cChildren > 1)
16765 return setError(VBOX_E_INVALID_OBJECT_STATE,
16766 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16767 pTmpMedium->i_getName().c_str(), cChildren);
16768
16769 pTmpMedium = pTmpMedium->i_getChildren().front();
16770 }
16771 llMedia.push_back(pTmpMedium);
16772 }
16773 }
16774 }
16775
16776 ComObjPtr<Progress> pProgress;
16777 pProgress.createObject();
16778 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16779 static_cast<IMachine*>(this) /* aInitiator */,
16780 tr("Change encryption"),
16781 TRUE /* fCancellable */,
16782 (ULONG)(4 + + llMedia.size()), // cOperations
16783 tr("Change encryption of the mediuma"));
16784 if (FAILED(hrc))
16785 return hrc;
16786
16787 /* create and start the task on a separate thread (note that it will not
16788 * start working until we release alock) */
16789 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16790 aCurrentPassword, aCipher, aNewPassword,
16791 aNewPasswordId, aForce, llMedia);
16792 hrc = pTask->createThread();
16793 pTask = NULL;
16794 if (FAILED(hrc))
16795 return hrc;
16796
16797 pProgress.queryInterfaceTo(aProgress.asOutParam());
16798
16799 LogFlowFuncLeave();
16800
16801 return S_OK;
16802#endif
16803}
16804
16805HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16806 com::Utf8Str &aPasswordId)
16807{
16808#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16809 RT_NOREF(aCipher, aPasswordId);
16810 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16811#else
16812 AutoLimitedCaller autoCaller(this);
16813 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16814
16815 PCVBOXCRYPTOIF pCryptoIf = NULL;
16816 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16817 if (FAILED(hrc)) return hrc; /* Error is set */
16818
16819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16820
16821 if (mData->mstrKeyStore.isNotEmpty())
16822 {
16823 char *pszCipher = NULL;
16824 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16825 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16826 if (RT_SUCCESS(vrc))
16827 {
16828 aCipher = getCipherStringWithoutMode(pszCipher);
16829 RTStrFree(pszCipher);
16830 aPasswordId = mData->mstrKeyId;
16831 }
16832 else
16833 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16834 tr("Failed to query the encryption settings with %Rrc"),
16835 vrc);
16836 }
16837 else
16838 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16839
16840 mParent->i_releaseCryptoIf(pCryptoIf);
16841
16842 return hrc;
16843#endif
16844}
16845
16846HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16847{
16848#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16849 RT_NOREF(aPassword);
16850 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16851#else
16852 AutoLimitedCaller autoCaller(this);
16853 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16854
16855 PCVBOXCRYPTOIF pCryptoIf = NULL;
16856 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16857 if (FAILED(hrc)) return hrc; /* Error is set */
16858
16859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16860
16861 if (mData->mstrKeyStore.isNotEmpty())
16862 {
16863 char *pszCipher = NULL;
16864 uint8_t *pbDek = NULL;
16865 size_t cbDek = 0;
16866 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16867 &pbDek, &cbDek, &pszCipher);
16868 if (RT_SUCCESS(vrc))
16869 {
16870 RTStrFree(pszCipher);
16871 RTMemSaferFree(pbDek, cbDek);
16872 }
16873 else
16874 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16875 tr("The password supplied for the encrypted machine is incorrect"));
16876 }
16877 else
16878 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16879
16880 mParent->i_releaseCryptoIf(pCryptoIf);
16881
16882 return hrc;
16883#endif
16884}
16885
16886HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16887 const com::Utf8Str &aPassword)
16888{
16889#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16890 RT_NOREF(aId, aPassword);
16891 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16892#else
16893 AutoLimitedCaller autoCaller(this);
16894 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16895
16896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16897
16898 size_t cbPassword = aPassword.length() + 1;
16899 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16900
16901 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16902
16903 if ( mData->mAccessible
16904 && mData->mSession.mState == SessionState_Locked
16905 && mData->mSession.mLockType == LockType_VM
16906 && mData->mSession.mDirectControl != NULL)
16907 {
16908 /* get the console from the direct session */
16909 ComPtr<IConsole> console;
16910 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16911 ComAssertComRC(hrc);
16912 /* send passsword to console */
16913 console->AddEncryptionPassword(Bstr(aId).raw(),
16914 Bstr(aPassword).raw(),
16915 TRUE);
16916 }
16917
16918 if (mData->mstrKeyId == aId)
16919 {
16920 HRESULT hrc = checkEncryptionPassword(aPassword);
16921 if (FAILED(hrc))
16922 return hrc;
16923
16924 if (SUCCEEDED(hrc))
16925 {
16926 /*
16927 * Encryption is used and password is correct,
16928 * Reinit the machine if required.
16929 */
16930 BOOL fAccessible;
16931 alock.release();
16932 getAccessible(&fAccessible);
16933 alock.acquire();
16934 }
16935 }
16936
16937 /*
16938 * Add the password into the NvramStore only after
16939 * the machine becomes accessible and the NvramStore
16940 * contains key id and key store.
16941 */
16942 if (mNvramStore.isNotNull())
16943 mNvramStore->i_addPassword(aId, aPassword);
16944
16945 return S_OK;
16946#endif
16947}
16948
16949HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16950 const std::vector<com::Utf8Str> &aPasswords)
16951{
16952#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16953 RT_NOREF(aIds, aPasswords);
16954 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16955#else
16956 if (aIds.size() != aPasswords.size())
16957 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16958
16959 HRESULT hrc = S_OK;
16960 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16961 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16962
16963 return hrc;
16964#endif
16965}
16966
16967HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16968{
16969#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16970 RT_NOREF(autoCaller, aId);
16971 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16972#else
16973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16974
16975 if ( mData->mAccessible
16976 && mData->mSession.mState == SessionState_Locked
16977 && mData->mSession.mLockType == LockType_VM
16978 && mData->mSession.mDirectControl != NULL)
16979 {
16980 /* get the console from the direct session */
16981 ComPtr<IConsole> console;
16982 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16983 ComAssertComRC(hrc);
16984 /* send passsword to console */
16985 console->RemoveEncryptionPassword(Bstr(aId).raw());
16986 }
16987
16988 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16989 {
16990 if (Global::IsOnlineOrTransient(mData->mMachineState))
16991 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16992 alock.release();
16993 autoCaller.release();
16994 /* return because all passwords are purged when machine becomes inaccessible; */
16995 return i_setInaccessible();
16996 }
16997
16998 if (mNvramStore.isNotNull())
16999 mNvramStore->i_removePassword(aId);
17000 mData->mpKeyStore->deleteSecretKey(aId);
17001 return S_OK;
17002#endif
17003}
17004
17005HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17006{
17007#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17008 RT_NOREF(autoCaller);
17009 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17010#else
17011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17012
17013 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17014 {
17015 if (Global::IsOnlineOrTransient(mData->mMachineState))
17016 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17017 alock.release();
17018 autoCaller.release();
17019 /* return because all passwords are purged when machine becomes inaccessible; */
17020 return i_setInaccessible();
17021 }
17022
17023 mNvramStore->i_removeAllPasswords();
17024 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17025 return S_OK;
17026#endif
17027}
17028
17029#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17030HRESULT Machine::i_setInaccessible()
17031{
17032 if (!mData->mAccessible)
17033 return S_OK;
17034
17035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17036 VirtualBox *pParent = mParent;
17037 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17038 Guid id(i_getId());
17039
17040 alock.release();
17041
17042 uninit();
17043 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17044
17045 alock.acquire();
17046 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17047 return hrc;
17048}
17049#endif
17050
17051/* This isn't handled entirely by the wrapper generator yet. */
17052#ifdef VBOX_WITH_XPCOM
17053NS_DECL_CLASSINFO(SessionMachine)
17054NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17055
17056NS_DECL_CLASSINFO(SnapshotMachine)
17057NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17058#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette