VirtualBox

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

Last change on this file since 98288 was 98288, checked in by vboxsync, 22 months ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

  • 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 98288 2023-01-24 15:32:43Z 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 mediums 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 rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3499 if (SUCCEEDED(rc2))
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> > &llMediums,
5385 const StringsList &llFilesToDelete)
5386 : Task(m, p, t),
5387 m_llMediums(llMediums),
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_llMediums;
5405 StringsList m_llFilesToDelete;
5406
5407 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5408};
5409
5410/**
5411 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5412 * SessionMachine::taskHandler().
5413 *
5414 * @note Locks this object for writing.
5415 *
5416 * @param task
5417 * @return
5418 */
5419void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5420{
5421 LogFlowThisFuncEnter();
5422
5423 AutoCaller autoCaller(this);
5424 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5425 if (FAILED(autoCaller.hrc()))
5426 {
5427 /* we might have been uninitialized because the session was accidentally
5428 * closed by the client, so don't assert */
5429 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
5430 task.m_pProgress->i_notifyComplete(hrc);
5431 LogFlowThisFuncLeave();
5432 return;
5433 }
5434
5435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5436
5437 HRESULT hrc;
5438 try
5439 {
5440 ULONG uLogHistoryCount = 3;
5441 ComPtr<ISystemProperties> systemProperties;
5442 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5443 if (FAILED(hrc)) throw hrc;
5444
5445 if (!systemProperties.isNull())
5446 {
5447 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5448 if (FAILED(hrc)) throw hrc;
5449 }
5450
5451 MachineState_T oldState = mData->mMachineState;
5452 i_setMachineState(MachineState_SettingUp);
5453 alock.release();
5454 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5455 {
5456 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5457 {
5458 AutoCaller mac(pMedium);
5459 if (FAILED(mac.hrc())) throw mac.hrc();
5460 Utf8Str strLocation = pMedium->i_getLocationFull();
5461 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5462 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5463 if (FAILED(hrc)) throw hrc;
5464 }
5465 if (pMedium->i_isMediumFormatFile())
5466 {
5467 ComPtr<IProgress> pProgress2;
5468 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
5469 if (FAILED(hrc)) throw hrc;
5470 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5471 if (FAILED(hrc)) throw hrc;
5472 }
5473
5474 /* Close the medium, deliberately without checking the return
5475 * code, and without leaving any trace in the error info, as
5476 * a failure here is a very minor issue, which shouldn't happen
5477 * as above we even managed to delete the medium. */
5478 {
5479 ErrorInfoKeeper eik;
5480 pMedium->Close();
5481 }
5482 }
5483 i_setMachineState(oldState);
5484 alock.acquire();
5485
5486 // delete the files pushed on the task list by Machine::Delete()
5487 // (this includes saved states of the machine and snapshots and
5488 // medium storage files from the IMedium list passed in, and the
5489 // machine XML file)
5490 for (StringsList::const_iterator
5491 it = task.m_llFilesToDelete.begin();
5492 it != task.m_llFilesToDelete.end();
5493 ++it)
5494 {
5495 const Utf8Str &strFile = *it;
5496 LogFunc(("Deleting file %s\n", strFile.c_str()));
5497 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5498 if (FAILED(hrc)) throw hrc;
5499 i_deleteFile(strFile);
5500 }
5501
5502 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5503 if (FAILED(hrc)) throw hrc;
5504
5505 /* delete the settings only when the file actually exists */
5506 if (mData->pMachineConfigFile->fileExists())
5507 {
5508 /* Delete any backup or uncommitted XML files. Ignore failures.
5509 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5510 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5511 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5512 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5513 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5514 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5515
5516 /* delete the Logs folder, nothing important should be left
5517 * there (we don't check for errors because the user might have
5518 * some private files there that we don't want to delete) */
5519 Utf8Str logFolder;
5520 getLogFolder(logFolder);
5521 Assert(logFolder.length());
5522 if (RTDirExists(logFolder.c_str()))
5523 {
5524 /* Delete all VBox.log[.N] files from the Logs folder
5525 * (this must be in sync with the rotation logic in
5526 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5527 * files that may have been created by the GUI. */
5528 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5529 i_deleteFile(log, true /* fIgnoreFailures */);
5530 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5531 i_deleteFile(log, true /* fIgnoreFailures */);
5532 for (ULONG i = uLogHistoryCount; i > 0; i--)
5533 {
5534 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5535 i_deleteFile(log, true /* fIgnoreFailures */);
5536 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5537 i_deleteFile(log, true /* fIgnoreFailures */);
5538 }
5539 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5540 i_deleteFile(log, true /* fIgnoreFailures */);
5541#if defined(RT_OS_WINDOWS)
5542 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5543 i_deleteFile(log, true /* fIgnoreFailures */);
5544 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5545 i_deleteFile(log, true /* fIgnoreFailures */);
5546#endif
5547
5548 RTDirRemove(logFolder.c_str());
5549 }
5550
5551 /* delete the Snapshots folder, nothing important should be left
5552 * there (we don't check for errors because the user might have
5553 * some private files there that we don't want to delete) */
5554 Utf8Str strFullSnapshotFolder;
5555 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5556 Assert(!strFullSnapshotFolder.isEmpty());
5557 if (RTDirExists(strFullSnapshotFolder.c_str()))
5558 RTDirRemove(strFullSnapshotFolder.c_str());
5559
5560 // delete the directory that contains the settings file, but only
5561 // if it matches the VM name
5562 Utf8Str settingsDir;
5563 if (i_isInOwnDir(&settingsDir))
5564 RTDirRemove(settingsDir.c_str());
5565 }
5566
5567 alock.release();
5568
5569 mParent->i_saveModifiedRegistries();
5570 }
5571 catch (HRESULT hrcXcpt)
5572 {
5573 hrc = hrcXcpt;
5574 }
5575
5576 task.m_pProgress->i_notifyComplete(hrc);
5577
5578 LogFlowThisFuncLeave();
5579}
5580
5581HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5582{
5583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5584
5585 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5586 if (FAILED(hrc)) return hrc;
5587
5588 if (mData->mRegistered)
5589 return setError(VBOX_E_INVALID_VM_STATE,
5590 tr("Cannot delete settings of a registered machine"));
5591
5592 // collect files to delete
5593 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5594 // machine config file
5595 if (mData->pMachineConfigFile->fileExists())
5596 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5597 // backup of machine config file
5598 Utf8Str strTmp(mData->m_strConfigFileFull);
5599 strTmp.append("-prev");
5600 if (RTFileExists(strTmp.c_str()))
5601 llFilesToDelete.push_back(strTmp);
5602
5603 RTCList<ComPtr<IMedium> > llMediums;
5604 for (size_t i = 0; i < aMedia.size(); ++i)
5605 {
5606 IMedium *pIMedium(aMedia[i]);
5607 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5608 if (pMedium.isNull())
5609 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5610 SafeArray<BSTR> ids;
5611 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5612 if (FAILED(hrc)) return hrc;
5613 /* At this point the medium should not have any back references
5614 * anymore. If it has it is attached to another VM and *must* not
5615 * deleted. */
5616 if (ids.size() < 1)
5617 llMediums.append(pMedium);
5618 }
5619
5620 ComObjPtr<Progress> pProgress;
5621 pProgress.createObject();
5622 hrc = pProgress->init(i_getVirtualBox(),
5623 static_cast<IMachine*>(this) /* aInitiator */,
5624 tr("Deleting files"),
5625 true /* fCancellable */,
5626 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5627 tr("Collecting file inventory"));
5628 if (FAILED(hrc))
5629 return hrc;
5630
5631 /* create and start the task on a separate thread (note that it will not
5632 * start working until we release alock) */
5633 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5634 hrc = pTask->createThread();
5635 pTask = NULL;
5636 if (FAILED(hrc))
5637 return hrc;
5638
5639 pProgress.queryInterfaceTo(aProgress.asOutParam());
5640
5641 LogFlowFuncLeave();
5642
5643 return S_OK;
5644}
5645
5646HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5647{
5648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5649
5650 ComObjPtr<Snapshot> pSnapshot;
5651 HRESULT hrc;
5652
5653 if (aNameOrId.isEmpty())
5654 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5655 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5656 else
5657 {
5658 Guid uuid(aNameOrId);
5659 if (uuid.isValid())
5660 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5661 else
5662 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5663 }
5664 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5665
5666 return hrc;
5667}
5668
5669HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5670 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5671{
5672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5673
5674 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5675 if (FAILED(hrc)) return hrc;
5676
5677 ComObjPtr<SharedFolder> sharedFolder;
5678 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5679 if (SUCCEEDED(hrc))
5680 return setError(VBOX_E_OBJECT_IN_USE,
5681 tr("Shared folder named '%s' already exists"),
5682 aName.c_str());
5683
5684 sharedFolder.createObject();
5685 hrc = sharedFolder->init(i_getMachine(),
5686 aName,
5687 aHostPath,
5688 !!aWritable,
5689 !!aAutomount,
5690 aAutoMountPoint,
5691 true /* fFailOnError */);
5692 if (FAILED(hrc)) return hrc;
5693
5694 i_setModified(IsModified_SharedFolders);
5695 mHWData.backup();
5696 mHWData->mSharedFolders.push_back(sharedFolder);
5697
5698 /* inform the direct session if any */
5699 alock.release();
5700 i_onSharedFolderChange();
5701
5702 return S_OK;
5703}
5704
5705HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5706{
5707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5708
5709 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5710 if (FAILED(hrc)) return hrc;
5711
5712 ComObjPtr<SharedFolder> sharedFolder;
5713 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5714 if (FAILED(hrc)) return hrc;
5715
5716 i_setModified(IsModified_SharedFolders);
5717 mHWData.backup();
5718 mHWData->mSharedFolders.remove(sharedFolder);
5719
5720 /* inform the direct session if any */
5721 alock.release();
5722 i_onSharedFolderChange();
5723
5724 return S_OK;
5725}
5726
5727HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5728{
5729 /* start with No */
5730 *aCanShow = FALSE;
5731
5732 ComPtr<IInternalSessionControl> directControl;
5733 {
5734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5735
5736 if (mData->mSession.mState != SessionState_Locked)
5737 return setError(VBOX_E_INVALID_VM_STATE,
5738 tr("Machine is not locked for session (session state: %s)"),
5739 Global::stringifySessionState(mData->mSession.mState));
5740
5741 if (mData->mSession.mLockType == LockType_VM)
5742 directControl = mData->mSession.mDirectControl;
5743 }
5744
5745 /* ignore calls made after #OnSessionEnd() is called */
5746 if (!directControl)
5747 return S_OK;
5748
5749 LONG64 dummy;
5750 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5751}
5752
5753HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5754{
5755 ComPtr<IInternalSessionControl> directControl;
5756 {
5757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5758
5759 if (mData->mSession.mState != SessionState_Locked)
5760 return setError(E_FAIL,
5761 tr("Machine is not locked for session (session state: %s)"),
5762 Global::stringifySessionState(mData->mSession.mState));
5763
5764 if (mData->mSession.mLockType == LockType_VM)
5765 directControl = mData->mSession.mDirectControl;
5766 }
5767
5768 /* ignore calls made after #OnSessionEnd() is called */
5769 if (!directControl)
5770 return S_OK;
5771
5772 BOOL dummy;
5773 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5774}
5775
5776#ifdef VBOX_WITH_GUEST_PROPS
5777/**
5778 * Look up a guest property in VBoxSVC's internal structures.
5779 */
5780HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5781 com::Utf8Str &aValue,
5782 LONG64 *aTimestamp,
5783 com::Utf8Str &aFlags) const
5784{
5785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5786
5787 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5788 if (it != mHWData->mGuestProperties.end())
5789 {
5790 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5791 aValue = it->second.strValue;
5792 *aTimestamp = it->second.mTimestamp;
5793 GuestPropWriteFlags(it->second.mFlags, szFlags);
5794 aFlags = Utf8Str(szFlags);
5795 }
5796
5797 return S_OK;
5798}
5799
5800/**
5801 * Query the VM that a guest property belongs to for the property.
5802 * @returns E_ACCESSDENIED if the VM process is not available or not
5803 * currently handling queries and the lookup should then be done in
5804 * VBoxSVC.
5805 */
5806HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5807 com::Utf8Str &aValue,
5808 LONG64 *aTimestamp,
5809 com::Utf8Str &aFlags) const
5810{
5811 HRESULT hrc = S_OK;
5812 Bstr bstrValue;
5813 Bstr bstrFlags;
5814
5815 ComPtr<IInternalSessionControl> directControl;
5816 {
5817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5818 if (mData->mSession.mLockType == LockType_VM)
5819 directControl = mData->mSession.mDirectControl;
5820 }
5821
5822 /* ignore calls made after #OnSessionEnd() is called */
5823 if (!directControl)
5824 hrc = E_ACCESSDENIED;
5825 else
5826 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5827 0 /* accessMode */,
5828 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5829
5830 aValue = bstrValue;
5831 aFlags = bstrFlags;
5832
5833 return hrc;
5834}
5835#endif // VBOX_WITH_GUEST_PROPS
5836
5837HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5838 com::Utf8Str &aValue,
5839 LONG64 *aTimestamp,
5840 com::Utf8Str &aFlags)
5841{
5842#ifndef VBOX_WITH_GUEST_PROPS
5843 ReturnComNotImplemented();
5844#else // VBOX_WITH_GUEST_PROPS
5845
5846 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5847
5848 if (hrc == E_ACCESSDENIED)
5849 /* The VM is not running or the service is not (yet) accessible */
5850 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5851 return hrc;
5852#endif // VBOX_WITH_GUEST_PROPS
5853}
5854
5855HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5856{
5857 LONG64 dummyTimestamp;
5858 com::Utf8Str dummyFlags;
5859 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5860
5861}
5862HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5863{
5864 com::Utf8Str dummyFlags;
5865 com::Utf8Str dummyValue;
5866 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5867}
5868
5869#ifdef VBOX_WITH_GUEST_PROPS
5870/**
5871 * Set a guest property in VBoxSVC's internal structures.
5872 */
5873HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5874 const com::Utf8Str &aFlags, bool fDelete)
5875{
5876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5877 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5878 if (FAILED(hrc)) return hrc;
5879
5880 try
5881 {
5882 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5883 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5884 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5885
5886 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5887 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5888
5889 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5890 if (it == mHWData->mGuestProperties.end())
5891 {
5892 if (!fDelete)
5893 {
5894 i_setModified(IsModified_MachineData);
5895 mHWData.backupEx();
5896
5897 RTTIMESPEC time;
5898 HWData::GuestProperty prop;
5899 prop.strValue = aValue;
5900 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5901 prop.mFlags = fFlags;
5902 mHWData->mGuestProperties[aName] = prop;
5903 }
5904 }
5905 else
5906 {
5907 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5908 {
5909 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5910 }
5911 else
5912 {
5913 i_setModified(IsModified_MachineData);
5914 mHWData.backupEx();
5915
5916 /* The backupEx() operation invalidates our iterator,
5917 * so get a new one. */
5918 it = mHWData->mGuestProperties.find(aName);
5919 Assert(it != mHWData->mGuestProperties.end());
5920
5921 if (!fDelete)
5922 {
5923 RTTIMESPEC time;
5924 it->second.strValue = aValue;
5925 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5926 it->second.mFlags = fFlags;
5927 }
5928 else
5929 mHWData->mGuestProperties.erase(it);
5930 }
5931 }
5932
5933 if (SUCCEEDED(hrc))
5934 {
5935 alock.release();
5936
5937 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5938 }
5939 }
5940 catch (std::bad_alloc &)
5941 {
5942 hrc = E_OUTOFMEMORY;
5943 }
5944
5945 return hrc;
5946}
5947
5948/**
5949 * Set a property on the VM that that property belongs to.
5950 * @returns E_ACCESSDENIED if the VM process is not available or not
5951 * currently handling queries and the setting should then be done in
5952 * VBoxSVC.
5953 */
5954HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5955 const com::Utf8Str &aFlags, bool fDelete)
5956{
5957 HRESULT hrc;
5958
5959 try
5960 {
5961 ComPtr<IInternalSessionControl> directControl;
5962 {
5963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5964 if (mData->mSession.mLockType == LockType_VM)
5965 directControl = mData->mSession.mDirectControl;
5966 }
5967
5968 Bstr dummy1; /* will not be changed (setter) */
5969 Bstr dummy2; /* will not be changed (setter) */
5970 LONG64 dummy64;
5971 if (!directControl)
5972 hrc = E_ACCESSDENIED;
5973 else
5974 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5975 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5976 fDelete ? 2 : 1 /* accessMode */,
5977 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5978 }
5979 catch (std::bad_alloc &)
5980 {
5981 hrc = E_OUTOFMEMORY;
5982 }
5983
5984 return hrc;
5985}
5986#endif // VBOX_WITH_GUEST_PROPS
5987
5988HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5989 const com::Utf8Str &aFlags)
5990{
5991#ifndef VBOX_WITH_GUEST_PROPS
5992 ReturnComNotImplemented();
5993#else // VBOX_WITH_GUEST_PROPS
5994
5995 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5996 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5997
5998 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5999 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6000
6001 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6002 if (hrc == E_ACCESSDENIED)
6003 /* The VM is not running or the service is not (yet) accessible */
6004 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6005 return hrc;
6006#endif // VBOX_WITH_GUEST_PROPS
6007}
6008
6009HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6010{
6011 return setGuestProperty(aProperty, aValue, "");
6012}
6013
6014HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6015{
6016#ifndef VBOX_WITH_GUEST_PROPS
6017 ReturnComNotImplemented();
6018#else // VBOX_WITH_GUEST_PROPS
6019 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6020 if (hrc == E_ACCESSDENIED)
6021 /* The VM is not running or the service is not (yet) accessible */
6022 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6023 return hrc;
6024#endif // VBOX_WITH_GUEST_PROPS
6025}
6026
6027#ifdef VBOX_WITH_GUEST_PROPS
6028/**
6029 * Enumerate the guest properties in VBoxSVC's internal structures.
6030 */
6031HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6032 std::vector<com::Utf8Str> &aNames,
6033 std::vector<com::Utf8Str> &aValues,
6034 std::vector<LONG64> &aTimestamps,
6035 std::vector<com::Utf8Str> &aFlags)
6036{
6037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6038 Utf8Str strPatterns(aPatterns);
6039
6040 /*
6041 * Look for matching patterns and build up a list.
6042 */
6043 HWData::GuestPropertyMap propMap;
6044 for (HWData::GuestPropertyMap::const_iterator
6045 it = mHWData->mGuestProperties.begin();
6046 it != mHWData->mGuestProperties.end();
6047 ++it)
6048 {
6049 if ( strPatterns.isEmpty()
6050 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6051 RTSTR_MAX,
6052 it->first.c_str(),
6053 RTSTR_MAX,
6054 NULL)
6055 )
6056 propMap.insert(*it);
6057 }
6058
6059 alock.release();
6060
6061 /*
6062 * And build up the arrays for returning the property information.
6063 */
6064 size_t cEntries = propMap.size();
6065
6066 aNames.resize(cEntries);
6067 aValues.resize(cEntries);
6068 aTimestamps.resize(cEntries);
6069 aFlags.resize(cEntries);
6070
6071 size_t i = 0;
6072 for (HWData::GuestPropertyMap::const_iterator
6073 it = propMap.begin();
6074 it != propMap.end();
6075 ++it, ++i)
6076 {
6077 aNames[i] = it->first;
6078 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6079 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6080
6081 aValues[i] = it->second.strValue;
6082 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6083 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6084
6085 aTimestamps[i] = it->second.mTimestamp;
6086
6087 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6088 GuestPropWriteFlags(it->second.mFlags, szFlags);
6089 aFlags[i] = szFlags;
6090 }
6091
6092 return S_OK;
6093}
6094
6095/**
6096 * Enumerate the properties managed by a VM.
6097 * @returns E_ACCESSDENIED if the VM process is not available or not
6098 * currently handling queries and the setting should then be done in
6099 * VBoxSVC.
6100 */
6101HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6102 std::vector<com::Utf8Str> &aNames,
6103 std::vector<com::Utf8Str> &aValues,
6104 std::vector<LONG64> &aTimestamps,
6105 std::vector<com::Utf8Str> &aFlags)
6106{
6107 HRESULT hrc;
6108 ComPtr<IInternalSessionControl> directControl;
6109 {
6110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6111 if (mData->mSession.mLockType == LockType_VM)
6112 directControl = mData->mSession.mDirectControl;
6113 }
6114
6115 com::SafeArray<BSTR> bNames;
6116 com::SafeArray<BSTR> bValues;
6117 com::SafeArray<LONG64> bTimestamps;
6118 com::SafeArray<BSTR> bFlags;
6119
6120 if (!directControl)
6121 hrc = E_ACCESSDENIED;
6122 else
6123 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6124 ComSafeArrayAsOutParam(bNames),
6125 ComSafeArrayAsOutParam(bValues),
6126 ComSafeArrayAsOutParam(bTimestamps),
6127 ComSafeArrayAsOutParam(bFlags));
6128 size_t i;
6129 aNames.resize(bNames.size());
6130 for (i = 0; i < bNames.size(); ++i)
6131 aNames[i] = Utf8Str(bNames[i]);
6132 aValues.resize(bValues.size());
6133 for (i = 0; i < bValues.size(); ++i)
6134 aValues[i] = Utf8Str(bValues[i]);
6135 aTimestamps.resize(bTimestamps.size());
6136 for (i = 0; i < bTimestamps.size(); ++i)
6137 aTimestamps[i] = bTimestamps[i];
6138 aFlags.resize(bFlags.size());
6139 for (i = 0; i < bFlags.size(); ++i)
6140 aFlags[i] = Utf8Str(bFlags[i]);
6141
6142 return hrc;
6143}
6144#endif // VBOX_WITH_GUEST_PROPS
6145HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6146 std::vector<com::Utf8Str> &aNames,
6147 std::vector<com::Utf8Str> &aValues,
6148 std::vector<LONG64> &aTimestamps,
6149 std::vector<com::Utf8Str> &aFlags)
6150{
6151#ifndef VBOX_WITH_GUEST_PROPS
6152 ReturnComNotImplemented();
6153#else // VBOX_WITH_GUEST_PROPS
6154
6155 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6156
6157 if (hrc == E_ACCESSDENIED)
6158 /* The VM is not running or the service is not (yet) accessible */
6159 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6160 return hrc;
6161#endif // VBOX_WITH_GUEST_PROPS
6162}
6163
6164HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6165 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6166{
6167 MediumAttachmentList atts;
6168
6169 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
6170 if (FAILED(hrc)) return hrc;
6171
6172 aMediumAttachments.resize(atts.size());
6173 size_t i = 0;
6174 for (MediumAttachmentList::const_iterator
6175 it = atts.begin();
6176 it != atts.end();
6177 ++it, ++i)
6178 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6184 LONG aControllerPort,
6185 LONG aDevice,
6186 ComPtr<IMediumAttachment> &aAttachment)
6187{
6188 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6189 aName.c_str(), aControllerPort, aDevice));
6190
6191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6192
6193 aAttachment = NULL;
6194
6195 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6196 aName,
6197 aControllerPort,
6198 aDevice);
6199 if (pAttach.isNull())
6200 return setError(VBOX_E_OBJECT_NOT_FOUND,
6201 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6202 aDevice, aControllerPort, aName.c_str());
6203
6204 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6205
6206 return S_OK;
6207}
6208
6209
6210HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6211 StorageBus_T aConnectionType,
6212 ComPtr<IStorageController> &aController)
6213{
6214 if ( (aConnectionType <= StorageBus_Null)
6215 || (aConnectionType > StorageBus_VirtioSCSI))
6216 return setError(E_INVALIDARG,
6217 tr("Invalid connection type: %d"),
6218 aConnectionType);
6219
6220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6223 if (FAILED(hrc)) return hrc;
6224
6225 /* try to find one with the name first. */
6226 ComObjPtr<StorageController> ctrl;
6227
6228 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6229 if (SUCCEEDED(hrc))
6230 return setError(VBOX_E_OBJECT_IN_USE,
6231 tr("Storage controller named '%s' already exists"),
6232 aName.c_str());
6233
6234 ctrl.createObject();
6235
6236 /* get a new instance number for the storage controller */
6237 ULONG ulInstance = 0;
6238 bool fBootable = true;
6239 for (StorageControllerList::const_iterator
6240 it = mStorageControllers->begin();
6241 it != mStorageControllers->end();
6242 ++it)
6243 {
6244 if ((*it)->i_getStorageBus() == aConnectionType)
6245 {
6246 ULONG ulCurInst = (*it)->i_getInstance();
6247
6248 if (ulCurInst >= ulInstance)
6249 ulInstance = ulCurInst + 1;
6250
6251 /* Only one controller of each type can be marked as bootable. */
6252 if ((*it)->i_getBootable())
6253 fBootable = false;
6254 }
6255 }
6256
6257 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6258 if (FAILED(hrc)) return hrc;
6259
6260 i_setModified(IsModified_Storage);
6261 mStorageControllers.backup();
6262 mStorageControllers->push_back(ctrl);
6263
6264 ctrl.queryInterfaceTo(aController.asOutParam());
6265
6266 /* inform the direct session if any */
6267 alock.release();
6268 i_onStorageControllerChange(i_getId(), aName);
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6274 ComPtr<IStorageController> &aStorageController)
6275{
6276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 ComObjPtr<StorageController> ctrl;
6279
6280 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6281 if (SUCCEEDED(hrc))
6282 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6283
6284 return hrc;
6285}
6286
6287HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6288 ULONG aInstance,
6289 ComPtr<IStorageController> &aStorageController)
6290{
6291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6292
6293 for (StorageControllerList::const_iterator
6294 it = mStorageControllers->begin();
6295 it != mStorageControllers->end();
6296 ++it)
6297 {
6298 if ( (*it)->i_getStorageBus() == aConnectionType
6299 && (*it)->i_getInstance() == aInstance)
6300 {
6301 (*it).queryInterfaceTo(aStorageController.asOutParam());
6302 return S_OK;
6303 }
6304 }
6305
6306 return setError(VBOX_E_OBJECT_NOT_FOUND,
6307 tr("Could not find a storage controller with instance number '%lu'"),
6308 aInstance);
6309}
6310
6311HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6312{
6313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6314
6315 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6316 if (FAILED(hrc)) return hrc;
6317
6318 ComObjPtr<StorageController> ctrl;
6319
6320 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6321 if (SUCCEEDED(hrc))
6322 {
6323 /* Ensure that only one controller of each type is marked as bootable. */
6324 if (aBootable == TRUE)
6325 {
6326 for (StorageControllerList::const_iterator
6327 it = mStorageControllers->begin();
6328 it != mStorageControllers->end();
6329 ++it)
6330 {
6331 ComObjPtr<StorageController> aCtrl = (*it);
6332
6333 if ( (aCtrl->i_getName() != aName)
6334 && aCtrl->i_getBootable() == TRUE
6335 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6336 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6337 {
6338 aCtrl->i_setBootable(FALSE);
6339 break;
6340 }
6341 }
6342 }
6343
6344 if (SUCCEEDED(hrc))
6345 {
6346 ctrl->i_setBootable(aBootable);
6347 i_setModified(IsModified_Storage);
6348 }
6349 }
6350
6351 if (SUCCEEDED(hrc))
6352 {
6353 /* inform the direct session if any */
6354 alock.release();
6355 i_onStorageControllerChange(i_getId(), aName);
6356 }
6357
6358 return hrc;
6359}
6360
6361HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6362{
6363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6364
6365 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6366 if (FAILED(hrc)) return hrc;
6367
6368 ComObjPtr<StorageController> ctrl;
6369 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6370 if (FAILED(hrc)) return hrc;
6371
6372 MediumAttachmentList llDetachedAttachments;
6373 {
6374 /* find all attached devices to the appropriate storage controller and detach them all */
6375 // make a temporary list because detachDevice invalidates iterators into
6376 // mMediumAttachments
6377 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6378
6379 for (MediumAttachmentList::const_iterator
6380 it = llAttachments2.begin();
6381 it != llAttachments2.end();
6382 ++it)
6383 {
6384 MediumAttachment *pAttachTemp = *it;
6385
6386 AutoCaller localAutoCaller(pAttachTemp);
6387 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
6388
6389 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6390
6391 if (pAttachTemp->i_getControllerName() == aName)
6392 {
6393 llDetachedAttachments.push_back(pAttachTemp);
6394 hrc = i_detachDevice(pAttachTemp, alock, NULL);
6395 if (FAILED(hrc)) return hrc;
6396 }
6397 }
6398 }
6399
6400 /* send event about detached devices before removing parent controller */
6401 for (MediumAttachmentList::const_iterator
6402 it = llDetachedAttachments.begin();
6403 it != llDetachedAttachments.end();
6404 ++it)
6405 {
6406 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6407 }
6408
6409 /* We can remove it now. */
6410 i_setModified(IsModified_Storage);
6411 mStorageControllers.backup();
6412
6413 ctrl->i_unshare();
6414
6415 mStorageControllers->remove(ctrl);
6416
6417 /* inform the direct session if any */
6418 alock.release();
6419 i_onStorageControllerChange(i_getId(), aName);
6420
6421 return S_OK;
6422}
6423
6424HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6425 ComPtr<IUSBController> &aController)
6426{
6427 if ( (aType <= USBControllerType_Null)
6428 || (aType >= USBControllerType_Last))
6429 return setError(E_INVALIDARG,
6430 tr("Invalid USB controller type: %d"),
6431 aType);
6432
6433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6436 if (FAILED(hrc)) return hrc;
6437
6438 /* try to find one with the same type first. */
6439 ComObjPtr<USBController> ctrl;
6440
6441 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6442 if (SUCCEEDED(hrc))
6443 return setError(VBOX_E_OBJECT_IN_USE,
6444 tr("USB controller named '%s' already exists"),
6445 aName.c_str());
6446
6447 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6448 ULONG maxInstances;
6449 hrc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6450 if (FAILED(hrc))
6451 return hrc;
6452
6453 ULONG cInstances = i_getUSBControllerCountByType(aType);
6454 if (cInstances >= maxInstances)
6455 return setError(E_INVALIDARG,
6456 tr("Too many USB controllers of this type"));
6457
6458 ctrl.createObject();
6459
6460 hrc = ctrl->init(this, aName, aType);
6461 if (FAILED(hrc)) return hrc;
6462
6463 i_setModified(IsModified_USB);
6464 mUSBControllers.backup();
6465 mUSBControllers->push_back(ctrl);
6466
6467 ctrl.queryInterfaceTo(aController.asOutParam());
6468
6469 /* inform the direct session if any */
6470 alock.release();
6471 i_onUSBControllerChange();
6472
6473 return S_OK;
6474}
6475
6476HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6477{
6478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 ComObjPtr<USBController> ctrl;
6481
6482 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6483 if (SUCCEEDED(hrc))
6484 ctrl.queryInterfaceTo(aController.asOutParam());
6485
6486 return hrc;
6487}
6488
6489HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6490 ULONG *aControllers)
6491{
6492 if ( (aType <= USBControllerType_Null)
6493 || (aType >= USBControllerType_Last))
6494 return setError(E_INVALIDARG,
6495 tr("Invalid USB controller type: %d"),
6496 aType);
6497
6498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6499
6500 ComObjPtr<USBController> ctrl;
6501
6502 *aControllers = i_getUSBControllerCountByType(aType);
6503
6504 return S_OK;
6505}
6506
6507HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6508{
6509
6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6513 if (FAILED(hrc)) return hrc;
6514
6515 ComObjPtr<USBController> ctrl;
6516 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6517 if (FAILED(hrc)) return hrc;
6518
6519 i_setModified(IsModified_USB);
6520 mUSBControllers.backup();
6521
6522 ctrl->i_unshare();
6523
6524 mUSBControllers->remove(ctrl);
6525
6526 /* inform the direct session if any */
6527 alock.release();
6528 i_onUSBControllerChange();
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6534 ULONG *aOriginX,
6535 ULONG *aOriginY,
6536 ULONG *aWidth,
6537 ULONG *aHeight,
6538 BOOL *aEnabled)
6539{
6540 uint32_t u32OriginX= 0;
6541 uint32_t u32OriginY= 0;
6542 uint32_t u32Width = 0;
6543 uint32_t u32Height = 0;
6544 uint16_t u16Flags = 0;
6545
6546#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6547 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6548#else
6549 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6550#endif
6551 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6552 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6553 if (RT_FAILURE(vrc))
6554 {
6555#ifdef RT_OS_WINDOWS
6556 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6557 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6558 * So just assign fEnable to TRUE again.
6559 * The right fix would be to change GUI API wrappers to make sure that parameters
6560 * are changed only if API succeeds.
6561 */
6562 *aEnabled = TRUE;
6563#endif
6564 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6565 tr("Saved guest size is not available (%Rrc)"),
6566 vrc);
6567 }
6568
6569 *aOriginX = u32OriginX;
6570 *aOriginY = u32OriginY;
6571 *aWidth = u32Width;
6572 *aHeight = u32Height;
6573 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6574
6575 return S_OK;
6576}
6577
6578HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6579 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6580{
6581 if (aScreenId != 0)
6582 return E_NOTIMPL;
6583
6584 if ( aBitmapFormat != BitmapFormat_BGR0
6585 && aBitmapFormat != BitmapFormat_BGRA
6586 && aBitmapFormat != BitmapFormat_RGBA
6587 && aBitmapFormat != BitmapFormat_PNG)
6588 return setError(E_NOTIMPL,
6589 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6590
6591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6592
6593 uint8_t *pu8Data = NULL;
6594 uint32_t cbData = 0;
6595 uint32_t u32Width = 0;
6596 uint32_t u32Height = 0;
6597
6598#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6599 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6600#else
6601 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6602#endif
6603 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6604 &pu8Data, &cbData, &u32Width, &u32Height);
6605 if (RT_FAILURE(vrc))
6606 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6607 tr("Saved thumbnail data is not available (%Rrc)"),
6608 vrc);
6609
6610 HRESULT hr = S_OK;
6611
6612 *aWidth = u32Width;
6613 *aHeight = u32Height;
6614
6615 if (cbData > 0)
6616 {
6617 /* Convert pixels to the format expected by the API caller. */
6618 if (aBitmapFormat == BitmapFormat_BGR0)
6619 {
6620 /* [0] B, [1] G, [2] R, [3] 0. */
6621 aData.resize(cbData);
6622 memcpy(&aData.front(), pu8Data, cbData);
6623 }
6624 else if (aBitmapFormat == BitmapFormat_BGRA)
6625 {
6626 /* [0] B, [1] G, [2] R, [3] A. */
6627 aData.resize(cbData);
6628 for (uint32_t i = 0; i < cbData; i += 4)
6629 {
6630 aData[i] = pu8Data[i];
6631 aData[i + 1] = pu8Data[i + 1];
6632 aData[i + 2] = pu8Data[i + 2];
6633 aData[i + 3] = 0xff;
6634 }
6635 }
6636 else if (aBitmapFormat == BitmapFormat_RGBA)
6637 {
6638 /* [0] R, [1] G, [2] B, [3] A. */
6639 aData.resize(cbData);
6640 for (uint32_t i = 0; i < cbData; i += 4)
6641 {
6642 aData[i] = pu8Data[i + 2];
6643 aData[i + 1] = pu8Data[i + 1];
6644 aData[i + 2] = pu8Data[i];
6645 aData[i + 3] = 0xff;
6646 }
6647 }
6648 else if (aBitmapFormat == BitmapFormat_PNG)
6649 {
6650 uint8_t *pu8PNG = NULL;
6651 uint32_t cbPNG = 0;
6652 uint32_t cxPNG = 0;
6653 uint32_t cyPNG = 0;
6654
6655 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6656
6657 if (RT_SUCCESS(vrc))
6658 {
6659 aData.resize(cbPNG);
6660 if (cbPNG)
6661 memcpy(&aData.front(), pu8PNG, cbPNG);
6662 }
6663 else
6664 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6665 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6666 vrc);
6667
6668 RTMemFree(pu8PNG);
6669 }
6670 }
6671
6672 freeSavedDisplayScreenshot(pu8Data);
6673
6674 return hr;
6675}
6676
6677HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6678 ULONG *aWidth,
6679 ULONG *aHeight,
6680 std::vector<BitmapFormat_T> &aBitmapFormats)
6681{
6682 if (aScreenId != 0)
6683 return E_NOTIMPL;
6684
6685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6686
6687 uint8_t *pu8Data = NULL;
6688 uint32_t cbData = 0;
6689 uint32_t u32Width = 0;
6690 uint32_t u32Height = 0;
6691
6692#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6693 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6694#else
6695 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6696#endif
6697 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6698 &pu8Data, &cbData, &u32Width, &u32Height);
6699
6700 if (RT_FAILURE(vrc))
6701 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6702 tr("Saved screenshot data is not available (%Rrc)"),
6703 vrc);
6704
6705 *aWidth = u32Width;
6706 *aHeight = u32Height;
6707 aBitmapFormats.resize(1);
6708 aBitmapFormats[0] = BitmapFormat_PNG;
6709
6710 freeSavedDisplayScreenshot(pu8Data);
6711
6712 return S_OK;
6713}
6714
6715HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6716 BitmapFormat_T aBitmapFormat,
6717 ULONG *aWidth,
6718 ULONG *aHeight,
6719 std::vector<BYTE> &aData)
6720{
6721 if (aScreenId != 0)
6722 return E_NOTIMPL;
6723
6724 if (aBitmapFormat != BitmapFormat_PNG)
6725 return E_NOTIMPL;
6726
6727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6728
6729 uint8_t *pu8Data = NULL;
6730 uint32_t cbData = 0;
6731 uint32_t u32Width = 0;
6732 uint32_t u32Height = 0;
6733
6734#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6735 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6736#else
6737 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6738#endif
6739 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6740 &pu8Data, &cbData, &u32Width, &u32Height);
6741
6742 if (RT_FAILURE(vrc))
6743 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6744 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6745 vrc);
6746
6747 *aWidth = u32Width;
6748 *aHeight = u32Height;
6749
6750 aData.resize(cbData);
6751 if (cbData)
6752 memcpy(&aData.front(), pu8Data, cbData);
6753
6754 freeSavedDisplayScreenshot(pu8Data);
6755
6756 return S_OK;
6757}
6758
6759HRESULT Machine::hotPlugCPU(ULONG aCpu)
6760{
6761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6762
6763 if (!mHWData->mCPUHotPlugEnabled)
6764 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6765
6766 if (aCpu >= mHWData->mCPUCount)
6767 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6768
6769 if (mHWData->mCPUAttached[aCpu])
6770 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6771
6772 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6773 if (FAILED(hrc)) return hrc;
6774
6775 alock.release();
6776 hrc = i_onCPUChange(aCpu, false);
6777 alock.acquire();
6778 if (FAILED(hrc)) return hrc;
6779
6780 i_setModified(IsModified_MachineData);
6781 mHWData.backup();
6782 mHWData->mCPUAttached[aCpu] = true;
6783
6784 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6785 if (Global::IsOnline(mData->mMachineState))
6786 i_saveSettings(NULL, alock);
6787
6788 return S_OK;
6789}
6790
6791HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6792{
6793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6794
6795 if (!mHWData->mCPUHotPlugEnabled)
6796 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6797
6798 if (aCpu >= SchemaDefs::MaxCPUCount)
6799 return setError(E_INVALIDARG,
6800 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6801 SchemaDefs::MaxCPUCount);
6802
6803 if (!mHWData->mCPUAttached[aCpu])
6804 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6805
6806 /* CPU 0 can't be detached */
6807 if (aCpu == 0)
6808 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6809
6810 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6811 if (FAILED(hrc)) return hrc;
6812
6813 alock.release();
6814 hrc = i_onCPUChange(aCpu, true);
6815 alock.acquire();
6816 if (FAILED(hrc)) return hrc;
6817
6818 i_setModified(IsModified_MachineData);
6819 mHWData.backup();
6820 mHWData->mCPUAttached[aCpu] = false;
6821
6822 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6823 if (Global::IsOnline(mData->mMachineState))
6824 i_saveSettings(NULL, alock);
6825
6826 return S_OK;
6827}
6828
6829HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6830{
6831 *aAttached = false;
6832
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 /* If hotplug is enabled the CPU is always enabled. */
6836 if (!mHWData->mCPUHotPlugEnabled)
6837 {
6838 if (aCpu < mHWData->mCPUCount)
6839 *aAttached = true;
6840 }
6841 else
6842 {
6843 if (aCpu < SchemaDefs::MaxCPUCount)
6844 *aAttached = mHWData->mCPUAttached[aCpu];
6845 }
6846
6847 return S_OK;
6848}
6849
6850HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6851{
6852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6853
6854 Utf8Str log = i_getLogFilename(aIdx);
6855 if (!RTFileExists(log.c_str()))
6856 log.setNull();
6857 aFilename = log;
6858
6859 return S_OK;
6860}
6861
6862HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6863{
6864 if (aSize < 0)
6865 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6866
6867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6868
6869 HRESULT hrc = S_OK;
6870 Utf8Str log = i_getLogFilename(aIdx);
6871
6872 /* do not unnecessarily hold the lock while doing something which does
6873 * not need the lock and potentially takes a long time. */
6874 alock.release();
6875
6876 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6877 * keeps the SOAP reply size under 1M for the webservice (we're using
6878 * base64 encoded strings for binary data for years now, avoiding the
6879 * expansion of each byte array element to approx. 25 bytes of XML. */
6880 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6881 aData.resize(cbData);
6882
6883 int vrc = VINF_SUCCESS;
6884 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6885
6886#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6887 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6888 {
6889 PCVBOXCRYPTOIF pCryptoIf = NULL;
6890 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6891 if (SUCCEEDED(hrc))
6892 {
6893 alock.acquire();
6894
6895 SecretKey *pKey = NULL;
6896 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6897 alock.release();
6898
6899 if (RT_SUCCESS(vrc))
6900 {
6901 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6902 if (RT_SUCCESS(vrc))
6903 {
6904 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6905 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6906 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6907 if (RT_SUCCESS(vrc))
6908 {
6909 RTVfsIoStrmRelease(hVfsIosLog);
6910 hVfsIosLog = hVfsIosLogDec;
6911 }
6912 }
6913
6914 pKey->release();
6915 }
6916
6917 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6918 }
6919 }
6920 else
6921 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6922#else
6923 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6924#endif
6925 if (RT_SUCCESS(vrc))
6926 {
6927 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6928 cbData ? &aData.front() : NULL, cbData,
6929 true /*fBlocking*/, &cbData);
6930 if (RT_SUCCESS(vrc))
6931 aData.resize(cbData);
6932 else
6933 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6934
6935 RTVfsIoStrmRelease(hVfsIosLog);
6936 }
6937 else
6938 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6939
6940 if (FAILED(hrc))
6941 aData.resize(0);
6942
6943 return hrc;
6944}
6945
6946
6947/**
6948 * Currently this method doesn't attach device to the running VM,
6949 * just makes sure it's plugged on next VM start.
6950 */
6951HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6952{
6953 // lock scope
6954 {
6955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6956
6957 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6958 if (FAILED(hrc)) return hrc;
6959
6960 ChipsetType_T aChipset = ChipsetType_PIIX3;
6961 COMGETTER(ChipsetType)(&aChipset);
6962
6963 if (aChipset != ChipsetType_ICH9)
6964 {
6965 return setError(E_INVALIDARG,
6966 tr("Host PCI attachment only supported with ICH9 chipset"));
6967 }
6968
6969 // check if device with this host PCI address already attached
6970 for (HWData::PCIDeviceAssignmentList::const_iterator
6971 it = mHWData->mPCIDeviceAssignments.begin();
6972 it != mHWData->mPCIDeviceAssignments.end();
6973 ++it)
6974 {
6975 LONG iHostAddress = -1;
6976 ComPtr<PCIDeviceAttachment> pAttach;
6977 pAttach = *it;
6978 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6979 if (iHostAddress == aHostAddress)
6980 return setError(E_INVALIDARG,
6981 tr("Device with host PCI address already attached to this VM"));
6982 }
6983
6984 ComObjPtr<PCIDeviceAttachment> pda;
6985 char name[32];
6986
6987 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6988 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6989 pda.createObject();
6990 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6991 i_setModified(IsModified_MachineData);
6992 mHWData.backup();
6993 mHWData->mPCIDeviceAssignments.push_back(pda);
6994 }
6995
6996 return S_OK;
6997}
6998
6999/**
7000 * Currently this method doesn't detach device from the running VM,
7001 * just makes sure it's not plugged on next VM start.
7002 */
7003HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7004{
7005 ComObjPtr<PCIDeviceAttachment> pAttach;
7006 bool fRemoved = false;
7007 HRESULT hrc;
7008
7009 // lock scope
7010 {
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012
7013 hrc = i_checkStateDependency(MutableStateDep);
7014 if (FAILED(hrc)) return hrc;
7015
7016 for (HWData::PCIDeviceAssignmentList::const_iterator
7017 it = mHWData->mPCIDeviceAssignments.begin();
7018 it != mHWData->mPCIDeviceAssignments.end();
7019 ++it)
7020 {
7021 LONG iHostAddress = -1;
7022 pAttach = *it;
7023 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7024 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7025 {
7026 i_setModified(IsModified_MachineData);
7027 mHWData.backup();
7028 mHWData->mPCIDeviceAssignments.remove(pAttach);
7029 fRemoved = true;
7030 break;
7031 }
7032 }
7033 }
7034
7035
7036 /* Fire event outside of the lock */
7037 if (fRemoved)
7038 {
7039 Assert(!pAttach.isNull());
7040 ComPtr<IEventSource> es;
7041 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
7042 Assert(SUCCEEDED(hrc));
7043 Bstr mid;
7044 hrc = this->COMGETTER(Id)(mid.asOutParam());
7045 Assert(SUCCEEDED(hrc));
7046 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7047 }
7048
7049 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7050 tr("No host PCI device %08x attached"),
7051 aHostAddress
7052 );
7053}
7054
7055HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7056{
7057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7058
7059 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7060 size_t i = 0;
7061 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7062 it = mHWData->mPCIDeviceAssignments.begin();
7063 it != mHWData->mPCIDeviceAssignments.end();
7064 ++it, ++i)
7065 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7066
7067 return S_OK;
7068}
7069
7070HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7071{
7072 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7073
7074 return S_OK;
7075}
7076
7077HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7078{
7079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7080
7081 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7082
7083 return S_OK;
7084}
7085
7086HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7087{
7088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7089 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7090 if (SUCCEEDED(hrc))
7091 {
7092 hrc = mHWData.backupEx();
7093 if (SUCCEEDED(hrc))
7094 {
7095 i_setModified(IsModified_MachineData);
7096 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7097 }
7098 }
7099 return hrc;
7100}
7101
7102HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7103{
7104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7105 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7106 return S_OK;
7107}
7108
7109HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7110{
7111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7112 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7113 if (SUCCEEDED(hrc))
7114 {
7115 hrc = mHWData.backupEx();
7116 if (SUCCEEDED(hrc))
7117 {
7118 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7119 if (SUCCEEDED(hrc))
7120 i_setModified(IsModified_MachineData);
7121 }
7122 }
7123 return hrc;
7124}
7125
7126HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7127{
7128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7129
7130 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7131
7132 return S_OK;
7133}
7134
7135HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7136{
7137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7138 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7139 if (SUCCEEDED(hrc))
7140 {
7141 hrc = mHWData.backupEx();
7142 if (SUCCEEDED(hrc))
7143 {
7144 i_setModified(IsModified_MachineData);
7145 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7146 }
7147 }
7148 return hrc;
7149}
7150
7151HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7152{
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7156
7157 return S_OK;
7158}
7159
7160HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7161{
7162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7163
7164 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7165 if ( SUCCEEDED(hrc)
7166 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7167 {
7168 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7169 int vrc;
7170
7171 if (aAutostartEnabled)
7172 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7173 else
7174 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7175
7176 if (RT_SUCCESS(vrc))
7177 {
7178 hrc = mHWData.backupEx();
7179 if (SUCCEEDED(hrc))
7180 {
7181 i_setModified(IsModified_MachineData);
7182 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7183 }
7184 }
7185 else if (vrc == VERR_NOT_SUPPORTED)
7186 hrc = setError(VBOX_E_NOT_SUPPORTED,
7187 tr("The VM autostart feature is not supported on this platform"));
7188 else if (vrc == VERR_PATH_NOT_FOUND)
7189 hrc = setError(E_FAIL,
7190 tr("The path to the autostart database is not set"));
7191 else
7192 hrc = setError(E_UNEXPECTED,
7193 aAutostartEnabled ?
7194 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7195 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7196 mUserData->s.strName.c_str(), vrc);
7197 }
7198 return hrc;
7199}
7200
7201HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7202{
7203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7206
7207 return S_OK;
7208}
7209
7210HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7211{
7212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7213 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7214 if (SUCCEEDED(hrc))
7215 {
7216 hrc = mHWData.backupEx();
7217 if (SUCCEEDED(hrc))
7218 {
7219 i_setModified(IsModified_MachineData);
7220 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7221 }
7222 }
7223 return hrc;
7224}
7225
7226HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7227{
7228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7229
7230 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7231
7232 return S_OK;
7233}
7234
7235HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7236{
7237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7238 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7239 if ( SUCCEEDED(hrc)
7240 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7241 {
7242 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7243 int vrc;
7244
7245 if (aAutostopType != AutostopType_Disabled)
7246 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7247 else
7248 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7249
7250 if (RT_SUCCESS(vrc))
7251 {
7252 hrc = mHWData.backupEx();
7253 if (SUCCEEDED(hrc))
7254 {
7255 i_setModified(IsModified_MachineData);
7256 mHWData->mAutostart.enmAutostopType = aAutostopType;
7257 }
7258 }
7259 else if (vrc == VERR_NOT_SUPPORTED)
7260 hrc = setError(VBOX_E_NOT_SUPPORTED,
7261 tr("The VM autostop feature is not supported on this platform"));
7262 else if (vrc == VERR_PATH_NOT_FOUND)
7263 hrc = setError(E_FAIL,
7264 tr("The path to the autostart database is not set"));
7265 else
7266 hrc = setError(E_UNEXPECTED,
7267 aAutostopType != AutostopType_Disabled ?
7268 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7269 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7270 mUserData->s.strName.c_str(), vrc);
7271 }
7272 return hrc;
7273}
7274
7275HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7276{
7277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7278
7279 aDefaultFrontend = mHWData->mDefaultFrontend;
7280
7281 return S_OK;
7282}
7283
7284HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7285{
7286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7287 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7288 if (SUCCEEDED(hrc))
7289 {
7290 hrc = mHWData.backupEx();
7291 if (SUCCEEDED(hrc))
7292 {
7293 i_setModified(IsModified_MachineData);
7294 mHWData->mDefaultFrontend = aDefaultFrontend;
7295 }
7296 }
7297 return hrc;
7298}
7299
7300HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7301{
7302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7303 size_t cbIcon = mUserData->s.ovIcon.size();
7304 aIcon.resize(cbIcon);
7305 if (cbIcon)
7306 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7307 return S_OK;
7308}
7309
7310HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7311{
7312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7313 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7314 if (SUCCEEDED(hrc))
7315 {
7316 i_setModified(IsModified_MachineData);
7317 mUserData.backup();
7318 size_t cbIcon = aIcon.size();
7319 mUserData->s.ovIcon.resize(cbIcon);
7320 if (cbIcon)
7321 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7322 }
7323 return hrc;
7324}
7325
7326HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7327{
7328#ifdef VBOX_WITH_USB
7329 *aUSBProxyAvailable = true;
7330#else
7331 *aUSBProxyAvailable = false;
7332#endif
7333 return S_OK;
7334}
7335
7336HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7337{
7338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7339
7340 *aVMProcessPriority = mUserData->s.enmVMPriority;
7341
7342 return S_OK;
7343}
7344
7345HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7346{
7347 RT_NOREF(aVMProcessPriority);
7348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7349 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7350 if (SUCCEEDED(hrc))
7351 {
7352 hrc = mUserData.backupEx();
7353 if (SUCCEEDED(hrc))
7354 {
7355 i_setModified(IsModified_MachineData);
7356 mUserData->s.enmVMPriority = aVMProcessPriority;
7357 }
7358 }
7359 alock.release();
7360 if (SUCCEEDED(hrc))
7361 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7362 return hrc;
7363}
7364
7365HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7366 ComPtr<IProgress> &aProgress)
7367{
7368 ComObjPtr<Progress> pP;
7369 Progress *ppP = pP;
7370 IProgress *iP = static_cast<IProgress *>(ppP);
7371 IProgress **pProgress = &iP;
7372
7373 IMachine *pTarget = aTarget;
7374
7375 /* Convert the options. */
7376 RTCList<CloneOptions_T> optList;
7377 if (aOptions.size())
7378 for (size_t i = 0; i < aOptions.size(); ++i)
7379 optList.append(aOptions[i]);
7380
7381 if (optList.contains(CloneOptions_Link))
7382 {
7383 if (!i_isSnapshotMachine())
7384 return setError(E_INVALIDARG,
7385 tr("Linked clone can only be created from a snapshot"));
7386 if (aMode != CloneMode_MachineState)
7387 return setError(E_INVALIDARG,
7388 tr("Linked clone can only be created for a single machine state"));
7389 }
7390 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7391
7392 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7393
7394 HRESULT hrc = pWorker->start(pProgress);
7395
7396 pP = static_cast<Progress *>(*pProgress);
7397 pP.queryInterfaceTo(aProgress.asOutParam());
7398
7399 return hrc;
7400
7401}
7402
7403HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7404 const com::Utf8Str &aType,
7405 ComPtr<IProgress> &aProgress)
7406{
7407 LogFlowThisFuncEnter();
7408
7409 ComObjPtr<Progress> ptrProgress;
7410 HRESULT hrc = ptrProgress.createObject();
7411 if (SUCCEEDED(hrc))
7412 {
7413 com::Utf8Str strDefaultPath;
7414 if (aTargetPath.isEmpty())
7415 i_calculateFullPath(".", strDefaultPath);
7416
7417 /* Initialize our worker task */
7418 MachineMoveVM *pTask = NULL;
7419 try
7420 {
7421 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7422 }
7423 catch (std::bad_alloc &)
7424 {
7425 return E_OUTOFMEMORY;
7426 }
7427
7428 hrc = pTask->init();//no exceptions are thrown
7429
7430 if (SUCCEEDED(hrc))
7431 {
7432 hrc = pTask->createThread();
7433 pTask = NULL; /* Consumed by createThread(). */
7434 if (SUCCEEDED(hrc))
7435 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7436 else
7437 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7438 }
7439 else
7440 delete pTask;
7441 }
7442
7443 LogFlowThisFuncLeave();
7444 return hrc;
7445
7446}
7447
7448HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7449{
7450 NOREF(aProgress);
7451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 // This check should always fail.
7454 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7455 if (FAILED(hrc)) return hrc;
7456
7457 AssertFailedReturn(E_NOTIMPL);
7458}
7459
7460HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7461{
7462 NOREF(aSavedStateFile);
7463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7464
7465 // This check should always fail.
7466 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7467 if (FAILED(hrc)) return hrc;
7468
7469 AssertFailedReturn(E_NOTIMPL);
7470}
7471
7472HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7473{
7474 NOREF(aFRemoveFile);
7475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7476
7477 // This check should always fail.
7478 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7479 if (FAILED(hrc)) return hrc;
7480
7481 AssertFailedReturn(E_NOTIMPL);
7482}
7483
7484// public methods for internal purposes
7485/////////////////////////////////////////////////////////////////////////////
7486
7487/**
7488 * Adds the given IsModified_* flag to the dirty flags of the machine.
7489 * This must be called either during i_loadSettings or under the machine write lock.
7490 * @param fl Flag
7491 * @param fAllowStateModification If state modifications are allowed.
7492 */
7493void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7494{
7495 mData->flModifications |= fl;
7496 if (fAllowStateModification && i_isStateModificationAllowed())
7497 mData->mCurrentStateModified = true;
7498}
7499
7500/**
7501 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7502 * care of the write locking.
7503 *
7504 * @param fModification The flag to add.
7505 * @param fAllowStateModification If state modifications are allowed.
7506 */
7507void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7508{
7509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7510 i_setModified(fModification, fAllowStateModification);
7511}
7512
7513/**
7514 * Saves the registry entry of this machine to the given configuration node.
7515 *
7516 * @param data Machine registry data.
7517 *
7518 * @note locks this object for reading.
7519 */
7520HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7521{
7522 AutoLimitedCaller autoCaller(this);
7523 AssertComRCReturnRC(autoCaller.hrc());
7524
7525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7526
7527 data.uuid = mData->mUuid;
7528 data.strSettingsFile = mData->m_strConfigFile;
7529
7530 return S_OK;
7531}
7532
7533/**
7534 * Calculates the absolute path of the given path taking the directory of the
7535 * machine settings file as the current directory.
7536 *
7537 * @param strPath Path to calculate the absolute path for.
7538 * @param aResult Where to put the result (used only on success, can be the
7539 * same Utf8Str instance as passed in @a aPath).
7540 * @return IPRT result.
7541 *
7542 * @note Locks this object for reading.
7543 */
7544int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7545{
7546 AutoCaller autoCaller(this);
7547 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
7548
7549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7550
7551 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7552
7553 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7554
7555 strSettingsDir.stripFilename();
7556 char szFolder[RTPATH_MAX];
7557 size_t cbFolder = sizeof(szFolder);
7558 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7559 if (RT_SUCCESS(vrc))
7560 aResult = szFolder;
7561
7562 return vrc;
7563}
7564
7565/**
7566 * Copies strSource to strTarget, making it relative to the machine folder
7567 * if it is a subdirectory thereof, or simply copying it otherwise.
7568 *
7569 * @param strSource Path to evaluate and copy.
7570 * @param strTarget Buffer to receive target path.
7571 *
7572 * @note Locks this object for reading.
7573 */
7574void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7575 Utf8Str &strTarget)
7576{
7577 AutoCaller autoCaller(this);
7578 AssertComRCReturn(autoCaller.hrc(), (void)0);
7579
7580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7581
7582 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7583 // use strTarget as a temporary buffer to hold the machine settings dir
7584 strTarget = mData->m_strConfigFileFull;
7585 strTarget.stripFilename();
7586 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7587 {
7588 // is relative: then append what's left
7589 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7590 // for empty paths (only possible for subdirs) use "." to avoid
7591 // triggering default settings for not present config attributes.
7592 if (strTarget.isEmpty())
7593 strTarget = ".";
7594 }
7595 else
7596 // is not relative: then overwrite
7597 strTarget = strSource;
7598}
7599
7600/**
7601 * Returns the full path to the machine's log folder in the
7602 * \a aLogFolder argument.
7603 */
7604void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7605{
7606 AutoCaller autoCaller(this);
7607 AssertComRCReturnVoid(autoCaller.hrc());
7608
7609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7610
7611 char szTmp[RTPATH_MAX];
7612 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7613 if (RT_SUCCESS(vrc))
7614 {
7615 if (szTmp[0] && !mUserData.isNull())
7616 {
7617 char szTmp2[RTPATH_MAX];
7618 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7619 if (RT_SUCCESS(vrc))
7620 aLogFolder.printf("%s%c%s",
7621 szTmp2,
7622 RTPATH_DELIMITER,
7623 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7624 }
7625 else
7626 vrc = VERR_PATH_IS_RELATIVE;
7627 }
7628
7629 if (RT_FAILURE(vrc))
7630 {
7631 // fallback if VBOX_USER_LOGHOME is not set or invalid
7632 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7633 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7634 aLogFolder.append(RTPATH_DELIMITER);
7635 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7636 }
7637}
7638
7639/**
7640 * Returns the full path to the machine's log file for an given index.
7641 */
7642Utf8Str Machine::i_getLogFilename(ULONG idx)
7643{
7644 Utf8Str logFolder;
7645 getLogFolder(logFolder);
7646 Assert(logFolder.length());
7647
7648 Utf8Str log;
7649 if (idx == 0)
7650 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7651#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7652 else if (idx == 1)
7653 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7654 else
7655 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7656#else
7657 else
7658 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7659#endif
7660 return log;
7661}
7662
7663/**
7664 * Returns the full path to the machine's hardened log file.
7665 */
7666Utf8Str Machine::i_getHardeningLogFilename(void)
7667{
7668 Utf8Str strFilename;
7669 getLogFolder(strFilename);
7670 Assert(strFilename.length());
7671 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7672 return strFilename;
7673}
7674
7675/**
7676 * Returns the default NVRAM filename based on the location of the VM config.
7677 * Note that this is a relative path.
7678 */
7679Utf8Str Machine::i_getDefaultNVRAMFilename()
7680{
7681 AutoCaller autoCaller(this);
7682 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7683
7684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7685
7686 if (i_isSnapshotMachine())
7687 return Utf8Str::Empty;
7688
7689 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7690 strNVRAMFilePath.stripPath();
7691 strNVRAMFilePath.stripSuffix();
7692 strNVRAMFilePath += ".nvram";
7693
7694 return strNVRAMFilePath;
7695}
7696
7697/**
7698 * Returns the NVRAM filename for a new snapshot. This intentionally works
7699 * similarly to the saved state file naming. Note that this is usually
7700 * a relative path, unless the snapshot folder is absolute.
7701 */
7702Utf8Str Machine::i_getSnapshotNVRAMFilename()
7703{
7704 AutoCaller autoCaller(this);
7705 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7706
7707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7708
7709 RTTIMESPEC ts;
7710 RTTimeNow(&ts);
7711 RTTIME time;
7712 RTTimeExplode(&time, &ts);
7713
7714 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7715 strNVRAMFilePath += RTPATH_DELIMITER;
7716 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7717 time.i32Year, time.u8Month, time.u8MonthDay,
7718 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7719
7720 return strNVRAMFilePath;
7721}
7722
7723/**
7724 * Returns the version of the settings file.
7725 */
7726SettingsVersion_T Machine::i_getSettingsVersion(void)
7727{
7728 AutoCaller autoCaller(this);
7729 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7730
7731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7732
7733 return mData->pMachineConfigFile->getSettingsVersion();
7734}
7735
7736/**
7737 * Composes a unique saved state filename based on the current system time. The filename is
7738 * granular to the second so this will work so long as no more than one snapshot is taken on
7739 * a machine per second.
7740 *
7741 * Before version 4.1, we used this formula for saved state files:
7742 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7743 * which no longer works because saved state files can now be shared between the saved state of the
7744 * "saved" machine and an online snapshot, and the following would cause problems:
7745 * 1) save machine
7746 * 2) create online snapshot from that machine state --> reusing saved state file
7747 * 3) save machine again --> filename would be reused, breaking the online snapshot
7748 *
7749 * So instead we now use a timestamp.
7750 *
7751 * @param strStateFilePath
7752 */
7753
7754void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7755{
7756 AutoCaller autoCaller(this);
7757 AssertComRCReturnVoid(autoCaller.hrc());
7758
7759 {
7760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7761 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7762 }
7763
7764 RTTIMESPEC ts;
7765 RTTimeNow(&ts);
7766 RTTIME time;
7767 RTTimeExplode(&time, &ts);
7768
7769 strStateFilePath += RTPATH_DELIMITER;
7770 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7771 time.i32Year, time.u8Month, time.u8MonthDay,
7772 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7773}
7774
7775/**
7776 * Returns whether at least one USB controller is present for the VM.
7777 */
7778bool Machine::i_isUSBControllerPresent()
7779{
7780 AutoCaller autoCaller(this);
7781 AssertComRCReturn(autoCaller.hrc(), false);
7782
7783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 return (mUSBControllers->size() > 0);
7786}
7787
7788
7789/**
7790 * @note Locks this object for writing, calls the client process
7791 * (inside the lock).
7792 */
7793HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7794 const Utf8Str &strFrontend,
7795 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7796 ProgressProxy *aProgress)
7797{
7798 LogFlowThisFuncEnter();
7799
7800 AssertReturn(aControl, E_FAIL);
7801 AssertReturn(aProgress, E_FAIL);
7802 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7803
7804 AutoCaller autoCaller(this);
7805 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7806
7807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7808
7809 if (!mData->mRegistered)
7810 return setError(E_UNEXPECTED,
7811 tr("The machine '%s' is not registered"),
7812 mUserData->s.strName.c_str());
7813
7814 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7815
7816 /* The process started when launching a VM with separate UI/VM processes is always
7817 * the UI process, i.e. needs special handling as it won't claim the session. */
7818 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7819
7820 if (fSeparate)
7821 {
7822 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7823 return setError(VBOX_E_INVALID_OBJECT_STATE,
7824 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7825 mUserData->s.strName.c_str());
7826 }
7827 else
7828 {
7829 if ( mData->mSession.mState == SessionState_Locked
7830 || mData->mSession.mState == SessionState_Spawning
7831 || mData->mSession.mState == SessionState_Unlocking)
7832 return setError(VBOX_E_INVALID_OBJECT_STATE,
7833 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7834 mUserData->s.strName.c_str());
7835
7836 /* may not be busy */
7837 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7838 }
7839
7840 /* Hardening logging */
7841#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7842 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7843 {
7844 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7845 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7846 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7847 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7848 {
7849 Utf8Str strStartupLogDir = strHardeningLogFile;
7850 strStartupLogDir.stripFilename();
7851 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7852 file without stripping the file. */
7853 }
7854 strSupHardeningLogArg.append(strHardeningLogFile);
7855
7856 /* Remove legacy log filename to avoid confusion. */
7857 Utf8Str strOldStartupLogFile;
7858 getLogFolder(strOldStartupLogFile);
7859 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7860 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7861 }
7862#else
7863 Utf8Str strSupHardeningLogArg;
7864#endif
7865
7866 Utf8Str strAppOverride;
7867#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7868 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7869#endif
7870
7871 bool fUseVBoxSDS = false;
7872 Utf8Str strCanonicalName;
7873 if (false)
7874 { }
7875#ifdef VBOX_WITH_QTGUI
7876 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7877 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7878 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7879 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7880 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7881 {
7882 strCanonicalName = "GUI/Qt";
7883 fUseVBoxSDS = true;
7884 }
7885#endif
7886#ifdef VBOX_WITH_VBOXSDL
7887 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7888 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7889 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7890 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7891 {
7892 strCanonicalName = "GUI/SDL";
7893 fUseVBoxSDS = true;
7894 }
7895#endif
7896#ifdef VBOX_WITH_HEADLESS
7897 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7898 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7899 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7900 {
7901 strCanonicalName = "headless";
7902 }
7903#endif
7904 else
7905 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7906
7907 Utf8Str idStr = mData->mUuid.toString();
7908 Utf8Str const &strMachineName = mUserData->s.strName;
7909 RTPROCESS pid = NIL_RTPROCESS;
7910
7911#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7912 RT_NOREF(fUseVBoxSDS);
7913#else
7914 DWORD idCallerSession = ~(DWORD)0;
7915 if (fUseVBoxSDS)
7916 {
7917 /*
7918 * The VBoxSDS should be used for process launching the VM with
7919 * GUI only if the caller and the VBoxSDS are in different Windows
7920 * sessions and the caller in the interactive one.
7921 */
7922 fUseVBoxSDS = false;
7923
7924 /* Get windows session of the current process. The process token used
7925 due to several reasons:
7926 1. The token is absent for the current thread except someone set it
7927 for us.
7928 2. Needs to get the id of the session where the process is started.
7929 We only need to do this once, though. */
7930 static DWORD s_idCurrentSession = ~(DWORD)0;
7931 DWORD idCurrentSession = s_idCurrentSession;
7932 if (idCurrentSession == ~(DWORD)0)
7933 {
7934 HANDLE hCurrentProcessToken = NULL;
7935 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7936 {
7937 DWORD cbIgn = 0;
7938 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7939 s_idCurrentSession = idCurrentSession;
7940 else
7941 {
7942 idCurrentSession = ~(DWORD)0;
7943 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7944 }
7945 CloseHandle(hCurrentProcessToken);
7946 }
7947 else
7948 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7949 }
7950
7951 /* get the caller's session */
7952 HRESULT hrc = CoImpersonateClient();
7953 if (SUCCEEDED(hrc))
7954 {
7955 HANDLE hCallerThreadToken;
7956 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7957 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7958 &hCallerThreadToken))
7959 {
7960 SetLastError(NO_ERROR);
7961 DWORD cbIgn = 0;
7962 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7963 {
7964 /* Only need to use SDS if the session ID differs: */
7965 if (idCurrentSession != idCallerSession)
7966 {
7967 fUseVBoxSDS = false;
7968
7969 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7970 DWORD cbTokenGroups = 0;
7971 PTOKEN_GROUPS pTokenGroups = NULL;
7972 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7973 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7974 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7975 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7976 {
7977 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7978 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7979 PSID pInteractiveSid = NULL;
7980 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7981 {
7982 /* Iterate over the groups looking for the interactive SID: */
7983 fUseVBoxSDS = false;
7984 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7985 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7986 {
7987 fUseVBoxSDS = true;
7988 break;
7989 }
7990 FreeSid(pInteractiveSid);
7991 }
7992 }
7993 else
7994 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7995 RTMemTmpFree(pTokenGroups);
7996 }
7997 }
7998 else
7999 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8000 CloseHandle(hCallerThreadToken);
8001 }
8002 else
8003 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8004 CoRevertToSelf();
8005 }
8006 else
8007 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8008 }
8009 if (fUseVBoxSDS)
8010 {
8011 /* connect to VBoxSDS */
8012 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8013 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8014 if (FAILED(hrc))
8015 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8016 strMachineName.c_str());
8017
8018 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8019 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8020 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8021 service to access the files. */
8022 hrc = CoSetProxyBlanket(pVBoxSDS,
8023 RPC_C_AUTHN_DEFAULT,
8024 RPC_C_AUTHZ_DEFAULT,
8025 COLE_DEFAULT_PRINCIPAL,
8026 RPC_C_AUTHN_LEVEL_DEFAULT,
8027 RPC_C_IMP_LEVEL_IMPERSONATE,
8028 NULL,
8029 EOAC_DEFAULT);
8030 if (FAILED(hrc))
8031 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8032
8033 size_t const cEnvVars = aEnvironmentChanges.size();
8034 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8035 for (size_t i = 0; i < cEnvVars; i++)
8036 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8037
8038 ULONG uPid = 0;
8039 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8040 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8041 idCallerSession, &uPid);
8042 if (FAILED(hrc))
8043 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8044 pid = (RTPROCESS)uPid;
8045 }
8046 else
8047#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8048 {
8049 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8050 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8051 if (RT_FAILURE(vrc))
8052 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8053 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8054 }
8055
8056 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8057 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8058
8059 if (!fSeparate)
8060 {
8061 /*
8062 * Note that we don't release the lock here before calling the client,
8063 * because it doesn't need to call us back if called with a NULL argument.
8064 * Releasing the lock here is dangerous because we didn't prepare the
8065 * launch data yet, but the client we've just started may happen to be
8066 * too fast and call LockMachine() that will fail (because of PID, etc.),
8067 * so that the Machine will never get out of the Spawning session state.
8068 */
8069
8070 /* inform the session that it will be a remote one */
8071 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8072#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8073 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8074#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8075 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8076#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8077 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
8078
8079 if (FAILED(hrc))
8080 {
8081 /* restore the session state */
8082 mData->mSession.mState = SessionState_Unlocked;
8083 alock.release();
8084 mParent->i_addProcessToReap(pid);
8085 /* The failure may occur w/o any error info (from RPC), so provide one */
8086 return setError(VBOX_E_VM_ERROR,
8087 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
8088 }
8089
8090 /* attach launch data to the machine */
8091 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8092 mData->mSession.mRemoteControls.push_back(aControl);
8093 mData->mSession.mProgress = aProgress;
8094 mData->mSession.mPID = pid;
8095 mData->mSession.mState = SessionState_Spawning;
8096 Assert(strCanonicalName.isNotEmpty());
8097 mData->mSession.mName = strCanonicalName;
8098 }
8099 else
8100 {
8101 /* For separate UI process we declare the launch as completed instantly, as the
8102 * actual headless VM start may or may not come. No point in remembering anything
8103 * yet, as what matters for us is when the headless VM gets started. */
8104 aProgress->i_notifyComplete(S_OK);
8105 }
8106
8107 alock.release();
8108 mParent->i_addProcessToReap(pid);
8109
8110 LogFlowThisFuncLeave();
8111 return S_OK;
8112}
8113
8114/**
8115 * Returns @c true if the given session machine instance has an open direct
8116 * session (and optionally also for direct sessions which are closing) and
8117 * returns the session control machine instance if so.
8118 *
8119 * Note that when the method returns @c false, the arguments remain unchanged.
8120 *
8121 * @param aMachine Session machine object.
8122 * @param aControl Direct session control object (optional).
8123 * @param aRequireVM If true then only allow VM sessions.
8124 * @param aAllowClosing If true then additionally a session which is currently
8125 * being closed will also be allowed.
8126 *
8127 * @note locks this object for reading.
8128 */
8129bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8130 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8131 bool aRequireVM /*= false*/,
8132 bool aAllowClosing /*= false*/)
8133{
8134 AutoLimitedCaller autoCaller(this);
8135 AssertComRCReturn(autoCaller.hrc(), false);
8136
8137 /* just return false for inaccessible machines */
8138 if (getObjectState().getState() != ObjectState::Ready)
8139 return false;
8140
8141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8142
8143 if ( ( mData->mSession.mState == SessionState_Locked
8144 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8145 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8146 )
8147 {
8148 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8149
8150 aMachine = mData->mSession.mMachine;
8151
8152 if (aControl != NULL)
8153 *aControl = mData->mSession.mDirectControl;
8154
8155 return true;
8156 }
8157
8158 return false;
8159}
8160
8161/**
8162 * Returns @c true if the given machine has an spawning direct session.
8163 *
8164 * @note locks this object for reading.
8165 */
8166bool Machine::i_isSessionSpawning()
8167{
8168 AutoLimitedCaller autoCaller(this);
8169 AssertComRCReturn(autoCaller.hrc(), false);
8170
8171 /* just return false for inaccessible machines */
8172 if (getObjectState().getState() != ObjectState::Ready)
8173 return false;
8174
8175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8176
8177 if (mData->mSession.mState == SessionState_Spawning)
8178 return true;
8179
8180 return false;
8181}
8182
8183/**
8184 * Called from the client watcher thread to check for unexpected client process
8185 * death during Session_Spawning state (e.g. before it successfully opened a
8186 * direct session).
8187 *
8188 * On Win32 and on OS/2, this method is called only when we've got the
8189 * direct client's process termination notification, so it always returns @c
8190 * true.
8191 *
8192 * On other platforms, this method returns @c true if the client process is
8193 * terminated and @c false if it's still alive.
8194 *
8195 * @note Locks this object for writing.
8196 */
8197bool Machine::i_checkForSpawnFailure()
8198{
8199 AutoCaller autoCaller(this);
8200 if (!autoCaller.isOk())
8201 {
8202 /* nothing to do */
8203 LogFlowThisFunc(("Already uninitialized!\n"));
8204 return true;
8205 }
8206
8207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8208
8209 if (mData->mSession.mState != SessionState_Spawning)
8210 {
8211 /* nothing to do */
8212 LogFlowThisFunc(("Not spawning any more!\n"));
8213 return true;
8214 }
8215
8216 /* PID not yet initialized, skip check. */
8217 if (mData->mSession.mPID == NIL_RTPROCESS)
8218 return false;
8219
8220 HRESULT hrc = S_OK;
8221 RTPROCSTATUS status;
8222 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8223 if (vrc != VERR_PROCESS_RUNNING)
8224 {
8225 Utf8Str strExtraInfo;
8226
8227#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8228 /* If the startup logfile exists and is of non-zero length, tell the
8229 user to look there for more details to encourage them to attach it
8230 when reporting startup issues. */
8231 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8232 uint64_t cbStartupLogFile = 0;
8233 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8234 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8235 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8236#endif
8237
8238 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8239 hrc = setError(E_FAIL,
8240 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8241 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8242 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8243 hrc = setError(E_FAIL,
8244 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8245 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8246 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8247 hrc = setError(E_FAIL,
8248 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8249 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8250 else
8251 hrc = setErrorBoth(E_FAIL, vrc,
8252 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8253 i_getName().c_str(), vrc, strExtraInfo.c_str());
8254 }
8255
8256 if (FAILED(hrc))
8257 {
8258 /* Close the remote session, remove the remote control from the list
8259 * and reset session state to Closed (@note keep the code in sync with
8260 * the relevant part in LockMachine()). */
8261
8262 Assert(mData->mSession.mRemoteControls.size() == 1);
8263 if (mData->mSession.mRemoteControls.size() == 1)
8264 {
8265 ErrorInfoKeeper eik;
8266 mData->mSession.mRemoteControls.front()->Uninitialize();
8267 }
8268
8269 mData->mSession.mRemoteControls.clear();
8270 mData->mSession.mState = SessionState_Unlocked;
8271
8272 /* finalize the progress after setting the state */
8273 if (!mData->mSession.mProgress.isNull())
8274 {
8275 mData->mSession.mProgress->notifyComplete(hrc);
8276 mData->mSession.mProgress.setNull();
8277 }
8278
8279 mData->mSession.mPID = NIL_RTPROCESS;
8280
8281 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8282 return true;
8283 }
8284
8285 return false;
8286}
8287
8288/**
8289 * Checks whether the machine can be registered. If so, commits and saves
8290 * all settings.
8291 *
8292 * @note Must be called from mParent's write lock. Locks this object and
8293 * children for writing.
8294 */
8295HRESULT Machine::i_prepareRegister()
8296{
8297 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8298
8299 AutoLimitedCaller autoCaller(this);
8300 AssertComRCReturnRC(autoCaller.hrc());
8301
8302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8303
8304 /* wait for state dependents to drop to zero */
8305 i_ensureNoStateDependencies(alock);
8306
8307 if (!mData->mAccessible)
8308 return setError(VBOX_E_INVALID_OBJECT_STATE,
8309 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8310 mUserData->s.strName.c_str(),
8311 mData->mUuid.toString().c_str());
8312
8313 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8314
8315 if (mData->mRegistered)
8316 return setError(VBOX_E_INVALID_OBJECT_STATE,
8317 tr("The machine '%s' with UUID {%s} is already registered"),
8318 mUserData->s.strName.c_str(),
8319 mData->mUuid.toString().c_str());
8320
8321 HRESULT hrc = S_OK;
8322
8323 // Ensure the settings are saved. If we are going to be registered and
8324 // no config file exists yet, create it by calling i_saveSettings() too.
8325 if ( (mData->flModifications)
8326 || (!mData->pMachineConfigFile->fileExists())
8327 )
8328 {
8329 hrc = i_saveSettings(NULL, alock);
8330 // no need to check whether VirtualBox.xml needs saving too since
8331 // we can't have a machine XML file rename pending
8332 if (FAILED(hrc)) return hrc;
8333 }
8334
8335 /* more config checking goes here */
8336
8337 if (SUCCEEDED(hrc))
8338 {
8339 /* we may have had implicit modifications we want to fix on success */
8340 i_commit();
8341
8342 mData->mRegistered = true;
8343 }
8344 else
8345 {
8346 /* we may have had implicit modifications we want to cancel on failure*/
8347 i_rollback(false /* aNotify */);
8348 }
8349
8350 return hrc;
8351}
8352
8353/**
8354 * Increases the number of objects dependent on the machine state or on the
8355 * registered state. Guarantees that these two states will not change at least
8356 * until #i_releaseStateDependency() is called.
8357 *
8358 * Depending on the @a aDepType value, additional state checks may be made.
8359 * These checks will set extended error info on failure. See
8360 * #i_checkStateDependency() for more info.
8361 *
8362 * If this method returns a failure, the dependency is not added and the caller
8363 * is not allowed to rely on any particular machine state or registration state
8364 * value and may return the failed result code to the upper level.
8365 *
8366 * @param aDepType Dependency type to add.
8367 * @param aState Current machine state (NULL if not interested).
8368 * @param aRegistered Current registered state (NULL if not interested).
8369 *
8370 * @note Locks this object for writing.
8371 */
8372HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8373 MachineState_T *aState /* = NULL */,
8374 BOOL *aRegistered /* = NULL */)
8375{
8376 AutoCaller autoCaller(this);
8377 AssertComRCReturnRC(autoCaller.hrc());
8378
8379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8380
8381 HRESULT hrc = i_checkStateDependency(aDepType);
8382 if (FAILED(hrc)) return hrc;
8383
8384 {
8385 if (mData->mMachineStateChangePending != 0)
8386 {
8387 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8388 * drop to zero so don't add more. It may make sense to wait a bit
8389 * and retry before reporting an error (since the pending state
8390 * transition should be really quick) but let's just assert for
8391 * now to see if it ever happens on practice. */
8392
8393 AssertFailed();
8394
8395 return setError(E_ACCESSDENIED,
8396 tr("Machine state change is in progress. Please retry the operation later."));
8397 }
8398
8399 ++mData->mMachineStateDeps;
8400 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8401 }
8402
8403 if (aState)
8404 *aState = mData->mMachineState;
8405 if (aRegistered)
8406 *aRegistered = mData->mRegistered;
8407
8408 return S_OK;
8409}
8410
8411/**
8412 * Decreases the number of objects dependent on the machine state.
8413 * Must always complete the #i_addStateDependency() call after the state
8414 * dependency is no more necessary.
8415 */
8416void Machine::i_releaseStateDependency()
8417{
8418 AutoCaller autoCaller(this);
8419 AssertComRCReturnVoid(autoCaller.hrc());
8420
8421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8422
8423 /* releaseStateDependency() w/o addStateDependency()? */
8424 AssertReturnVoid(mData->mMachineStateDeps != 0);
8425 -- mData->mMachineStateDeps;
8426
8427 if (mData->mMachineStateDeps == 0)
8428 {
8429 /* inform i_ensureNoStateDependencies() that there are no more deps */
8430 if (mData->mMachineStateChangePending != 0)
8431 {
8432 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8433 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8434 }
8435 }
8436}
8437
8438Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8439{
8440 /* start with nothing found */
8441 Utf8Str strResult("");
8442
8443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8444
8445 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8446 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8447 // found:
8448 strResult = it->second; // source is a Utf8Str
8449
8450 return strResult;
8451}
8452
8453// protected methods
8454/////////////////////////////////////////////////////////////////////////////
8455
8456/**
8457 * Performs machine state checks based on the @a aDepType value. If a check
8458 * fails, this method will set extended error info, otherwise it will return
8459 * S_OK. It is supposed, that on failure, the caller will immediately return
8460 * the return value of this method to the upper level.
8461 *
8462 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8463 *
8464 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8465 * current state of this machine object allows to change settings of the
8466 * machine (i.e. the machine is not registered, or registered but not running
8467 * and not saved). It is useful to call this method from Machine setters
8468 * before performing any change.
8469 *
8470 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8471 * as for MutableStateDep except that if the machine is saved, S_OK is also
8472 * returned. This is useful in setters which allow changing machine
8473 * properties when it is in the saved state.
8474 *
8475 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8476 * if the current state of this machine object allows to change runtime
8477 * changeable settings of the machine (i.e. the machine is not registered, or
8478 * registered but either running or not running and not saved). It is useful
8479 * to call this method from Machine setters before performing any changes to
8480 * runtime changeable settings.
8481 *
8482 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8483 * the same as for MutableOrRunningStateDep except that if the machine is
8484 * saved, S_OK is also returned. This is useful in setters which allow
8485 * changing runtime and saved state changeable machine properties.
8486 *
8487 * @param aDepType Dependency type to check.
8488 *
8489 * @note Non Machine based classes should use #i_addStateDependency() and
8490 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8491 * template.
8492 *
8493 * @note This method must be called from under this object's read or write
8494 * lock.
8495 */
8496HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8497{
8498 switch (aDepType)
8499 {
8500 case AnyStateDep:
8501 {
8502 break;
8503 }
8504 case MutableStateDep:
8505 {
8506 if ( mData->mRegistered
8507 && ( !i_isSessionMachine()
8508 || ( mData->mMachineState != MachineState_Aborted
8509 && mData->mMachineState != MachineState_Teleported
8510 && mData->mMachineState != MachineState_PoweredOff
8511 )
8512 )
8513 )
8514 return setError(VBOX_E_INVALID_VM_STATE,
8515 tr("The machine is not mutable (state is %s)"),
8516 Global::stringifyMachineState(mData->mMachineState));
8517 break;
8518 }
8519 case MutableOrSavedStateDep:
8520 {
8521 if ( mData->mRegistered
8522 && ( !i_isSessionMachine()
8523 || ( mData->mMachineState != MachineState_Aborted
8524 && mData->mMachineState != MachineState_Teleported
8525 && mData->mMachineState != MachineState_Saved
8526 && mData->mMachineState != MachineState_AbortedSaved
8527 && mData->mMachineState != MachineState_PoweredOff
8528 )
8529 )
8530 )
8531 return setError(VBOX_E_INVALID_VM_STATE,
8532 tr("The machine is not mutable or saved (state is %s)"),
8533 Global::stringifyMachineState(mData->mMachineState));
8534 break;
8535 }
8536 case MutableOrRunningStateDep:
8537 {
8538 if ( mData->mRegistered
8539 && ( !i_isSessionMachine()
8540 || ( mData->mMachineState != MachineState_Aborted
8541 && mData->mMachineState != MachineState_Teleported
8542 && mData->mMachineState != MachineState_PoweredOff
8543 && !Global::IsOnline(mData->mMachineState)
8544 )
8545 )
8546 )
8547 return setError(VBOX_E_INVALID_VM_STATE,
8548 tr("The machine is not mutable or running (state is %s)"),
8549 Global::stringifyMachineState(mData->mMachineState));
8550 break;
8551 }
8552 case MutableOrSavedOrRunningStateDep:
8553 {
8554 if ( mData->mRegistered
8555 && ( !i_isSessionMachine()
8556 || ( mData->mMachineState != MachineState_Aborted
8557 && mData->mMachineState != MachineState_Teleported
8558 && mData->mMachineState != MachineState_Saved
8559 && mData->mMachineState != MachineState_AbortedSaved
8560 && mData->mMachineState != MachineState_PoweredOff
8561 && !Global::IsOnline(mData->mMachineState)
8562 )
8563 )
8564 )
8565 return setError(VBOX_E_INVALID_VM_STATE,
8566 tr("The machine is not mutable, saved or running (state is %s)"),
8567 Global::stringifyMachineState(mData->mMachineState));
8568 break;
8569 }
8570 }
8571
8572 return S_OK;
8573}
8574
8575/**
8576 * Helper to initialize all associated child objects and allocate data
8577 * structures.
8578 *
8579 * This method must be called as a part of the object's initialization procedure
8580 * (usually done in the #init() method).
8581 *
8582 * @note Must be called only from #init() or from #i_registeredInit().
8583 */
8584HRESULT Machine::initDataAndChildObjects()
8585{
8586 AutoCaller autoCaller(this);
8587 AssertComRCReturnRC(autoCaller.hrc());
8588 AssertReturn( getObjectState().getState() == ObjectState::InInit
8589 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8590
8591 AssertReturn(!mData->mAccessible, E_FAIL);
8592
8593 /* allocate data structures */
8594 mSSData.allocate();
8595 mUserData.allocate();
8596 mHWData.allocate();
8597 mMediumAttachments.allocate();
8598 mStorageControllers.allocate();
8599 mUSBControllers.allocate();
8600
8601 /* initialize mOSTypeId */
8602 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8603
8604/** @todo r=bird: init() methods never fails, right? Why don't we make them
8605 * return void then! */
8606
8607 /* create associated BIOS settings object */
8608 unconst(mBIOSSettings).createObject();
8609 mBIOSSettings->init(this);
8610
8611 /* create associated recording settings object */
8612 unconst(mRecordingSettings).createObject();
8613 mRecordingSettings->init(this);
8614
8615 /* create associated trusted platform module object */
8616 unconst(mTrustedPlatformModule).createObject();
8617 mTrustedPlatformModule->init(this);
8618
8619 /* create associated NVRAM store object */
8620 unconst(mNvramStore).createObject();
8621 mNvramStore->init(this);
8622
8623 /* create the graphics adapter object (always present) */
8624 unconst(mGraphicsAdapter).createObject();
8625 mGraphicsAdapter->init(this);
8626
8627 /* create an associated VRDE object (default is disabled) */
8628 unconst(mVRDEServer).createObject();
8629 mVRDEServer->init(this);
8630
8631 /* create associated serial port objects */
8632 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8633 {
8634 unconst(mSerialPorts[slot]).createObject();
8635 mSerialPorts[slot]->init(this, slot);
8636 }
8637
8638 /* create associated parallel port objects */
8639 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8640 {
8641 unconst(mParallelPorts[slot]).createObject();
8642 mParallelPorts[slot]->init(this, slot);
8643 }
8644
8645 /* create the audio settings object */
8646 unconst(mAudioSettings).createObject();
8647 mAudioSettings->init(this);
8648
8649 /* create the USB device filters object (always present) */
8650 unconst(mUSBDeviceFilters).createObject();
8651 mUSBDeviceFilters->init(this);
8652
8653 /* create associated network adapter objects */
8654 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8655 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8656 {
8657 unconst(mNetworkAdapters[slot]).createObject();
8658 mNetworkAdapters[slot]->init(this, slot);
8659 }
8660
8661 /* create the bandwidth control */
8662 unconst(mBandwidthControl).createObject();
8663 mBandwidthControl->init(this);
8664
8665 /* create the guest debug control object */
8666 unconst(mGuestDebugControl).createObject();
8667 mGuestDebugControl->init(this);
8668
8669 return S_OK;
8670}
8671
8672/**
8673 * Helper to uninitialize all associated child objects and to free all data
8674 * structures.
8675 *
8676 * This method must be called as a part of the object's uninitialization
8677 * procedure (usually done in the #uninit() method).
8678 *
8679 * @note Must be called only from #uninit() or from #i_registeredInit().
8680 */
8681void Machine::uninitDataAndChildObjects()
8682{
8683 AutoCaller autoCaller(this);
8684 AssertComRCReturnVoid(autoCaller.hrc());
8685 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8686 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8687 || getObjectState().getState() == ObjectState::InUninit
8688 || getObjectState().getState() == ObjectState::Limited);
8689
8690 /* tell all our other child objects we've been uninitialized */
8691 if (mGuestDebugControl)
8692 {
8693 mGuestDebugControl->uninit();
8694 unconst(mGuestDebugControl).setNull();
8695 }
8696
8697 if (mBandwidthControl)
8698 {
8699 mBandwidthControl->uninit();
8700 unconst(mBandwidthControl).setNull();
8701 }
8702
8703 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8704 {
8705 if (mNetworkAdapters[slot])
8706 {
8707 mNetworkAdapters[slot]->uninit();
8708 unconst(mNetworkAdapters[slot]).setNull();
8709 }
8710 }
8711
8712 if (mUSBDeviceFilters)
8713 {
8714 mUSBDeviceFilters->uninit();
8715 unconst(mUSBDeviceFilters).setNull();
8716 }
8717
8718 if (mAudioSettings)
8719 {
8720 mAudioSettings->uninit();
8721 unconst(mAudioSettings).setNull();
8722 }
8723
8724 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8725 {
8726 if (mParallelPorts[slot])
8727 {
8728 mParallelPorts[slot]->uninit();
8729 unconst(mParallelPorts[slot]).setNull();
8730 }
8731 }
8732
8733 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8734 {
8735 if (mSerialPorts[slot])
8736 {
8737 mSerialPorts[slot]->uninit();
8738 unconst(mSerialPorts[slot]).setNull();
8739 }
8740 }
8741
8742 if (mVRDEServer)
8743 {
8744 mVRDEServer->uninit();
8745 unconst(mVRDEServer).setNull();
8746 }
8747
8748 if (mGraphicsAdapter)
8749 {
8750 mGraphicsAdapter->uninit();
8751 unconst(mGraphicsAdapter).setNull();
8752 }
8753
8754 if (mBIOSSettings)
8755 {
8756 mBIOSSettings->uninit();
8757 unconst(mBIOSSettings).setNull();
8758 }
8759
8760 if (mRecordingSettings)
8761 {
8762 mRecordingSettings->uninit();
8763 unconst(mRecordingSettings).setNull();
8764 }
8765
8766 if (mTrustedPlatformModule)
8767 {
8768 mTrustedPlatformModule->uninit();
8769 unconst(mTrustedPlatformModule).setNull();
8770 }
8771
8772 if (mNvramStore)
8773 {
8774 mNvramStore->uninit();
8775 unconst(mNvramStore).setNull();
8776 }
8777
8778 /* Deassociate media (only when a real Machine or a SnapshotMachine
8779 * instance is uninitialized; SessionMachine instances refer to real
8780 * Machine media). This is necessary for a clean re-initialization of
8781 * the VM after successfully re-checking the accessibility state. Note
8782 * that in case of normal Machine or SnapshotMachine uninitialization (as
8783 * a result of unregistering or deleting the snapshot), outdated media
8784 * attachments will already be uninitialized and deleted, so this
8785 * code will not affect them. */
8786 if ( !mMediumAttachments.isNull()
8787 && !i_isSessionMachine()
8788 )
8789 {
8790 for (MediumAttachmentList::const_iterator
8791 it = mMediumAttachments->begin();
8792 it != mMediumAttachments->end();
8793 ++it)
8794 {
8795 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8796 if (pMedium.isNull())
8797 continue;
8798 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8799 AssertComRC(hrc);
8800 }
8801 }
8802
8803 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8804 {
8805 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8806 if (mData->mFirstSnapshot)
8807 {
8808 // Snapshots tree is protected by machine write lock.
8809 // Otherwise we assert in Snapshot::uninit()
8810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8811 mData->mFirstSnapshot->uninit();
8812 mData->mFirstSnapshot.setNull();
8813 }
8814
8815 mData->mCurrentSnapshot.setNull();
8816 }
8817
8818 /* free data structures (the essential mData structure is not freed here
8819 * since it may be still in use) */
8820 mMediumAttachments.free();
8821 mStorageControllers.free();
8822 mUSBControllers.free();
8823 mHWData.free();
8824 mUserData.free();
8825 mSSData.free();
8826}
8827
8828/**
8829 * Returns a pointer to the Machine object for this machine that acts like a
8830 * parent for complex machine data objects such as shared folders, etc.
8831 *
8832 * For primary Machine objects and for SnapshotMachine objects, returns this
8833 * object's pointer itself. For SessionMachine objects, returns the peer
8834 * (primary) machine pointer.
8835 */
8836Machine *Machine::i_getMachine()
8837{
8838 if (i_isSessionMachine())
8839 return (Machine*)mPeer;
8840 return this;
8841}
8842
8843/**
8844 * Makes sure that there are no machine state dependents. If necessary, waits
8845 * for the number of dependents to drop to zero.
8846 *
8847 * Make sure this method is called from under this object's write lock to
8848 * guarantee that no new dependents may be added when this method returns
8849 * control to the caller.
8850 *
8851 * @note Receives a lock to this object for writing. The lock will be released
8852 * while waiting (if necessary).
8853 *
8854 * @warning To be used only in methods that change the machine state!
8855 */
8856void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8857{
8858 AssertReturnVoid(isWriteLockOnCurrentThread());
8859
8860 /* Wait for all state dependents if necessary */
8861 if (mData->mMachineStateDeps != 0)
8862 {
8863 /* lazy semaphore creation */
8864 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8865 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8866
8867 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8868 mData->mMachineStateDeps));
8869
8870 ++mData->mMachineStateChangePending;
8871
8872 /* reset the semaphore before waiting, the last dependent will signal
8873 * it */
8874 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8875
8876 alock.release();
8877
8878 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8879
8880 alock.acquire();
8881
8882 -- mData->mMachineStateChangePending;
8883 }
8884}
8885
8886/**
8887 * Changes the machine state and informs callbacks.
8888 *
8889 * This method is not intended to fail so it either returns S_OK or asserts (and
8890 * returns a failure).
8891 *
8892 * @note Locks this object for writing.
8893 */
8894HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8895{
8896 LogFlowThisFuncEnter();
8897 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8898 Assert(aMachineState != MachineState_Null);
8899
8900 AutoCaller autoCaller(this);
8901 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8902
8903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8904
8905 /* wait for state dependents to drop to zero */
8906 i_ensureNoStateDependencies(alock);
8907
8908 MachineState_T const enmOldState = mData->mMachineState;
8909 if (enmOldState != aMachineState)
8910 {
8911 mData->mMachineState = aMachineState;
8912 RTTimeNow(&mData->mLastStateChange);
8913
8914#ifdef VBOX_WITH_DTRACE_R3_MAIN
8915 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8916#endif
8917 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8918 }
8919
8920 LogFlowThisFuncLeave();
8921 return S_OK;
8922}
8923
8924/**
8925 * Searches for a shared folder with the given logical name
8926 * in the collection of shared folders.
8927 *
8928 * @param aName logical name of the shared folder
8929 * @param aSharedFolder where to return the found object
8930 * @param aSetError whether to set the error info if the folder is
8931 * not found
8932 * @return
8933 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8934 *
8935 * @note
8936 * must be called from under the object's lock!
8937 */
8938HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8939 ComObjPtr<SharedFolder> &aSharedFolder,
8940 bool aSetError /* = false */)
8941{
8942 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8943 for (HWData::SharedFolderList::const_iterator
8944 it = mHWData->mSharedFolders.begin();
8945 it != mHWData->mSharedFolders.end();
8946 ++it)
8947 {
8948 SharedFolder *pSF = *it;
8949 AutoCaller autoCaller(pSF);
8950 if (pSF->i_getName() == aName)
8951 {
8952 aSharedFolder = pSF;
8953 hrc = S_OK;
8954 break;
8955 }
8956 }
8957
8958 if (aSetError && FAILED(hrc))
8959 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8960
8961 return hrc;
8962}
8963
8964/**
8965 * Initializes all machine instance data from the given settings structures
8966 * from XML. The exception is the machine UUID which needs special handling
8967 * depending on the caller's use case, so the caller needs to set that herself.
8968 *
8969 * This gets called in several contexts during machine initialization:
8970 *
8971 * -- When machine XML exists on disk already and needs to be loaded into memory,
8972 * for example, from #i_registeredInit() to load all registered machines on
8973 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8974 * attached to the machine should be part of some media registry already.
8975 *
8976 * -- During OVF import, when a machine config has been constructed from an
8977 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8978 * ensure that the media listed as attachments in the config (which have
8979 * been imported from the OVF) receive the correct registry ID.
8980 *
8981 * -- During VM cloning.
8982 *
8983 * @param config Machine settings from XML.
8984 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8985 * for each attached medium in the config.
8986 * @return
8987 */
8988HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8989 const Guid *puuidRegistry)
8990{
8991 // copy name, description, OS type, teleporter, UTC etc.
8992 mUserData->s = config.machineUserData;
8993
8994 // look up the object by Id to check it is valid
8995 ComObjPtr<GuestOSType> pGuestOSType;
8996 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8997 if (!pGuestOSType.isNull())
8998 mUserData->s.strOsType = pGuestOSType->i_id();
8999
9000#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9001 // stateFile encryption (optional)
9002 mSSData->strStateKeyId = config.strStateKeyId;
9003 mSSData->strStateKeyStore = config.strStateKeyStore;
9004 mData->mstrLogKeyId = config.strLogKeyId;
9005 mData->mstrLogKeyStore = config.strLogKeyStore;
9006#endif
9007
9008 // stateFile (optional)
9009 if (config.strStateFile.isEmpty())
9010 mSSData->strStateFilePath.setNull();
9011 else
9012 {
9013 Utf8Str stateFilePathFull(config.strStateFile);
9014 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9015 if (RT_FAILURE(vrc))
9016 return setErrorBoth(E_FAIL, vrc,
9017 tr("Invalid saved state file path '%s' (%Rrc)"),
9018 config.strStateFile.c_str(),
9019 vrc);
9020 mSSData->strStateFilePath = stateFilePathFull;
9021 }
9022
9023 // snapshot folder needs special processing so set it again
9024 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9025 if (FAILED(hrc)) return hrc;
9026
9027 /* Copy the extra data items (config may or may not be the same as
9028 * mData->pMachineConfigFile) if necessary. When loading the XML files
9029 * from disk they are the same, but not for OVF import. */
9030 if (mData->pMachineConfigFile != &config)
9031 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9032
9033 /* currentStateModified (optional, default is true) */
9034 mData->mCurrentStateModified = config.fCurrentStateModified;
9035
9036 mData->mLastStateChange = config.timeLastStateChange;
9037
9038 /*
9039 * note: all mUserData members must be assigned prior this point because
9040 * we need to commit changes in order to let mUserData be shared by all
9041 * snapshot machine instances.
9042 */
9043 mUserData.commitCopy();
9044
9045 // machine registry, if present (must be loaded before snapshots)
9046 if (config.canHaveOwnMediaRegistry())
9047 {
9048 // determine machine folder
9049 Utf8Str strMachineFolder = i_getSettingsFileFull();
9050 strMachineFolder.stripFilename();
9051 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9052 config.mediaRegistry,
9053 strMachineFolder);
9054 if (FAILED(hrc)) return hrc;
9055 }
9056
9057 /* Snapshot node (optional) */
9058 size_t cRootSnapshots;
9059 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9060 {
9061 // there must be only one root snapshot
9062 Assert(cRootSnapshots == 1);
9063 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9064
9065 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
9066 if (FAILED(hrc)) return hrc;
9067 }
9068
9069 // hardware data
9070 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9071 config.recordingSettings);
9072 if (FAILED(hrc)) return hrc;
9073
9074 /*
9075 * NOTE: the assignment below must be the last thing to do,
9076 * otherwise it will be not possible to change the settings
9077 * somewhere in the code above because all setters will be
9078 * blocked by i_checkStateDependency(MutableStateDep).
9079 */
9080
9081 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9082 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9083 {
9084 /* no need to use i_setMachineState() during init() */
9085 mData->mMachineState = MachineState_AbortedSaved;
9086 }
9087 else if (config.fAborted)
9088 {
9089 mSSData->strStateFilePath.setNull();
9090
9091 /* no need to use i_setMachineState() during init() */
9092 mData->mMachineState = MachineState_Aborted;
9093 }
9094 else if (!mSSData->strStateFilePath.isEmpty())
9095 {
9096 /* no need to use i_setMachineState() during init() */
9097 mData->mMachineState = MachineState_Saved;
9098 }
9099
9100 // after loading settings, we are no longer different from the XML on disk
9101 mData->flModifications = 0;
9102
9103 return S_OK;
9104}
9105
9106/**
9107 * Loads all snapshots starting from the given settings.
9108 *
9109 * @param data snapshot settings.
9110 * @param aCurSnapshotId Current snapshot ID from the settings file.
9111 */
9112HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9113 const Guid &aCurSnapshotId)
9114{
9115 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9116 AssertReturn(!i_isSessionMachine(), E_FAIL);
9117
9118 HRESULT hrc = S_OK;
9119
9120 std::list<const settings::Snapshot *> llSettingsTodo;
9121 llSettingsTodo.push_back(&data);
9122 std::list<Snapshot *> llParentsTodo;
9123 llParentsTodo.push_back(NULL);
9124
9125 while (llSettingsTodo.size() > 0)
9126 {
9127 const settings::Snapshot *current = llSettingsTodo.front();
9128 llSettingsTodo.pop_front();
9129 Snapshot *pParent = llParentsTodo.front();
9130 llParentsTodo.pop_front();
9131
9132 Utf8Str strStateFile;
9133 if (!current->strStateFile.isEmpty())
9134 {
9135 /* optional */
9136 strStateFile = current->strStateFile;
9137 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9138 if (RT_FAILURE(vrc))
9139 {
9140 setErrorBoth(E_FAIL, vrc,
9141 tr("Invalid saved state file path '%s' (%Rrc)"),
9142 strStateFile.c_str(), vrc);
9143 }
9144 }
9145
9146 /* create a snapshot machine object */
9147 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9148 pSnapshotMachine.createObject();
9149 hrc = pSnapshotMachine->initFromSettings(this,
9150 current->hardware,
9151 &current->debugging,
9152 &current->autostart,
9153 current->recordingSettings,
9154 current->uuid.ref(),
9155 strStateFile);
9156 if (FAILED(hrc)) break;
9157
9158 /* create a snapshot object */
9159 ComObjPtr<Snapshot> pSnapshot;
9160 pSnapshot.createObject();
9161 /* initialize the snapshot */
9162 hrc = pSnapshot->init(mParent, // VirtualBox object
9163 current->uuid,
9164 current->strName,
9165 current->strDescription,
9166 current->timestamp,
9167 pSnapshotMachine,
9168 pParent);
9169 if (FAILED(hrc)) break;
9170
9171 /* memorize the first snapshot if necessary */
9172 if (!mData->mFirstSnapshot)
9173 {
9174 Assert(pParent == NULL);
9175 mData->mFirstSnapshot = pSnapshot;
9176 }
9177
9178 /* memorize the current snapshot when appropriate */
9179 if ( !mData->mCurrentSnapshot
9180 && pSnapshot->i_getId() == aCurSnapshotId
9181 )
9182 mData->mCurrentSnapshot = pSnapshot;
9183
9184 /* create all children */
9185 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9186 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9187 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9188 {
9189 llSettingsTodo.push_back(&*it);
9190 llParentsTodo.push_back(pSnapshot);
9191 }
9192 }
9193
9194 return hrc;
9195}
9196
9197/**
9198 * Loads settings into mHWData.
9199 *
9200 * @param puuidRegistry Registry ID.
9201 * @param puuidSnapshot Snapshot ID
9202 * @param data Reference to the hardware settings.
9203 * @param pDbg Pointer to the debugging settings.
9204 * @param pAutostart Pointer to the autostart settings
9205 * @param recording Reference to recording settings.
9206 */
9207HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9208 const Guid *puuidSnapshot,
9209 const settings::Hardware &data,
9210 const settings::Debugging *pDbg,
9211 const settings::Autostart *pAutostart,
9212 const settings::RecordingSettings &recording)
9213{
9214 AssertReturn(!i_isSessionMachine(), E_FAIL);
9215
9216 HRESULT hrc = S_OK;
9217
9218 try
9219 {
9220 ComObjPtr<GuestOSType> pGuestOSType;
9221 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9222
9223 /* The hardware version attribute (optional). */
9224 mHWData->mHWVersion = data.strVersion;
9225 mHWData->mHardwareUUID = data.uuid;
9226
9227 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9228 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9229 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9230 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9231 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9232 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9233 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9234 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9235 mHWData->mPAEEnabled = data.fPAE;
9236 mHWData->mLongMode = data.enmLongMode;
9237 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9238 mHWData->mAPIC = data.fAPIC;
9239 mHWData->mX2APIC = data.fX2APIC;
9240 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9241 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9242 mHWData->mSpecCtrl = data.fSpecCtrl;
9243 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9244 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9245 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9246 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9247 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9248 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9249 mHWData->mCPUCount = data.cCPUs;
9250 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9251 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9252 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9253 mHWData->mCpuProfile = data.strCpuProfile;
9254
9255 // cpu
9256 if (mHWData->mCPUHotPlugEnabled)
9257 {
9258 for (settings::CpuList::const_iterator
9259 it = data.llCpus.begin();
9260 it != data.llCpus.end();
9261 ++it)
9262 {
9263 const settings::Cpu &cpu = *it;
9264
9265 mHWData->mCPUAttached[cpu.ulId] = true;
9266 }
9267 }
9268
9269 // cpuid leafs
9270 for (settings::CpuIdLeafsList::const_iterator
9271 it = data.llCpuIdLeafs.begin();
9272 it != data.llCpuIdLeafs.end();
9273 ++it)
9274 {
9275 const settings::CpuIdLeaf &rLeaf= *it;
9276 if ( rLeaf.idx < UINT32_C(0x20)
9277 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9278 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9279 mHWData->mCpuIdLeafList.push_back(rLeaf);
9280 /* else: just ignore */
9281 }
9282
9283 mHWData->mMemorySize = data.ulMemorySizeMB;
9284 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9285
9286 // boot order
9287 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9288 {
9289 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9290 if (it == data.mapBootOrder.end())
9291 mHWData->mBootOrder[i] = DeviceType_Null;
9292 else
9293 mHWData->mBootOrder[i] = it->second;
9294 }
9295
9296 mHWData->mFirmwareType = data.firmwareType;
9297 mHWData->mPointingHIDType = data.pointingHIDType;
9298 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9299 mHWData->mChipsetType = data.chipsetType;
9300 mHWData->mIommuType = data.iommuType;
9301 mHWData->mParavirtProvider = data.paravirtProvider;
9302 mHWData->mParavirtDebug = data.strParavirtDebug;
9303 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9304 mHWData->mHPETEnabled = data.fHPETEnabled;
9305
9306 /* GraphicsAdapter */
9307 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9308 if (FAILED(hrc)) return hrc;
9309
9310 /* VRDEServer */
9311 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9312 if (FAILED(hrc)) return hrc;
9313
9314 /* BIOS */
9315 hrc = mBIOSSettings->i_loadSettings(data.biosSettings);
9316 if (FAILED(hrc)) return hrc;
9317
9318 /* Recording */
9319 hrc = mRecordingSettings->i_loadSettings(recording);
9320 if (FAILED(hrc)) return hrc;
9321
9322 /* Trusted Platform Module */
9323 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9324 if (FAILED(hrc)) return hrc;
9325
9326 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
9327 if (FAILED(hrc)) return hrc;
9328
9329 // Bandwidth control (must come before network adapters)
9330 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
9331 if (FAILED(hrc)) return hrc;
9332
9333 /* USB controllers */
9334 for (settings::USBControllerList::const_iterator
9335 it = data.usbSettings.llUSBControllers.begin();
9336 it != data.usbSettings.llUSBControllers.end();
9337 ++it)
9338 {
9339 const settings::USBController &settingsCtrl = *it;
9340 ComObjPtr<USBController> newCtrl;
9341
9342 newCtrl.createObject();
9343 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9344 mUSBControllers->push_back(newCtrl);
9345 }
9346
9347 /* USB device filters */
9348 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9349 if (FAILED(hrc)) return hrc;
9350
9351 // network adapters (establish array size first and apply defaults, to
9352 // ensure reading the same settings as we saved, since the list skips
9353 // adapters having defaults)
9354 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9355 size_t oldCount = mNetworkAdapters.size();
9356 if (newCount > oldCount)
9357 {
9358 mNetworkAdapters.resize(newCount);
9359 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9360 {
9361 unconst(mNetworkAdapters[slot]).createObject();
9362 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9363 }
9364 }
9365 else if (newCount < oldCount)
9366 mNetworkAdapters.resize(newCount);
9367 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9368 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9369 for (settings::NetworkAdaptersList::const_iterator
9370 it = data.llNetworkAdapters.begin();
9371 it != data.llNetworkAdapters.end();
9372 ++it)
9373 {
9374 const settings::NetworkAdapter &nic = *it;
9375
9376 /* slot uniqueness is guaranteed by XML Schema */
9377 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9378 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9379 if (FAILED(hrc)) return hrc;
9380 }
9381
9382 // serial ports (establish defaults first, to ensure reading the same
9383 // settings as we saved, since the list skips ports having defaults)
9384 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9385 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9386 for (settings::SerialPortsList::const_iterator
9387 it = data.llSerialPorts.begin();
9388 it != data.llSerialPorts.end();
9389 ++it)
9390 {
9391 const settings::SerialPort &s = *it;
9392
9393 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9394 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9395 if (FAILED(hrc)) return hrc;
9396 }
9397
9398 // parallel ports (establish defaults first, to ensure reading the same
9399 // settings as we saved, since the list skips ports having defaults)
9400 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9401 mParallelPorts[i]->i_applyDefaults();
9402 for (settings::ParallelPortsList::const_iterator
9403 it = data.llParallelPorts.begin();
9404 it != data.llParallelPorts.end();
9405 ++it)
9406 {
9407 const settings::ParallelPort &p = *it;
9408
9409 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9410 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9411 if (FAILED(hrc)) return hrc;
9412 }
9413
9414 /* Audio settings */
9415 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
9416 if (FAILED(hrc)) return hrc;
9417
9418 /* storage controllers */
9419 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
9420 if (FAILED(hrc)) return hrc;
9421
9422 /* Shared folders */
9423 for (settings::SharedFoldersList::const_iterator
9424 it = data.llSharedFolders.begin();
9425 it != data.llSharedFolders.end();
9426 ++it)
9427 {
9428 const settings::SharedFolder &sf = *it;
9429
9430 ComObjPtr<SharedFolder> sharedFolder;
9431 /* Check for double entries. Not allowed! */
9432 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9433 if (SUCCEEDED(hrc))
9434 return setError(VBOX_E_OBJECT_IN_USE,
9435 tr("Shared folder named '%s' already exists"),
9436 sf.strName.c_str());
9437
9438 /* Create the new shared folder. Don't break on error. This will be
9439 * reported when the machine starts. */
9440 sharedFolder.createObject();
9441 hrc = sharedFolder->init(i_getMachine(),
9442 sf.strName,
9443 sf.strHostPath,
9444 RT_BOOL(sf.fWritable),
9445 RT_BOOL(sf.fAutoMount),
9446 sf.strAutoMountPoint,
9447 false /* fFailOnError */);
9448 if (FAILED(hrc)) return hrc;
9449 mHWData->mSharedFolders.push_back(sharedFolder);
9450 }
9451
9452 // Clipboard
9453 mHWData->mClipboardMode = data.clipboardMode;
9454 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9455
9456 // drag'n'drop
9457 mHWData->mDnDMode = data.dndMode;
9458
9459 // guest settings
9460 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9461
9462 // IO settings
9463 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9464 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9465
9466 // Host PCI devices
9467 for (settings::HostPCIDeviceAttachmentList::const_iterator
9468 it = data.pciAttachments.begin();
9469 it != data.pciAttachments.end();
9470 ++it)
9471 {
9472 const settings::HostPCIDeviceAttachment &hpda = *it;
9473 ComObjPtr<PCIDeviceAttachment> pda;
9474
9475 pda.createObject();
9476 pda->i_loadSettings(this, hpda);
9477 mHWData->mPCIDeviceAssignments.push_back(pda);
9478 }
9479
9480 /*
9481 * (The following isn't really real hardware, but it lives in HWData
9482 * for reasons of convenience.)
9483 */
9484
9485#ifdef VBOX_WITH_GUEST_PROPS
9486 /* Guest properties (optional) */
9487
9488 /* Only load transient guest properties for configs which have saved
9489 * state, because there shouldn't be any for powered off VMs. The same
9490 * logic applies for snapshots, as offline snapshots shouldn't have
9491 * any such properties. They confuse the code in various places.
9492 * Note: can't rely on the machine state, as it isn't set yet. */
9493 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9494 /* apologies for the hacky unconst() usage, but this needs hacking
9495 * actually inconsistent settings into consistency, otherwise there
9496 * will be some corner cases where the inconsistency survives
9497 * surprisingly long without getting fixed, especially for snapshots
9498 * as there are no config changes. */
9499 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9500 for (settings::GuestPropertiesList::iterator
9501 it = llGuestProperties.begin();
9502 it != llGuestProperties.end();
9503 /*nothing*/)
9504 {
9505 const settings::GuestProperty &prop = *it;
9506 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9507 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9508 if ( fSkipTransientGuestProperties
9509 && ( fFlags & GUEST_PROP_F_TRANSIENT
9510 || fFlags & GUEST_PROP_F_TRANSRESET))
9511 {
9512 it = llGuestProperties.erase(it);
9513 continue;
9514 }
9515 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9516 mHWData->mGuestProperties[prop.strName] = property;
9517 ++it;
9518 }
9519#endif /* VBOX_WITH_GUEST_PROPS defined */
9520
9521 hrc = i_loadDebugging(pDbg);
9522 if (FAILED(hrc))
9523 return hrc;
9524
9525 mHWData->mAutostart = *pAutostart;
9526
9527 /* default frontend */
9528 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9529 }
9530 catch (std::bad_alloc &)
9531 {
9532 return E_OUTOFMEMORY;
9533 }
9534
9535 AssertComRC(hrc);
9536 return hrc;
9537}
9538
9539/**
9540 * Called from i_loadHardware() to load the debugging settings of the
9541 * machine.
9542 *
9543 * @param pDbg Pointer to the settings.
9544 */
9545HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9546{
9547 mHWData->mDebugging = *pDbg;
9548 /* no more processing currently required, this will probably change. */
9549
9550 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
9551 if (FAILED(hrc)) return hrc;
9552
9553 return S_OK;
9554}
9555
9556/**
9557 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9558 *
9559 * @param data storage settings.
9560 * @param puuidRegistry media registry ID to set media to or NULL;
9561 * see Machine::i_loadMachineDataFromSettings()
9562 * @param puuidSnapshot snapshot ID
9563 * @return
9564 */
9565HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9566 const Guid *puuidRegistry,
9567 const Guid *puuidSnapshot)
9568{
9569 AssertReturn(!i_isSessionMachine(), E_FAIL);
9570
9571 HRESULT hrc = S_OK;
9572
9573 for (settings::StorageControllersList::const_iterator
9574 it = data.llStorageControllers.begin();
9575 it != data.llStorageControllers.end();
9576 ++it)
9577 {
9578 const settings::StorageController &ctlData = *it;
9579
9580 ComObjPtr<StorageController> pCtl;
9581 /* Try to find one with the name first. */
9582 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9583 if (SUCCEEDED(hrc))
9584 return setError(VBOX_E_OBJECT_IN_USE,
9585 tr("Storage controller named '%s' already exists"),
9586 ctlData.strName.c_str());
9587
9588 pCtl.createObject();
9589 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
9590 if (FAILED(hrc)) return hrc;
9591
9592 mStorageControllers->push_back(pCtl);
9593
9594 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9595 if (FAILED(hrc)) return hrc;
9596
9597 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9598 if (FAILED(hrc)) return hrc;
9599
9600 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9601 if (FAILED(hrc)) return hrc;
9602
9603 /* Load the attached devices now. */
9604 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
9605 if (FAILED(hrc)) return hrc;
9606 }
9607
9608 return S_OK;
9609}
9610
9611/**
9612 * Called from i_loadStorageControllers for a controller's devices.
9613 *
9614 * @param aStorageController
9615 * @param data
9616 * @param puuidRegistry media registry ID to set media to or NULL; see
9617 * Machine::i_loadMachineDataFromSettings()
9618 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9619 * @return
9620 */
9621HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9622 const settings::StorageController &data,
9623 const Guid *puuidRegistry,
9624 const Guid *puuidSnapshot)
9625{
9626 HRESULT hrc = S_OK;
9627
9628 /* paranoia: detect duplicate attachments */
9629 for (settings::AttachedDevicesList::const_iterator
9630 it = data.llAttachedDevices.begin();
9631 it != data.llAttachedDevices.end();
9632 ++it)
9633 {
9634 const settings::AttachedDevice &ad = *it;
9635
9636 for (settings::AttachedDevicesList::const_iterator it2 = it;
9637 it2 != data.llAttachedDevices.end();
9638 ++it2)
9639 {
9640 if (it == it2)
9641 continue;
9642
9643 const settings::AttachedDevice &ad2 = *it2;
9644
9645 if ( ad.lPort == ad2.lPort
9646 && ad.lDevice == ad2.lDevice)
9647 {
9648 return setError(E_FAIL,
9649 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9650 aStorageController->i_getName().c_str(),
9651 ad.lPort,
9652 ad.lDevice,
9653 mUserData->s.strName.c_str());
9654 }
9655 }
9656 }
9657
9658 for (settings::AttachedDevicesList::const_iterator
9659 it = data.llAttachedDevices.begin();
9660 it != data.llAttachedDevices.end();
9661 ++it)
9662 {
9663 const settings::AttachedDevice &dev = *it;
9664 ComObjPtr<Medium> medium;
9665
9666 switch (dev.deviceType)
9667 {
9668 case DeviceType_Floppy:
9669 case DeviceType_DVD:
9670 if (dev.strHostDriveSrc.isNotEmpty())
9671 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9672 false /* fRefresh */, medium);
9673 else
9674 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9675 dev.uuid,
9676 false /* fRefresh */,
9677 false /* aSetError */,
9678 medium);
9679 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9680 // This is not an error. The host drive or UUID might have vanished, so just go
9681 // ahead without this removeable medium attachment
9682 hrc = S_OK;
9683 break;
9684
9685 case DeviceType_HardDisk:
9686 {
9687 /* find a hard disk by UUID */
9688 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9689 if (FAILED(hrc))
9690 {
9691 if (i_isSnapshotMachine())
9692 {
9693 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9694 // so the user knows that the bad disk is in a snapshot somewhere
9695 com::ErrorInfo info;
9696 return setError(E_FAIL,
9697 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9698 puuidSnapshot->raw(),
9699 info.getText().raw());
9700 }
9701 return hrc;
9702 }
9703
9704 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9705
9706 if (medium->i_getType() == MediumType_Immutable)
9707 {
9708 if (i_isSnapshotMachine())
9709 return setError(E_FAIL,
9710 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9711 "of the virtual machine '%s' ('%s')"),
9712 medium->i_getLocationFull().c_str(),
9713 dev.uuid.raw(),
9714 puuidSnapshot->raw(),
9715 mUserData->s.strName.c_str(),
9716 mData->m_strConfigFileFull.c_str());
9717
9718 return setError(E_FAIL,
9719 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9720 medium->i_getLocationFull().c_str(),
9721 dev.uuid.raw(),
9722 mUserData->s.strName.c_str(),
9723 mData->m_strConfigFileFull.c_str());
9724 }
9725
9726 if (medium->i_getType() == MediumType_MultiAttach)
9727 {
9728 if (i_isSnapshotMachine())
9729 return setError(E_FAIL,
9730 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9731 "of the virtual machine '%s' ('%s')"),
9732 medium->i_getLocationFull().c_str(),
9733 dev.uuid.raw(),
9734 puuidSnapshot->raw(),
9735 mUserData->s.strName.c_str(),
9736 mData->m_strConfigFileFull.c_str());
9737
9738 return setError(E_FAIL,
9739 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9740 medium->i_getLocationFull().c_str(),
9741 dev.uuid.raw(),
9742 mUserData->s.strName.c_str(),
9743 mData->m_strConfigFileFull.c_str());
9744 }
9745
9746 if ( !i_isSnapshotMachine()
9747 && medium->i_getChildren().size() != 0
9748 )
9749 return setError(E_FAIL,
9750 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9751 "because it has %d differencing child hard disks"),
9752 medium->i_getLocationFull().c_str(),
9753 dev.uuid.raw(),
9754 mUserData->s.strName.c_str(),
9755 mData->m_strConfigFileFull.c_str(),
9756 medium->i_getChildren().size());
9757
9758 if (i_findAttachment(*mMediumAttachments.data(),
9759 medium))
9760 return setError(E_FAIL,
9761 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9762 medium->i_getLocationFull().c_str(),
9763 dev.uuid.raw(),
9764 mUserData->s.strName.c_str(),
9765 mData->m_strConfigFileFull.c_str());
9766
9767 break;
9768 }
9769
9770 default:
9771 return setError(E_FAIL,
9772 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9773 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9774 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9775 }
9776
9777 if (FAILED(hrc))
9778 break;
9779
9780 /* Bandwidth groups are loaded at this point. */
9781 ComObjPtr<BandwidthGroup> pBwGroup;
9782
9783 if (!dev.strBwGroup.isEmpty())
9784 {
9785 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9786 if (FAILED(hrc))
9787 return setError(E_FAIL,
9788 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9789 medium->i_getLocationFull().c_str(),
9790 dev.strBwGroup.c_str(),
9791 mUserData->s.strName.c_str(),
9792 mData->m_strConfigFileFull.c_str());
9793 pBwGroup->i_reference();
9794 }
9795
9796 const Utf8Str controllerName = aStorageController->i_getName();
9797 ComObjPtr<MediumAttachment> pAttachment;
9798 pAttachment.createObject();
9799 hrc = pAttachment->init(this,
9800 medium,
9801 controllerName,
9802 dev.lPort,
9803 dev.lDevice,
9804 dev.deviceType,
9805 false,
9806 dev.fPassThrough,
9807 dev.fTempEject,
9808 dev.fNonRotational,
9809 dev.fDiscard,
9810 dev.fHotPluggable,
9811 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9812 if (FAILED(hrc)) break;
9813
9814 /* associate the medium with this machine and snapshot */
9815 if (!medium.isNull())
9816 {
9817 AutoCaller medCaller(medium);
9818 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9819 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9820
9821 if (i_isSnapshotMachine())
9822 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9823 else
9824 hrc = medium->i_addBackReference(mData->mUuid);
9825 /* If the medium->addBackReference fails it sets an appropriate
9826 * error message, so no need to do any guesswork here. */
9827
9828 if (puuidRegistry)
9829 // caller wants registry ID to be set on all attached media (OVF import case)
9830 medium->i_addRegistry(*puuidRegistry);
9831 }
9832
9833 if (FAILED(hrc))
9834 break;
9835
9836 /* back up mMediumAttachments to let registeredInit() properly rollback
9837 * on failure (= limited accessibility) */
9838 i_setModified(IsModified_Storage);
9839 mMediumAttachments.backup();
9840 mMediumAttachments->push_back(pAttachment);
9841 }
9842
9843 return hrc;
9844}
9845
9846/**
9847 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9848 *
9849 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9850 * @param aSnapshot where to return the found snapshot
9851 * @param aSetError true to set extended error info on failure
9852 */
9853HRESULT Machine::i_findSnapshotById(const Guid &aId,
9854 ComObjPtr<Snapshot> &aSnapshot,
9855 bool aSetError /* = false */)
9856{
9857 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9858
9859 if (!mData->mFirstSnapshot)
9860 {
9861 if (aSetError)
9862 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9863 return E_FAIL;
9864 }
9865
9866 if (aId.isZero())
9867 aSnapshot = mData->mFirstSnapshot;
9868 else
9869 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9870
9871 if (!aSnapshot)
9872 {
9873 if (aSetError)
9874 return setError(E_FAIL,
9875 tr("Could not find a snapshot with UUID {%s}"),
9876 aId.toString().c_str());
9877 return E_FAIL;
9878 }
9879
9880 return S_OK;
9881}
9882
9883/**
9884 * Returns the snapshot with the given name or fails of no such snapshot.
9885 *
9886 * @param strName snapshot name to find
9887 * @param aSnapshot where to return the found snapshot
9888 * @param aSetError true to set extended error info on failure
9889 */
9890HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9891 ComObjPtr<Snapshot> &aSnapshot,
9892 bool aSetError /* = false */)
9893{
9894 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9895
9896 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9897
9898 if (!mData->mFirstSnapshot)
9899 {
9900 if (aSetError)
9901 return setError(VBOX_E_OBJECT_NOT_FOUND,
9902 tr("This machine does not have any snapshots"));
9903 return VBOX_E_OBJECT_NOT_FOUND;
9904 }
9905
9906 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9907
9908 if (!aSnapshot)
9909 {
9910 if (aSetError)
9911 return setError(VBOX_E_OBJECT_NOT_FOUND,
9912 tr("Could not find a snapshot named '%s'"), strName.c_str());
9913 return VBOX_E_OBJECT_NOT_FOUND;
9914 }
9915
9916 return S_OK;
9917}
9918
9919/**
9920 * Returns a storage controller object with the given name.
9921 *
9922 * @param aName storage controller name to find
9923 * @param aStorageController where to return the found storage controller
9924 * @param aSetError true to set extended error info on failure
9925 */
9926HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9927 ComObjPtr<StorageController> &aStorageController,
9928 bool aSetError /* = false */)
9929{
9930 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9931
9932 for (StorageControllerList::const_iterator
9933 it = mStorageControllers->begin();
9934 it != mStorageControllers->end();
9935 ++it)
9936 {
9937 if ((*it)->i_getName() == aName)
9938 {
9939 aStorageController = (*it);
9940 return S_OK;
9941 }
9942 }
9943
9944 if (aSetError)
9945 return setError(VBOX_E_OBJECT_NOT_FOUND,
9946 tr("Could not find a storage controller named '%s'"),
9947 aName.c_str());
9948 return VBOX_E_OBJECT_NOT_FOUND;
9949}
9950
9951/**
9952 * Returns a USB controller object with the given name.
9953 *
9954 * @param aName USB controller name to find
9955 * @param aUSBController where to return the found USB controller
9956 * @param aSetError true to set extended error info on failure
9957 */
9958HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9959 ComObjPtr<USBController> &aUSBController,
9960 bool aSetError /* = false */)
9961{
9962 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9963
9964 for (USBControllerList::const_iterator
9965 it = mUSBControllers->begin();
9966 it != mUSBControllers->end();
9967 ++it)
9968 {
9969 if ((*it)->i_getName() == aName)
9970 {
9971 aUSBController = (*it);
9972 return S_OK;
9973 }
9974 }
9975
9976 if (aSetError)
9977 return setError(VBOX_E_OBJECT_NOT_FOUND,
9978 tr("Could not find a storage controller named '%s'"),
9979 aName.c_str());
9980 return VBOX_E_OBJECT_NOT_FOUND;
9981}
9982
9983/**
9984 * Returns the number of USB controller instance of the given type.
9985 *
9986 * @param enmType USB controller type.
9987 */
9988ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9989{
9990 ULONG cCtrls = 0;
9991
9992 for (USBControllerList::const_iterator
9993 it = mUSBControllers->begin();
9994 it != mUSBControllers->end();
9995 ++it)
9996 {
9997 if ((*it)->i_getControllerType() == enmType)
9998 cCtrls++;
9999 }
10000
10001 return cCtrls;
10002}
10003
10004HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10005 MediumAttachmentList &atts)
10006{
10007 AutoCaller autoCaller(this);
10008 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
10009
10010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10011
10012 for (MediumAttachmentList::const_iterator
10013 it = mMediumAttachments->begin();
10014 it != mMediumAttachments->end();
10015 ++it)
10016 {
10017 const ComObjPtr<MediumAttachment> &pAtt = *it;
10018 // should never happen, but deal with NULL pointers in the list.
10019 AssertContinue(!pAtt.isNull());
10020
10021 // getControllerName() needs caller+read lock
10022 AutoCaller autoAttCaller(pAtt);
10023 if (FAILED(autoAttCaller.hrc()))
10024 {
10025 atts.clear();
10026 return autoAttCaller.hrc();
10027 }
10028 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10029
10030 if (pAtt->i_getControllerName() == aName)
10031 atts.push_back(pAtt);
10032 }
10033
10034 return S_OK;
10035}
10036
10037
10038/**
10039 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10040 * file if the machine name was changed and about creating a new settings file
10041 * if this is a new machine.
10042 *
10043 * @note Must be never called directly but only from #saveSettings().
10044 */
10045HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10046 bool *pfSettingsFileIsNew)
10047{
10048 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10049
10050 HRESULT hrc = S_OK;
10051
10052 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10053 /// @todo need to handle primary group change, too
10054
10055 /* attempt to rename the settings file if machine name is changed */
10056 if ( mUserData->s.fNameSync
10057 && mUserData.isBackedUp()
10058 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10059 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10060 )
10061 {
10062 bool dirRenamed = false;
10063 bool fileRenamed = false;
10064
10065 Utf8Str configFile, newConfigFile;
10066 Utf8Str configFilePrev, newConfigFilePrev;
10067 Utf8Str NVRAMFile, newNVRAMFile;
10068 Utf8Str configDir, newConfigDir;
10069
10070 do
10071 {
10072 int vrc = VINF_SUCCESS;
10073
10074 Utf8Str name = mUserData.backedUpData()->s.strName;
10075 Utf8Str newName = mUserData->s.strName;
10076 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10077 if (group == "/")
10078 group.setNull();
10079 Utf8Str newGroup = mUserData->s.llGroups.front();
10080 if (newGroup == "/")
10081 newGroup.setNull();
10082
10083 configFile = mData->m_strConfigFileFull;
10084
10085 /* first, rename the directory if it matches the group and machine name */
10086 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10087 /** @todo hack, make somehow use of ComposeMachineFilename */
10088 if (mUserData->s.fDirectoryIncludesUUID)
10089 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10090 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10091 /** @todo hack, make somehow use of ComposeMachineFilename */
10092 if (mUserData->s.fDirectoryIncludesUUID)
10093 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10094 configDir = configFile;
10095 configDir.stripFilename();
10096 newConfigDir = configDir;
10097 if ( configDir.length() >= groupPlusName.length()
10098 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10099 groupPlusName.c_str()))
10100 {
10101 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10102 Utf8Str newConfigBaseDir(newConfigDir);
10103 newConfigDir.append(newGroupPlusName);
10104 /* consistency: use \ if appropriate on the platform */
10105 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10106 /* new dir and old dir cannot be equal here because of 'if'
10107 * above and because name != newName */
10108 Assert(configDir != newConfigDir);
10109 if (!fSettingsFileIsNew)
10110 {
10111 /* perform real rename only if the machine is not new */
10112 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10113 if ( vrc == VERR_FILE_NOT_FOUND
10114 || vrc == VERR_PATH_NOT_FOUND)
10115 {
10116 /* create the parent directory, then retry renaming */
10117 Utf8Str parent(newConfigDir);
10118 parent.stripFilename();
10119 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10120 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10121 }
10122 if (RT_FAILURE(vrc))
10123 {
10124 hrc = setErrorBoth(E_FAIL, vrc,
10125 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10126 configDir.c_str(),
10127 newConfigDir.c_str(),
10128 vrc);
10129 break;
10130 }
10131 /* delete subdirectories which are no longer needed */
10132 Utf8Str dir(configDir);
10133 dir.stripFilename();
10134 while (dir != newConfigBaseDir && dir != ".")
10135 {
10136 vrc = RTDirRemove(dir.c_str());
10137 if (RT_FAILURE(vrc))
10138 break;
10139 dir.stripFilename();
10140 }
10141 dirRenamed = true;
10142 }
10143 }
10144
10145 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10146
10147 /* then try to rename the settings file itself */
10148 if (newConfigFile != configFile)
10149 {
10150 /* get the path to old settings file in renamed directory */
10151 Assert(mData->m_strConfigFileFull == configFile);
10152 configFile.printf("%s%c%s",
10153 newConfigDir.c_str(),
10154 RTPATH_DELIMITER,
10155 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10156 if (!fSettingsFileIsNew)
10157 {
10158 /* perform real rename only if the machine is not new */
10159 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10160 if (RT_FAILURE(vrc))
10161 {
10162 hrc = setErrorBoth(E_FAIL, vrc,
10163 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10164 configFile.c_str(),
10165 newConfigFile.c_str(),
10166 vrc);
10167 break;
10168 }
10169 fileRenamed = true;
10170 configFilePrev = configFile;
10171 configFilePrev += "-prev";
10172 newConfigFilePrev = newConfigFile;
10173 newConfigFilePrev += "-prev";
10174 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10175 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10176 if (NVRAMFile.isNotEmpty())
10177 {
10178 // in the NVRAM file path, replace the old directory with the new directory
10179 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10180 {
10181 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10182 NVRAMFile = newConfigDir + strNVRAMFile;
10183 }
10184 newNVRAMFile = newConfigFile;
10185 newNVRAMFile.stripSuffix();
10186 newNVRAMFile += ".nvram";
10187 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10188 }
10189 }
10190 }
10191
10192 // update m_strConfigFileFull amd mConfigFile
10193 mData->m_strConfigFileFull = newConfigFile;
10194 // compute the relative path too
10195 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10196
10197 // store the old and new so that VirtualBox::i_saveSettings() can update
10198 // the media registry
10199 if ( mData->mRegistered
10200 && (configDir != newConfigDir || configFile != newConfigFile))
10201 {
10202 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10203
10204 if (pfNeedsGlobalSaveSettings)
10205 *pfNeedsGlobalSaveSettings = true;
10206 }
10207
10208 // in the saved state file path, replace the old directory with the new directory
10209 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10210 {
10211 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10212 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10213 }
10214 if (newNVRAMFile.isNotEmpty())
10215 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10216
10217 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10218 if (mData->mFirstSnapshot)
10219 {
10220 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10221 newConfigDir.c_str());
10222 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10223 newConfigDir.c_str());
10224 }
10225 }
10226 while (0);
10227
10228 if (FAILED(hrc))
10229 {
10230 /* silently try to rename everything back */
10231 if (fileRenamed)
10232 {
10233 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10234 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10235 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10236 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10237 }
10238 if (dirRenamed)
10239 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10240 }
10241
10242 if (FAILED(hrc)) return hrc;
10243 }
10244
10245 if (fSettingsFileIsNew)
10246 {
10247 /* create a virgin config file */
10248 int vrc = VINF_SUCCESS;
10249
10250 /* ensure the settings directory exists */
10251 Utf8Str path(mData->m_strConfigFileFull);
10252 path.stripFilename();
10253 if (!RTDirExists(path.c_str()))
10254 {
10255 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10256 if (RT_FAILURE(vrc))
10257 {
10258 return setErrorBoth(E_FAIL, vrc,
10259 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10260 path.c_str(),
10261 vrc);
10262 }
10263 }
10264
10265 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10266 path = mData->m_strConfigFileFull;
10267 RTFILE f = NIL_RTFILE;
10268 vrc = RTFileOpen(&f, path.c_str(),
10269 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10270 if (RT_FAILURE(vrc))
10271 return setErrorBoth(E_FAIL, vrc,
10272 tr("Could not create the settings file '%s' (%Rrc)"),
10273 path.c_str(),
10274 vrc);
10275 RTFileClose(f);
10276 }
10277 if (pfSettingsFileIsNew)
10278 *pfSettingsFileIsNew = fSettingsFileIsNew;
10279
10280 return hrc;
10281}
10282
10283/**
10284 * Saves and commits machine data, user data and hardware data.
10285 *
10286 * Note that on failure, the data remains uncommitted.
10287 *
10288 * @a aFlags may combine the following flags:
10289 *
10290 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10291 * Used when saving settings after an operation that makes them 100%
10292 * correspond to the settings from the current snapshot.
10293 * - SaveS_Force: settings will be saved without doing a deep compare of the
10294 * settings structures. This is used when this is called because snapshots
10295 * have changed to avoid the overhead of the deep compare.
10296 *
10297 * @note Must be called from under this object's write lock. Locks children for
10298 * writing.
10299 *
10300 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10301 * initialized to false and that will be set to true by this function if
10302 * the caller must invoke VirtualBox::i_saveSettings() because the global
10303 * settings have changed. This will happen if a machine rename has been
10304 * saved and the global machine and media registries will therefore need
10305 * updating.
10306 * @param alock Reference to the lock for this machine object.
10307 * @param aFlags Flags.
10308 */
10309HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10310 AutoWriteLock &alock,
10311 int aFlags /*= 0*/)
10312{
10313 LogFlowThisFuncEnter();
10314
10315 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10316
10317 /* make sure child objects are unable to modify the settings while we are
10318 * saving them */
10319 i_ensureNoStateDependencies(alock);
10320
10321 AssertReturn(!i_isSnapshotMachine(),
10322 E_FAIL);
10323
10324 if (!mData->mAccessible)
10325 return setError(VBOX_E_INVALID_VM_STATE,
10326 tr("The machine is not accessible, so cannot save settings"));
10327
10328 HRESULT hrc = S_OK;
10329 PCVBOXCRYPTOIF pCryptoIf = NULL;
10330 const char *pszPassword = NULL;
10331 SecretKey *pKey = NULL;
10332
10333#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10334 if (mData->mstrKeyId.isNotEmpty())
10335 {
10336 /* VM is going to be encrypted. */
10337 alock.release(); /** @todo Revise the locking. */
10338 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
10339 alock.acquire();
10340 if (FAILED(hrc)) return hrc; /* Error is set. */
10341
10342 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10343 if (RT_SUCCESS(vrc))
10344 pszPassword = (const char *)pKey->getKeyBuffer();
10345 else
10346 {
10347 mParent->i_releaseCryptoIf(pCryptoIf);
10348 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10349 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10350 mData->mstrKeyId.c_str(), vrc);
10351 }
10352 }
10353#else
10354 RT_NOREF(pKey);
10355#endif
10356
10357 bool fNeedsWrite = false;
10358 bool fSettingsFileIsNew = false;
10359
10360 /* First, prepare to save settings. It will care about renaming the
10361 * settings directory and file if the machine name was changed and about
10362 * creating a new settings file if this is a new machine. */
10363 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
10364 if (FAILED(hrc))
10365 {
10366#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10367 if (pCryptoIf)
10368 {
10369 alock.release(); /** @todo Revise the locking. */
10370 mParent->i_releaseCryptoIf(pCryptoIf);
10371 alock.acquire();
10372 }
10373 if (pKey)
10374 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10375#endif
10376 return hrc;
10377 }
10378
10379 // keep a pointer to the current settings structures
10380 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10381 settings::MachineConfigFile *pNewConfig = NULL;
10382
10383 try
10384 {
10385 // make a fresh one to have everyone write stuff into
10386 pNewConfig = new settings::MachineConfigFile(NULL);
10387 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10388#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10389 pNewConfig->strKeyId = mData->mstrKeyId;
10390 pNewConfig->strKeyStore = mData->mstrKeyStore;
10391#endif
10392
10393 // now go and copy all the settings data from COM to the settings structures
10394 // (this calls i_saveSettings() on all the COM objects in the machine)
10395 i_copyMachineDataToSettings(*pNewConfig);
10396
10397 if (aFlags & SaveS_ResetCurStateModified)
10398 {
10399 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10400 mData->mCurrentStateModified = FALSE;
10401 fNeedsWrite = true; // always, no need to compare
10402 }
10403 else if (aFlags & SaveS_Force)
10404 {
10405 fNeedsWrite = true; // always, no need to compare
10406 }
10407 else
10408 {
10409 if (!mData->mCurrentStateModified)
10410 {
10411 // do a deep compare of the settings that we just saved with the settings
10412 // previously stored in the config file; this invokes MachineConfigFile::operator==
10413 // which does a deep compare of all the settings, which is expensive but less expensive
10414 // than writing out XML in vain
10415 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10416
10417 // could still be modified if any settings changed
10418 mData->mCurrentStateModified = fAnySettingsChanged;
10419
10420 fNeedsWrite = fAnySettingsChanged;
10421 }
10422 else
10423 fNeedsWrite = true;
10424 }
10425
10426 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10427
10428 if (fNeedsWrite)
10429 {
10430 // now spit it all out!
10431 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10432 if (aFlags & SaveS_RemoveBackup)
10433 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10434 }
10435
10436 mData->pMachineConfigFile = pNewConfig;
10437 delete pOldConfig;
10438 i_commit();
10439
10440 // after saving settings, we are no longer different from the XML on disk
10441 mData->flModifications = 0;
10442 }
10443 catch (HRESULT err)
10444 {
10445 // we assume that error info is set by the thrower
10446 hrc = err;
10447
10448 // delete any newly created settings file
10449 if (fSettingsFileIsNew)
10450 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10451
10452 // restore old config
10453 delete pNewConfig;
10454 mData->pMachineConfigFile = pOldConfig;
10455 }
10456 catch (...)
10457 {
10458 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10459 }
10460
10461#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10462 if (pCryptoIf)
10463 {
10464 alock.release(); /** @todo Revise the locking. */
10465 mParent->i_releaseCryptoIf(pCryptoIf);
10466 alock.acquire();
10467 }
10468 if (pKey)
10469 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10470#endif
10471
10472 if (fNeedsWrite)
10473 {
10474 /* Fire the data change event, even on failure (since we've already
10475 * committed all data). This is done only for SessionMachines because
10476 * mutable Machine instances are always not registered (i.e. private
10477 * to the client process that creates them) and thus don't need to
10478 * inform callbacks. */
10479 if (i_isSessionMachine())
10480 mParent->i_onMachineDataChanged(mData->mUuid);
10481 }
10482
10483 LogFlowThisFunc(("hrc=%08X\n", hrc));
10484 LogFlowThisFuncLeave();
10485 return hrc;
10486}
10487
10488/**
10489 * Implementation for saving the machine settings into the given
10490 * settings::MachineConfigFile instance. This copies machine extradata
10491 * from the previous machine config file in the instance data, if any.
10492 *
10493 * This gets called from two locations:
10494 *
10495 * -- Machine::i_saveSettings(), during the regular XML writing;
10496 *
10497 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10498 * exported to OVF and we write the VirtualBox proprietary XML
10499 * into a <vbox:Machine> tag.
10500 *
10501 * This routine fills all the fields in there, including snapshots, *except*
10502 * for the following:
10503 *
10504 * -- fCurrentStateModified. There is some special logic associated with that.
10505 *
10506 * The caller can then call MachineConfigFile::write() or do something else
10507 * with it.
10508 *
10509 * Caller must hold the machine lock!
10510 *
10511 * This throws XML errors and HRESULT, so the caller must have a catch block!
10512 */
10513void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10514{
10515 // deep copy extradata, being extra careful with self assignment (the STL
10516 // map assignment on Mac OS X clang based Xcode isn't checking)
10517 if (&config != mData->pMachineConfigFile)
10518 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10519
10520 config.uuid = mData->mUuid;
10521
10522 // copy name, description, OS type, teleport, UTC etc.
10523 config.machineUserData = mUserData->s;
10524
10525#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10526 config.strStateKeyId = mSSData->strStateKeyId;
10527 config.strStateKeyStore = mSSData->strStateKeyStore;
10528 config.strLogKeyId = mData->mstrLogKeyId;
10529 config.strLogKeyStore = mData->mstrLogKeyStore;
10530#endif
10531
10532 if ( mData->mMachineState == MachineState_Saved
10533 || mData->mMachineState == MachineState_AbortedSaved
10534 || mData->mMachineState == MachineState_Restoring
10535 // when doing certain snapshot operations we may or may not have
10536 // a saved state in the current state, so keep everything as is
10537 || ( ( mData->mMachineState == MachineState_Snapshotting
10538 || mData->mMachineState == MachineState_DeletingSnapshot
10539 || mData->mMachineState == MachineState_RestoringSnapshot)
10540 && (!mSSData->strStateFilePath.isEmpty())
10541 )
10542 )
10543 {
10544 Assert(!mSSData->strStateFilePath.isEmpty());
10545 /* try to make the file name relative to the settings file dir */
10546 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10547 }
10548 else
10549 {
10550 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10551 config.strStateFile.setNull();
10552 }
10553
10554 if (mData->mCurrentSnapshot)
10555 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10556 else
10557 config.uuidCurrentSnapshot.clear();
10558
10559 config.timeLastStateChange = mData->mLastStateChange;
10560 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10561 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10562
10563 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10564 if (FAILED(hrc)) throw hrc;
10565
10566 // save machine's media registry if this is VirtualBox 4.0 or later
10567 if (config.canHaveOwnMediaRegistry())
10568 {
10569 // determine machine folder
10570 Utf8Str strMachineFolder = i_getSettingsFileFull();
10571 strMachineFolder.stripFilename();
10572 mParent->i_saveMediaRegistry(config.mediaRegistry,
10573 i_getId(), // only media with registry ID == machine UUID
10574 strMachineFolder);
10575 // this throws HRESULT
10576 }
10577
10578 // save snapshots
10579 hrc = i_saveAllSnapshots(config);
10580 if (FAILED(hrc)) throw hrc;
10581}
10582
10583/**
10584 * Saves all snapshots of the machine into the given machine config file. Called
10585 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10586 * @param config
10587 * @return
10588 */
10589HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10590{
10591 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10592
10593 HRESULT hrc = S_OK;
10594
10595 try
10596 {
10597 config.llFirstSnapshot.clear();
10598
10599 if (mData->mFirstSnapshot)
10600 {
10601 // the settings use a list for "the first snapshot"
10602 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10603
10604 // get reference to the snapshot on the list and work on that
10605 // element straight in the list to avoid excessive copying later
10606 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10607 if (FAILED(hrc)) throw hrc;
10608 }
10609
10610// if (mType == IsSessionMachine)
10611// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10612
10613 }
10614 catch (HRESULT err)
10615 {
10616 /* we assume that error info is set by the thrower */
10617 hrc = err;
10618 }
10619 catch (...)
10620 {
10621 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10622 }
10623
10624 return hrc;
10625}
10626
10627/**
10628 * Saves the VM hardware configuration. It is assumed that the
10629 * given node is empty.
10630 *
10631 * @param data Reference to the settings object for the hardware config.
10632 * @param pDbg Pointer to the settings object for the debugging config
10633 * which happens to live in mHWData.
10634 * @param pAutostart Pointer to the settings object for the autostart config
10635 * which happens to live in mHWData.
10636 * @param recording Reference to reecording settings.
10637 */
10638HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10639 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10640{
10641 HRESULT hrc = S_OK;
10642
10643 try
10644 {
10645 /* The hardware version attribute (optional).
10646 Automatically upgrade from 1 to current default hardware version
10647 when there is no saved state. (ugly!) */
10648 if ( mHWData->mHWVersion == "1"
10649 && mSSData->strStateFilePath.isEmpty()
10650 )
10651 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10652
10653 data.strVersion = mHWData->mHWVersion;
10654 data.uuid = mHWData->mHardwareUUID;
10655
10656 // CPU
10657 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10658 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10659 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10660 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10661 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10662 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10663 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10664 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10665 data.fPAE = !!mHWData->mPAEEnabled;
10666 data.enmLongMode = mHWData->mLongMode;
10667 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10668 data.fAPIC = !!mHWData->mAPIC;
10669 data.fX2APIC = !!mHWData->mX2APIC;
10670 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10671 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10672 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10673 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10674 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10675 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10676 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10677 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10678 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10679 data.cCPUs = mHWData->mCPUCount;
10680 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10681 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10682 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10683 data.strCpuProfile = mHWData->mCpuProfile;
10684
10685 data.llCpus.clear();
10686 if (data.fCpuHotPlug)
10687 {
10688 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10689 {
10690 if (mHWData->mCPUAttached[idx])
10691 {
10692 settings::Cpu cpu;
10693 cpu.ulId = idx;
10694 data.llCpus.push_back(cpu);
10695 }
10696 }
10697 }
10698
10699 /* Standard and Extended CPUID leafs. */
10700 data.llCpuIdLeafs.clear();
10701 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10702
10703 // memory
10704 data.ulMemorySizeMB = mHWData->mMemorySize;
10705 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10706
10707 // firmware
10708 data.firmwareType = mHWData->mFirmwareType;
10709
10710 // HID
10711 data.pointingHIDType = mHWData->mPointingHIDType;
10712 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10713
10714 // chipset
10715 data.chipsetType = mHWData->mChipsetType;
10716
10717 // iommu
10718 data.iommuType = mHWData->mIommuType;
10719
10720 // paravirt
10721 data.paravirtProvider = mHWData->mParavirtProvider;
10722 data.strParavirtDebug = mHWData->mParavirtDebug;
10723
10724 // emulated USB card reader
10725 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10726
10727 // HPET
10728 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10729
10730 // boot order
10731 data.mapBootOrder.clear();
10732 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10733 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10734
10735 /* VRDEServer settings (optional) */
10736 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10737 if (FAILED(hrc)) throw hrc;
10738
10739 /* BIOS settings (required) */
10740 hrc = mBIOSSettings->i_saveSettings(data.biosSettings);
10741 if (FAILED(hrc)) throw hrc;
10742
10743 /* Recording settings. */
10744 hrc = mRecordingSettings->i_saveSettings(recording);
10745 if (FAILED(hrc)) throw hrc;
10746
10747 /* Trusted Platform Module settings (required) */
10748 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10749 if (FAILED(hrc)) throw hrc;
10750
10751 /* NVRAM settings (required) */
10752 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10753 if (FAILED(hrc)) throw hrc;
10754
10755 /* GraphicsAdapter settings (required) */
10756 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10757 if (FAILED(hrc)) throw hrc;
10758
10759 /* USB Controller (required) */
10760 data.usbSettings.llUSBControllers.clear();
10761 for (USBControllerList::const_iterator
10762 it = mUSBControllers->begin();
10763 it != mUSBControllers->end();
10764 ++it)
10765 {
10766 ComObjPtr<USBController> ctrl = *it;
10767 settings::USBController settingsCtrl;
10768
10769 settingsCtrl.strName = ctrl->i_getName();
10770 settingsCtrl.enmType = ctrl->i_getControllerType();
10771
10772 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10773 }
10774
10775 /* USB device filters (required) */
10776 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10777 if (FAILED(hrc)) throw hrc;
10778
10779 /* Network adapters (required) */
10780 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10781 data.llNetworkAdapters.clear();
10782 /* Write out only the nominal number of network adapters for this
10783 * chipset type. Since Machine::commit() hasn't been called there
10784 * may be extra NIC settings in the vector. */
10785 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10786 {
10787 settings::NetworkAdapter nic;
10788 nic.ulSlot = (uint32_t)slot;
10789 /* paranoia check... must not be NULL, but must not crash either. */
10790 if (mNetworkAdapters[slot])
10791 {
10792 if (mNetworkAdapters[slot]->i_hasDefaults())
10793 continue;
10794
10795 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10796 if (FAILED(hrc)) throw hrc;
10797
10798 data.llNetworkAdapters.push_back(nic);
10799 }
10800 }
10801
10802 /* Serial ports */
10803 data.llSerialPorts.clear();
10804 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10805 {
10806 if (mSerialPorts[slot]->i_hasDefaults())
10807 continue;
10808
10809 settings::SerialPort s;
10810 s.ulSlot = slot;
10811 hrc = mSerialPorts[slot]->i_saveSettings(s);
10812 if (FAILED(hrc)) return hrc;
10813
10814 data.llSerialPorts.push_back(s);
10815 }
10816
10817 /* Parallel ports */
10818 data.llParallelPorts.clear();
10819 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10820 {
10821 if (mParallelPorts[slot]->i_hasDefaults())
10822 continue;
10823
10824 settings::ParallelPort p;
10825 p.ulSlot = slot;
10826 hrc = mParallelPorts[slot]->i_saveSettings(p);
10827 if (FAILED(hrc)) return hrc;
10828
10829 data.llParallelPorts.push_back(p);
10830 }
10831
10832 /* Audio settings */
10833 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10834 if (FAILED(hrc)) return hrc;
10835
10836 hrc = i_saveStorageControllers(data.storage);
10837 if (FAILED(hrc)) return hrc;
10838
10839 /* Shared folders */
10840 data.llSharedFolders.clear();
10841 for (HWData::SharedFolderList::const_iterator
10842 it = mHWData->mSharedFolders.begin();
10843 it != mHWData->mSharedFolders.end();
10844 ++it)
10845 {
10846 SharedFolder *pSF = *it;
10847 AutoCaller sfCaller(pSF);
10848 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10849 settings::SharedFolder sf;
10850 sf.strName = pSF->i_getName();
10851 sf.strHostPath = pSF->i_getHostPath();
10852 sf.fWritable = !!pSF->i_isWritable();
10853 sf.fAutoMount = !!pSF->i_isAutoMounted();
10854 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10855
10856 data.llSharedFolders.push_back(sf);
10857 }
10858
10859 // clipboard
10860 data.clipboardMode = mHWData->mClipboardMode;
10861 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10862
10863 // drag'n'drop
10864 data.dndMode = mHWData->mDnDMode;
10865
10866 /* Guest */
10867 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10868
10869 // IO settings
10870 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10871 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10872
10873 /* BandwidthControl (required) */
10874 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10875 if (FAILED(hrc)) throw hrc;
10876
10877 /* Host PCI devices */
10878 data.pciAttachments.clear();
10879 for (HWData::PCIDeviceAssignmentList::const_iterator
10880 it = mHWData->mPCIDeviceAssignments.begin();
10881 it != mHWData->mPCIDeviceAssignments.end();
10882 ++it)
10883 {
10884 ComObjPtr<PCIDeviceAttachment> pda = *it;
10885 settings::HostPCIDeviceAttachment hpda;
10886
10887 hrc = pda->i_saveSettings(hpda);
10888 if (FAILED(hrc)) throw hrc;
10889
10890 data.pciAttachments.push_back(hpda);
10891 }
10892
10893 // guest properties
10894 data.llGuestProperties.clear();
10895#ifdef VBOX_WITH_GUEST_PROPS
10896 for (HWData::GuestPropertyMap::const_iterator
10897 it = mHWData->mGuestProperties.begin();
10898 it != mHWData->mGuestProperties.end();
10899 ++it)
10900 {
10901 HWData::GuestProperty property = it->second;
10902
10903 /* Remove transient guest properties at shutdown unless we
10904 * are saving state. Note that restoring snapshot intentionally
10905 * keeps them, they will be removed if appropriate once the final
10906 * machine state is set (as crashes etc. need to work). */
10907 if ( ( mData->mMachineState == MachineState_PoweredOff
10908 || mData->mMachineState == MachineState_Aborted
10909 || mData->mMachineState == MachineState_Teleported)
10910 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10911 continue;
10912 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10913 prop.strName = it->first;
10914 prop.strValue = property.strValue;
10915 prop.timestamp = (uint64_t)property.mTimestamp;
10916 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10917 GuestPropWriteFlags(property.mFlags, szFlags);
10918 prop.strFlags = szFlags;
10919
10920 data.llGuestProperties.push_back(prop);
10921 }
10922
10923 /* I presume this doesn't require a backup(). */
10924 mData->mGuestPropertiesModified = FALSE;
10925#endif /* VBOX_WITH_GUEST_PROPS defined */
10926
10927 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10928 if (FAILED(hrc)) throw hrc;
10929
10930 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10931 *pAutostart = mHWData->mAutostart;
10932
10933 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10934 }
10935 catch (std::bad_alloc &)
10936 {
10937 return E_OUTOFMEMORY;
10938 }
10939
10940 AssertComRC(hrc);
10941 return hrc;
10942}
10943
10944/**
10945 * Saves the storage controller configuration.
10946 *
10947 * @param data storage settings.
10948 */
10949HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10950{
10951 data.llStorageControllers.clear();
10952
10953 for (StorageControllerList::const_iterator
10954 it = mStorageControllers->begin();
10955 it != mStorageControllers->end();
10956 ++it)
10957 {
10958 ComObjPtr<StorageController> pCtl = *it;
10959
10960 settings::StorageController ctl;
10961 ctl.strName = pCtl->i_getName();
10962 ctl.controllerType = pCtl->i_getControllerType();
10963 ctl.storageBus = pCtl->i_getStorageBus();
10964 ctl.ulInstance = pCtl->i_getInstance();
10965 ctl.fBootable = pCtl->i_getBootable();
10966
10967 /* Save the port count. */
10968 ULONG portCount;
10969 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10970 ComAssertComRCRet(hrc, hrc);
10971 ctl.ulPortCount = portCount;
10972
10973 /* Save fUseHostIOCache */
10974 BOOL fUseHostIOCache;
10975 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10976 ComAssertComRCRet(hrc, hrc);
10977 ctl.fUseHostIOCache = !!fUseHostIOCache;
10978
10979 /* save the devices now. */
10980 hrc = i_saveStorageDevices(pCtl, ctl);
10981 ComAssertComRCRet(hrc, hrc);
10982
10983 data.llStorageControllers.push_back(ctl);
10984 }
10985
10986 return S_OK;
10987}
10988
10989/**
10990 * Saves the hard disk configuration.
10991 */
10992HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10993 settings::StorageController &data)
10994{
10995 MediumAttachmentList atts;
10996
10997 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10998 if (FAILED(hrc)) return hrc;
10999
11000 data.llAttachedDevices.clear();
11001 for (MediumAttachmentList::const_iterator
11002 it = atts.begin();
11003 it != atts.end();
11004 ++it)
11005 {
11006 settings::AttachedDevice dev;
11007 IMediumAttachment *iA = *it;
11008 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11009 Medium *pMedium = pAttach->i_getMedium();
11010
11011 dev.deviceType = pAttach->i_getType();
11012 dev.lPort = pAttach->i_getPort();
11013 dev.lDevice = pAttach->i_getDevice();
11014 dev.fPassThrough = pAttach->i_getPassthrough();
11015 dev.fHotPluggable = pAttach->i_getHotPluggable();
11016 if (pMedium)
11017 {
11018 if (pMedium->i_isHostDrive())
11019 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11020 else
11021 dev.uuid = pMedium->i_getId();
11022 dev.fTempEject = pAttach->i_getTempEject();
11023 dev.fNonRotational = pAttach->i_getNonRotational();
11024 dev.fDiscard = pAttach->i_getDiscard();
11025 }
11026
11027 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11028
11029 data.llAttachedDevices.push_back(dev);
11030 }
11031
11032 return S_OK;
11033}
11034
11035/**
11036 * Saves machine state settings as defined by aFlags
11037 * (SaveSTS_* values).
11038 *
11039 * @param aFlags Combination of SaveSTS_* flags.
11040 *
11041 * @note Locks objects for writing.
11042 */
11043HRESULT Machine::i_saveStateSettings(int aFlags)
11044{
11045 if (aFlags == 0)
11046 return S_OK;
11047
11048 AutoCaller autoCaller(this);
11049 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11050
11051 /* This object's write lock is also necessary to serialize file access
11052 * (prevent concurrent reads and writes) */
11053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11054
11055 HRESULT hrc = S_OK;
11056
11057 Assert(mData->pMachineConfigFile);
11058
11059 try
11060 {
11061 if (aFlags & SaveSTS_CurStateModified)
11062 mData->pMachineConfigFile->fCurrentStateModified = true;
11063
11064 if (aFlags & SaveSTS_StateFilePath)
11065 {
11066 if (!mSSData->strStateFilePath.isEmpty())
11067 /* try to make the file name relative to the settings file dir */
11068 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11069 else
11070 mData->pMachineConfigFile->strStateFile.setNull();
11071 }
11072
11073 if (aFlags & SaveSTS_StateTimeStamp)
11074 {
11075 Assert( mData->mMachineState != MachineState_Aborted
11076 || mSSData->strStateFilePath.isEmpty());
11077
11078 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11079
11080 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11081 || mData->mMachineState == MachineState_AbortedSaved);
11082/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11083 }
11084
11085 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11086 }
11087 catch (...)
11088 {
11089 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11090 }
11091
11092 return hrc;
11093}
11094
11095/**
11096 * Ensures that the given medium is added to a media registry. If this machine
11097 * was created with 4.0 or later, then the machine registry is used. Otherwise
11098 * the global VirtualBox media registry is used.
11099 *
11100 * Caller must NOT hold machine lock, media tree or any medium locks!
11101 *
11102 * @param pMedium
11103 */
11104void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11105{
11106 /* Paranoia checks: do not hold machine or media tree locks. */
11107 AssertReturnVoid(!isWriteLockOnCurrentThread());
11108 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11109
11110 ComObjPtr<Medium> pBase;
11111 {
11112 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11113 pBase = pMedium->i_getBase();
11114 }
11115
11116 /* Paranoia checks: do not hold medium locks. */
11117 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11118 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11119
11120 // decide which medium registry to use now that the medium is attached:
11121 Guid uuid;
11122 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11123 if (fCanHaveOwnMediaRegistry)
11124 // machine XML is VirtualBox 4.0 or higher:
11125 uuid = i_getId(); // machine UUID
11126 else
11127 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11128
11129 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11130 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11131 if (pMedium->i_addRegistry(uuid))
11132 mParent->i_markRegistryModified(uuid);
11133
11134 /* For more complex hard disk structures it can happen that the base
11135 * medium isn't yet associated with any medium registry. Do that now. */
11136 if (pMedium != pBase)
11137 {
11138 /* Tree lock needed by Medium::addRegistryAll. */
11139 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11140 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11141 {
11142 treeLock.release();
11143 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11144 treeLock.acquire();
11145 }
11146 if (pBase->i_addRegistryAll(uuid))
11147 {
11148 treeLock.release();
11149 mParent->i_markRegistryModified(uuid);
11150 }
11151 }
11152}
11153
11154/**
11155 * Physically deletes a file belonging to a machine.
11156 *
11157 * @returns HRESULT
11158 * @retval VBOX_E_FILE_ERROR on failure.
11159 * @param strFile File to delete.
11160 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11161 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11162 * @param strWhat File hint which will be used when setting an error. Optional.
11163 * @param prc Where to return IPRT's status code on failure.
11164 * Optional and can be NULL.
11165 */
11166HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11167 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11168{
11169 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11170
11171 HRESULT hrc = S_OK;
11172
11173 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11174
11175 int vrc = RTFileDelete(strFile.c_str());
11176 if (RT_FAILURE(vrc))
11177 {
11178 if ( !fIgnoreFailures
11179 /* Don't (externally) bitch about stuff which doesn't exist. */
11180 && ( vrc != VERR_FILE_NOT_FOUND
11181 && vrc != VERR_PATH_NOT_FOUND
11182 )
11183 )
11184 {
11185 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11186
11187 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11188 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11189 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
11190 }
11191 }
11192
11193 if (prc)
11194 *prc = vrc;
11195 return hrc;
11196}
11197
11198/**
11199 * Creates differencing hard disks for all normal hard disks attached to this
11200 * machine and a new set of attachments to refer to created disks.
11201 *
11202 * Used when taking a snapshot or when deleting the current state. Gets called
11203 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11204 *
11205 * This method assumes that mMediumAttachments contains the original hard disk
11206 * attachments it needs to create diffs for. On success, these attachments will
11207 * be replaced with the created diffs.
11208 *
11209 * Attachments with non-normal hard disks are left as is.
11210 *
11211 * If @a aOnline is @c false then the original hard disks that require implicit
11212 * diffs will be locked for reading. Otherwise it is assumed that they are
11213 * already locked for writing (when the VM was started). Note that in the latter
11214 * case it is responsibility of the caller to lock the newly created diffs for
11215 * writing if this method succeeds.
11216 *
11217 * @param aProgress Progress object to run (must contain at least as
11218 * many operations left as the number of hard disks
11219 * attached).
11220 * @param aWeight Weight of this operation.
11221 * @param aOnline Whether the VM was online prior to this operation.
11222 *
11223 * @note The progress object is not marked as completed, neither on success nor
11224 * on failure. This is a responsibility of the caller.
11225 *
11226 * @note Locks this object and the media tree for writing.
11227 */
11228HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11229 ULONG aWeight,
11230 bool aOnline)
11231{
11232 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11233
11234 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11235 AssertReturn(!!pProgressControl, E_INVALIDARG);
11236
11237 AutoCaller autoCaller(this);
11238 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11239
11240 AutoMultiWriteLock2 alock(this->lockHandle(),
11241 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11242
11243 /* must be in a protective state because we release the lock below */
11244 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11245 || mData->mMachineState == MachineState_OnlineSnapshotting
11246 || mData->mMachineState == MachineState_LiveSnapshotting
11247 || mData->mMachineState == MachineState_RestoringSnapshot
11248 || mData->mMachineState == MachineState_DeletingSnapshot
11249 , E_FAIL);
11250
11251 HRESULT hrc = S_OK;
11252
11253 // use appropriate locked media map (online or offline)
11254 MediumLockListMap lockedMediaOffline;
11255 MediumLockListMap *lockedMediaMap;
11256 if (aOnline)
11257 lockedMediaMap = &mData->mSession.mLockedMedia;
11258 else
11259 lockedMediaMap = &lockedMediaOffline;
11260
11261 try
11262 {
11263 if (!aOnline)
11264 {
11265 /* lock all attached hard disks early to detect "in use"
11266 * situations before creating actual diffs */
11267 for (MediumAttachmentList::const_iterator
11268 it = mMediumAttachments->begin();
11269 it != mMediumAttachments->end();
11270 ++it)
11271 {
11272 MediumAttachment *pAtt = *it;
11273 if (pAtt->i_getType() == DeviceType_HardDisk)
11274 {
11275 Medium *pMedium = pAtt->i_getMedium();
11276 Assert(pMedium);
11277
11278 MediumLockList *pMediumLockList(new MediumLockList());
11279 alock.release();
11280 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11281 NULL /* pToLockWrite */,
11282 false /* fMediumLockWriteAll */,
11283 NULL,
11284 *pMediumLockList);
11285 alock.acquire();
11286 if (FAILED(hrc))
11287 {
11288 delete pMediumLockList;
11289 throw hrc;
11290 }
11291 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11292 if (FAILED(hrc))
11293 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
11294 }
11295 }
11296
11297 /* Now lock all media. If this fails, nothing is locked. */
11298 alock.release();
11299 hrc = lockedMediaMap->Lock();
11300 alock.acquire();
11301 if (FAILED(hrc))
11302 throw setError(hrc, tr("Locking of attached media failed"));
11303 }
11304
11305 /* remember the current list (note that we don't use backup() since
11306 * mMediumAttachments may be already backed up) */
11307 MediumAttachmentList atts = *mMediumAttachments.data();
11308
11309 /* start from scratch */
11310 mMediumAttachments->clear();
11311
11312 /* go through remembered attachments and create diffs for normal hard
11313 * disks and attach them */
11314 for (MediumAttachmentList::const_iterator
11315 it = atts.begin();
11316 it != atts.end();
11317 ++it)
11318 {
11319 MediumAttachment *pAtt = *it;
11320
11321 DeviceType_T devType = pAtt->i_getType();
11322 Medium *pMedium = pAtt->i_getMedium();
11323
11324 if ( devType != DeviceType_HardDisk
11325 || pMedium == NULL
11326 || pMedium->i_getType() != MediumType_Normal)
11327 {
11328 /* copy the attachment as is */
11329
11330 /** @todo the progress object created in SessionMachine::TakeSnaphot
11331 * only expects operations for hard disks. Later other
11332 * device types need to show up in the progress as well. */
11333 if (devType == DeviceType_HardDisk)
11334 {
11335 if (pMedium == NULL)
11336 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11337 aWeight); // weight
11338 else
11339 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11340 pMedium->i_getBase()->i_getName().c_str()).raw(),
11341 aWeight); // weight
11342 }
11343
11344 mMediumAttachments->push_back(pAtt);
11345 continue;
11346 }
11347
11348 /* need a diff */
11349 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11350 pMedium->i_getBase()->i_getName().c_str()).raw(),
11351 aWeight); // weight
11352
11353 Utf8Str strFullSnapshotFolder;
11354 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11355
11356 ComObjPtr<Medium> diff;
11357 diff.createObject();
11358 // store the diff in the same registry as the parent
11359 // (this cannot fail here because we can't create implicit diffs for
11360 // unregistered images)
11361 Guid uuidRegistryParent;
11362 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11363 Assert(fInRegistry); NOREF(fInRegistry);
11364 hrc = diff->init(mParent,
11365 pMedium->i_getPreferredDiffFormat(),
11366 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11367 uuidRegistryParent,
11368 DeviceType_HardDisk);
11369 if (FAILED(hrc)) throw hrc;
11370
11371 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11372 * the push_back? Looks like we're going to release medium with the
11373 * wrong kind of lock (general issue with if we fail anywhere at all)
11374 * and an orphaned VDI in the snapshots folder. */
11375
11376 /* update the appropriate lock list */
11377 MediumLockList *pMediumLockList;
11378 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
11379 AssertComRCThrowRC(hrc);
11380 if (aOnline)
11381 {
11382 alock.release();
11383 /* The currently attached medium will be read-only, change
11384 * the lock type to read. */
11385 hrc = pMediumLockList->Update(pMedium, false);
11386 alock.acquire();
11387 AssertComRCThrowRC(hrc);
11388 }
11389
11390 /* release the locks before the potentially lengthy operation */
11391 alock.release();
11392 hrc = pMedium->i_createDiffStorage(diff,
11393 pMedium->i_getPreferredDiffVariant(),
11394 pMediumLockList,
11395 NULL /* aProgress */,
11396 true /* aWait */,
11397 false /* aNotify */);
11398 alock.acquire();
11399 if (FAILED(hrc)) throw hrc;
11400
11401 /* actual lock list update is done in Machine::i_commitMedia */
11402
11403 hrc = diff->i_addBackReference(mData->mUuid);
11404 AssertComRCThrowRC(hrc);
11405
11406 /* add a new attachment */
11407 ComObjPtr<MediumAttachment> attachment;
11408 attachment.createObject();
11409 hrc = attachment->init(this,
11410 diff,
11411 pAtt->i_getControllerName(),
11412 pAtt->i_getPort(),
11413 pAtt->i_getDevice(),
11414 DeviceType_HardDisk,
11415 true /* aImplicit */,
11416 false /* aPassthrough */,
11417 false /* aTempEject */,
11418 pAtt->i_getNonRotational(),
11419 pAtt->i_getDiscard(),
11420 pAtt->i_getHotPluggable(),
11421 pAtt->i_getBandwidthGroup());
11422 if (FAILED(hrc)) throw hrc;
11423
11424 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11425 AssertComRCThrowRC(hrc);
11426 mMediumAttachments->push_back(attachment);
11427 }
11428 }
11429 catch (HRESULT hrcXcpt)
11430 {
11431 hrc = hrcXcpt;
11432 }
11433
11434 /* unlock all hard disks we locked when there is no VM */
11435 if (!aOnline)
11436 {
11437 ErrorInfoKeeper eik;
11438
11439 HRESULT rc1 = lockedMediaMap->Clear();
11440 AssertComRC(rc1);
11441 }
11442
11443 return hrc;
11444}
11445
11446/**
11447 * Deletes implicit differencing hard disks created either by
11448 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11449 * mMediumAttachments.
11450 *
11451 * Note that to delete hard disks created by #attachDevice() this method is
11452 * called from #i_rollbackMedia() when the changes are rolled back.
11453 *
11454 * @note Locks this object and the media tree for writing.
11455 */
11456HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11457{
11458 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11459
11460 AutoCaller autoCaller(this);
11461 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11462
11463 AutoMultiWriteLock2 alock(this->lockHandle(),
11464 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11465
11466 /* We absolutely must have backed up state. */
11467 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11468
11469 /* Check if there are any implicitly created diff images. */
11470 bool fImplicitDiffs = false;
11471 for (MediumAttachmentList::const_iterator
11472 it = mMediumAttachments->begin();
11473 it != mMediumAttachments->end();
11474 ++it)
11475 {
11476 const ComObjPtr<MediumAttachment> &pAtt = *it;
11477 if (pAtt->i_isImplicit())
11478 {
11479 fImplicitDiffs = true;
11480 break;
11481 }
11482 }
11483 /* If there is nothing to do, leave early. This saves lots of image locking
11484 * effort. It also avoids a MachineStateChanged event without real reason.
11485 * This is important e.g. when loading a VM config, because there should be
11486 * no events. Otherwise API clients can become thoroughly confused for
11487 * inaccessible VMs (the code for loading VM configs uses this method for
11488 * cleanup if the config makes no sense), as they take such events as an
11489 * indication that the VM is alive, and they would force the VM config to
11490 * be reread, leading to an endless loop. */
11491 if (!fImplicitDiffs)
11492 return S_OK;
11493
11494 HRESULT hrc = S_OK;
11495 MachineState_T oldState = mData->mMachineState;
11496
11497 /* will release the lock before the potentially lengthy operation,
11498 * so protect with the special state (unless already protected) */
11499 if ( oldState != MachineState_Snapshotting
11500 && oldState != MachineState_OnlineSnapshotting
11501 && oldState != MachineState_LiveSnapshotting
11502 && oldState != MachineState_RestoringSnapshot
11503 && oldState != MachineState_DeletingSnapshot
11504 && oldState != MachineState_DeletingSnapshotOnline
11505 && oldState != MachineState_DeletingSnapshotPaused
11506 )
11507 i_setMachineState(MachineState_SettingUp);
11508
11509 // use appropriate locked media map (online or offline)
11510 MediumLockListMap lockedMediaOffline;
11511 MediumLockListMap *lockedMediaMap;
11512 if (aOnline)
11513 lockedMediaMap = &mData->mSession.mLockedMedia;
11514 else
11515 lockedMediaMap = &lockedMediaOffline;
11516
11517 try
11518 {
11519 if (!aOnline)
11520 {
11521 /* lock all attached hard disks early to detect "in use"
11522 * situations before deleting actual diffs */
11523 for (MediumAttachmentList::const_iterator
11524 it = mMediumAttachments->begin();
11525 it != mMediumAttachments->end();
11526 ++it)
11527 {
11528 MediumAttachment *pAtt = *it;
11529 if (pAtt->i_getType() == DeviceType_HardDisk)
11530 {
11531 Medium *pMedium = pAtt->i_getMedium();
11532 Assert(pMedium);
11533
11534 MediumLockList *pMediumLockList(new MediumLockList());
11535 alock.release();
11536 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11537 NULL /* pToLockWrite */,
11538 false /* fMediumLockWriteAll */,
11539 NULL,
11540 *pMediumLockList);
11541 alock.acquire();
11542
11543 if (FAILED(hrc))
11544 {
11545 delete pMediumLockList;
11546 throw hrc;
11547 }
11548
11549 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11550 if (FAILED(hrc))
11551 throw hrc;
11552 }
11553 }
11554
11555 if (FAILED(hrc))
11556 throw hrc;
11557 } // end of offline
11558
11559 /* Lock lists are now up to date and include implicitly created media */
11560
11561 /* Go through remembered attachments and delete all implicitly created
11562 * diffs and fix up the attachment information */
11563 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11564 MediumAttachmentList implicitAtts;
11565 for (MediumAttachmentList::const_iterator
11566 it = mMediumAttachments->begin();
11567 it != mMediumAttachments->end();
11568 ++it)
11569 {
11570 ComObjPtr<MediumAttachment> pAtt = *it;
11571 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11572 if (pMedium.isNull())
11573 continue;
11574
11575 // Implicit attachments go on the list for deletion and back references are removed.
11576 if (pAtt->i_isImplicit())
11577 {
11578 /* Deassociate and mark for deletion */
11579 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11580 hrc = pMedium->i_removeBackReference(mData->mUuid);
11581 if (FAILED(hrc))
11582 throw hrc;
11583 implicitAtts.push_back(pAtt);
11584 continue;
11585 }
11586
11587 /* Was this medium attached before? */
11588 if (!i_findAttachment(oldAtts, pMedium))
11589 {
11590 /* no: de-associate */
11591 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11592 hrc = pMedium->i_removeBackReference(mData->mUuid);
11593 if (FAILED(hrc))
11594 throw hrc;
11595 continue;
11596 }
11597 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11598 }
11599
11600 /* If there are implicit attachments to delete, throw away the lock
11601 * map contents (which will unlock all media) since the medium
11602 * attachments will be rolled back. Below we need to completely
11603 * recreate the lock map anyway since it is infinitely complex to
11604 * do this incrementally (would need reconstructing each attachment
11605 * change, which would be extremely hairy). */
11606 if (implicitAtts.size() != 0)
11607 {
11608 ErrorInfoKeeper eik;
11609
11610 HRESULT rc1 = lockedMediaMap->Clear();
11611 AssertComRC(rc1);
11612 }
11613
11614 /* rollback hard disk changes */
11615 mMediumAttachments.rollback();
11616
11617 MultiResult mrc(S_OK);
11618
11619 // Delete unused implicit diffs.
11620 if (implicitAtts.size() != 0)
11621 {
11622 alock.release();
11623
11624 for (MediumAttachmentList::const_iterator
11625 it = implicitAtts.begin();
11626 it != implicitAtts.end();
11627 ++it)
11628 {
11629 // Remove medium associated with this attachment.
11630 ComObjPtr<MediumAttachment> pAtt = *it;
11631 Assert(pAtt);
11632 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11633 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11634 Assert(pMedium);
11635
11636 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11637 // continue on delete failure, just collect error messages
11638 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
11639 pMedium->i_getLocationFull().c_str() ));
11640 mrc = hrc;
11641 }
11642 // Clear the list of deleted implicit attachments now, while not
11643 // holding the lock, as it will ultimately trigger Medium::uninit()
11644 // calls which assume that the media tree lock isn't held.
11645 implicitAtts.clear();
11646
11647 alock.acquire();
11648
11649 /* if there is a VM recreate media lock map as mentioned above,
11650 * otherwise it is a waste of time and we leave things unlocked */
11651 if (aOnline)
11652 {
11653 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11654 /* must never be NULL, but better safe than sorry */
11655 if (!pMachine.isNull())
11656 {
11657 alock.release();
11658 hrc = mData->mSession.mMachine->i_lockMedia();
11659 alock.acquire();
11660 if (FAILED(hrc))
11661 throw hrc;
11662 }
11663 }
11664 }
11665 }
11666 catch (HRESULT hrcXcpt)
11667 {
11668 hrc = hrcXcpt;
11669 }
11670
11671 if (mData->mMachineState == MachineState_SettingUp)
11672 i_setMachineState(oldState);
11673
11674 /* unlock all hard disks we locked when there is no VM */
11675 if (!aOnline)
11676 {
11677 ErrorInfoKeeper eik;
11678
11679 HRESULT rc1 = lockedMediaMap->Clear();
11680 AssertComRC(rc1);
11681 }
11682
11683 return hrc;
11684}
11685
11686
11687/**
11688 * Looks through the given list of media attachments for one with the given parameters
11689 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11690 * can be searched as well if needed.
11691 *
11692 * @param ll
11693 * @param aControllerName
11694 * @param aControllerPort
11695 * @param aDevice
11696 * @return
11697 */
11698MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11699 const Utf8Str &aControllerName,
11700 LONG aControllerPort,
11701 LONG aDevice)
11702{
11703 for (MediumAttachmentList::const_iterator
11704 it = ll.begin();
11705 it != ll.end();
11706 ++it)
11707 {
11708 MediumAttachment *pAttach = *it;
11709 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11710 return pAttach;
11711 }
11712
11713 return NULL;
11714}
11715
11716/**
11717 * Looks through the given list of media attachments for one with the given parameters
11718 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11719 * can be searched as well if needed.
11720 *
11721 * @param ll
11722 * @param pMedium
11723 * @return
11724 */
11725MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11726 ComObjPtr<Medium> pMedium)
11727{
11728 for (MediumAttachmentList::const_iterator
11729 it = ll.begin();
11730 it != ll.end();
11731 ++it)
11732 {
11733 MediumAttachment *pAttach = *it;
11734 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11735 if (pMediumThis == pMedium)
11736 return pAttach;
11737 }
11738
11739 return NULL;
11740}
11741
11742/**
11743 * Looks through the given list of media attachments for one with the given parameters
11744 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11745 * can be searched as well if needed.
11746 *
11747 * @param ll
11748 * @param id
11749 * @return
11750 */
11751MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11752 Guid &id)
11753{
11754 for (MediumAttachmentList::const_iterator
11755 it = ll.begin();
11756 it != ll.end();
11757 ++it)
11758 {
11759 MediumAttachment *pAttach = *it;
11760 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11761 if (pMediumThis->i_getId() == id)
11762 return pAttach;
11763 }
11764
11765 return NULL;
11766}
11767
11768/**
11769 * Main implementation for Machine::DetachDevice. This also gets called
11770 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11771 *
11772 * @param pAttach Medium attachment to detach.
11773 * @param writeLock Machine write lock which the caller must have locked once.
11774 * This may be released temporarily in here.
11775 * @param pSnapshot If NULL, then the detachment is for the current machine.
11776 * Otherwise this is for a SnapshotMachine, and this must be
11777 * its snapshot.
11778 * @return
11779 */
11780HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11781 AutoWriteLock &writeLock,
11782 Snapshot *pSnapshot)
11783{
11784 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11785 DeviceType_T mediumType = pAttach->i_getType();
11786
11787 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11788
11789 if (pAttach->i_isImplicit())
11790 {
11791 /* attempt to implicitly delete the implicitly created diff */
11792
11793 /// @todo move the implicit flag from MediumAttachment to Medium
11794 /// and forbid any hard disk operation when it is implicit. Or maybe
11795 /// a special media state for it to make it even more simple.
11796
11797 Assert(mMediumAttachments.isBackedUp());
11798
11799 /* will release the lock before the potentially lengthy operation, so
11800 * protect with the special state */
11801 MachineState_T oldState = mData->mMachineState;
11802 i_setMachineState(MachineState_SettingUp);
11803
11804 writeLock.release();
11805
11806 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11807
11808 writeLock.acquire();
11809
11810 i_setMachineState(oldState);
11811
11812 if (FAILED(hrc)) return hrc;
11813 }
11814
11815 i_setModified(IsModified_Storage);
11816 mMediumAttachments.backup();
11817 mMediumAttachments->remove(pAttach);
11818
11819 if (!oldmedium.isNull())
11820 {
11821 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11822 if (pSnapshot)
11823 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11824 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11825 else if (mediumType != DeviceType_HardDisk)
11826 oldmedium->i_removeBackReference(mData->mUuid);
11827 }
11828
11829 return S_OK;
11830}
11831
11832/**
11833 * Goes thru all media of the given list and
11834 *
11835 * 1) calls i_detachDevice() on each of them for this machine and
11836 * 2) adds all Medium objects found in the process to the given list,
11837 * depending on cleanupMode.
11838 *
11839 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11840 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11841 * media to the list.
11842 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disk and
11843 * also removable medias if they are located in the VM folder and referenced
11844 * only by this VM (media prepared by unattended installer).
11845 *
11846 * This gets called from Machine::Unregister, both for the actual Machine and
11847 * the SnapshotMachine objects that might be found in the snapshots.
11848 *
11849 * Requires caller and locking. The machine lock must be passed in because it
11850 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11851 *
11852 * @param writeLock Machine lock from top-level caller; this gets passed to
11853 * i_detachDevice.
11854 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11855 * object if called for a SnapshotMachine.
11856 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11857 * added to llMedia; if Full, then all media get added;
11858 * otherwise no media get added.
11859 * @param llMedia Caller's list to receive Medium objects which got detached so
11860 * caller can close() them, depending on cleanupMode.
11861 * @return
11862 */
11863HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11864 Snapshot *pSnapshot,
11865 CleanupMode_T cleanupMode,
11866 MediaList &llMedia)
11867{
11868 Assert(isWriteLockOnCurrentThread());
11869
11870 HRESULT hrc;
11871
11872 // make a temporary list because i_detachDevice invalidates iterators into
11873 // mMediumAttachments
11874 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11875
11876 for (MediumAttachmentList::iterator
11877 it = llAttachments2.begin();
11878 it != llAttachments2.end();
11879 ++it)
11880 {
11881 ComObjPtr<MediumAttachment> &pAttach = *it;
11882 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11883
11884 if (!pMedium.isNull())
11885 {
11886 AutoCaller mac(pMedium);
11887 if (FAILED(mac.hrc())) return mac.hrc();
11888 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11889 DeviceType_T devType = pMedium->i_getDeviceType();
11890 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11891 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11892 strMediumLocation.stripFilename();
11893 Utf8Str strMachineFolder = i_getSettingsFileFull();
11894 strMachineFolder.stripFilename();
11895 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11896 && devType == DeviceType_HardDisk)
11897 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11898 && ( devType == DeviceType_HardDisk
11899 || ( cBackRefs <= 1
11900 && strMediumLocation == strMachineFolder
11901 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11902 || (cleanupMode == CleanupMode_Full)
11903 )
11904 {
11905 llMedia.push_back(pMedium);
11906 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11907 /* Not allowed to keep this lock as below we need the parent
11908 * medium lock, and the lock order is parent to child. */
11909 lock.release();
11910 /*
11911 * Search for medias which are not attached to any machine, but
11912 * in the chain to an attached disk. Mediums are only consided
11913 * if they are:
11914 * - have only one child
11915 * - no references to any machines
11916 * - are of normal medium type
11917 */
11918 while (!pParent.isNull())
11919 {
11920 AutoCaller mac1(pParent);
11921 if (FAILED(mac1.hrc())) return mac1.hrc();
11922 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11923 if (pParent->i_getChildren().size() == 1)
11924 {
11925 if ( pParent->i_getMachineBackRefCount() == 0
11926 && pParent->i_getType() == MediumType_Normal
11927 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11928 llMedia.push_back(pParent);
11929 }
11930 else
11931 break;
11932 pParent = pParent->i_getParent();
11933 }
11934 }
11935 }
11936
11937 // real machine: then we need to use the proper method
11938 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11939
11940 if (FAILED(hrc))
11941 return hrc;
11942 }
11943
11944 return S_OK;
11945}
11946
11947/**
11948 * Perform deferred hard disk detachments.
11949 *
11950 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11951 * changed (not backed up).
11952 *
11953 * If @a aOnline is @c true then this method will also unlock the old hard
11954 * disks for which the new implicit diffs were created and will lock these new
11955 * diffs for writing.
11956 *
11957 * @param aOnline Whether the VM was online prior to this operation.
11958 *
11959 * @note Locks this object for writing!
11960 */
11961void Machine::i_commitMedia(bool aOnline /*= false*/)
11962{
11963 AutoCaller autoCaller(this);
11964 AssertComRCReturnVoid(autoCaller.hrc());
11965
11966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11967
11968 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11969
11970 HRESULT hrc = S_OK;
11971
11972 /* no attach/detach operations -- nothing to do */
11973 if (!mMediumAttachments.isBackedUp())
11974 return;
11975
11976 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11977 bool fMediaNeedsLocking = false;
11978
11979 /* enumerate new attachments */
11980 for (MediumAttachmentList::const_iterator
11981 it = mMediumAttachments->begin();
11982 it != mMediumAttachments->end();
11983 ++it)
11984 {
11985 MediumAttachment *pAttach = *it;
11986
11987 pAttach->i_commit();
11988
11989 Medium *pMedium = pAttach->i_getMedium();
11990 bool fImplicit = pAttach->i_isImplicit();
11991
11992 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11993 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11994 fImplicit));
11995
11996 /** @todo convert all this Machine-based voodoo to MediumAttachment
11997 * based commit logic. */
11998 if (fImplicit)
11999 {
12000 /* convert implicit attachment to normal */
12001 pAttach->i_setImplicit(false);
12002
12003 if ( aOnline
12004 && pMedium
12005 && pAttach->i_getType() == DeviceType_HardDisk
12006 )
12007 {
12008 /* update the appropriate lock list */
12009 MediumLockList *pMediumLockList;
12010 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12011 AssertComRC(hrc);
12012 if (pMediumLockList)
12013 {
12014 /* unlock if there's a need to change the locking */
12015 if (!fMediaNeedsLocking)
12016 {
12017 Assert(mData->mSession.mLockedMedia.IsLocked());
12018 hrc = mData->mSession.mLockedMedia.Unlock();
12019 AssertComRC(hrc);
12020 fMediaNeedsLocking = true;
12021 }
12022 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
12023 AssertComRC(hrc);
12024 hrc = pMediumLockList->Append(pMedium, true);
12025 AssertComRC(hrc);
12026 }
12027 }
12028
12029 continue;
12030 }
12031
12032 if (pMedium)
12033 {
12034 /* was this medium attached before? */
12035 for (MediumAttachmentList::iterator
12036 oldIt = oldAtts.begin();
12037 oldIt != oldAtts.end();
12038 ++oldIt)
12039 {
12040 MediumAttachment *pOldAttach = *oldIt;
12041 if (pOldAttach->i_getMedium() == pMedium)
12042 {
12043 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12044
12045 /* yes: remove from old to avoid de-association */
12046 oldAtts.erase(oldIt);
12047 break;
12048 }
12049 }
12050 }
12051 }
12052
12053 /* enumerate remaining old attachments and de-associate from the
12054 * current machine state */
12055 for (MediumAttachmentList::const_iterator
12056 it = oldAtts.begin();
12057 it != oldAtts.end();
12058 ++it)
12059 {
12060 MediumAttachment *pAttach = *it;
12061 Medium *pMedium = pAttach->i_getMedium();
12062
12063 /* Detach only hard disks, since DVD/floppy media is detached
12064 * instantly in MountMedium. */
12065 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12066 {
12067 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12068
12069 /* now de-associate from the current machine state */
12070 hrc = pMedium->i_removeBackReference(mData->mUuid);
12071 AssertComRC(hrc);
12072
12073 if (aOnline)
12074 {
12075 /* unlock since medium is not used anymore */
12076 MediumLockList *pMediumLockList;
12077 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12078 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
12079 {
12080 /* this happens for online snapshots, there the attachment
12081 * is changing, but only to a diff image created under
12082 * the old one, so there is no separate lock list */
12083 Assert(!pMediumLockList);
12084 }
12085 else
12086 {
12087 AssertComRC(hrc);
12088 if (pMediumLockList)
12089 {
12090 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
12091 AssertComRC(hrc);
12092 }
12093 }
12094 }
12095 }
12096 }
12097
12098 /* take media locks again so that the locking state is consistent */
12099 if (fMediaNeedsLocking)
12100 {
12101 Assert(aOnline);
12102 hrc = mData->mSession.mLockedMedia.Lock();
12103 AssertComRC(hrc);
12104 }
12105
12106 /* commit the hard disk changes */
12107 mMediumAttachments.commit();
12108
12109 if (i_isSessionMachine())
12110 {
12111 /*
12112 * Update the parent machine to point to the new owner.
12113 * This is necessary because the stored parent will point to the
12114 * session machine otherwise and cause crashes or errors later
12115 * when the session machine gets invalid.
12116 */
12117 /** @todo Change the MediumAttachment class to behave like any other
12118 * class in this regard by creating peer MediumAttachment
12119 * objects for session machines and share the data with the peer
12120 * machine.
12121 */
12122 for (MediumAttachmentList::const_iterator
12123 it = mMediumAttachments->begin();
12124 it != mMediumAttachments->end();
12125 ++it)
12126 (*it)->i_updateParentMachine(mPeer);
12127
12128 /* attach new data to the primary machine and reshare it */
12129 mPeer->mMediumAttachments.attach(mMediumAttachments);
12130 }
12131
12132 return;
12133}
12134
12135/**
12136 * Perform deferred deletion of implicitly created diffs.
12137 *
12138 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12139 * changed (not backed up).
12140 *
12141 * @note Locks this object for writing!
12142 */
12143void Machine::i_rollbackMedia()
12144{
12145 AutoCaller autoCaller(this);
12146 AssertComRCReturnVoid(autoCaller.hrc());
12147
12148 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12149 LogFlowThisFunc(("Entering rollbackMedia\n"));
12150
12151 HRESULT hrc = S_OK;
12152
12153 /* no attach/detach operations -- nothing to do */
12154 if (!mMediumAttachments.isBackedUp())
12155 return;
12156
12157 /* enumerate new attachments */
12158 for (MediumAttachmentList::const_iterator
12159 it = mMediumAttachments->begin();
12160 it != mMediumAttachments->end();
12161 ++it)
12162 {
12163 MediumAttachment *pAttach = *it;
12164 /* Fix up the backrefs for DVD/floppy media. */
12165 if (pAttach->i_getType() != DeviceType_HardDisk)
12166 {
12167 Medium *pMedium = pAttach->i_getMedium();
12168 if (pMedium)
12169 {
12170 hrc = pMedium->i_removeBackReference(mData->mUuid);
12171 AssertComRC(hrc);
12172 }
12173 }
12174
12175 (*it)->i_rollback();
12176
12177 pAttach = *it;
12178 /* Fix up the backrefs for DVD/floppy media. */
12179 if (pAttach->i_getType() != DeviceType_HardDisk)
12180 {
12181 Medium *pMedium = pAttach->i_getMedium();
12182 if (pMedium)
12183 {
12184 hrc = pMedium->i_addBackReference(mData->mUuid);
12185 AssertComRC(hrc);
12186 }
12187 }
12188 }
12189
12190 /** @todo convert all this Machine-based voodoo to MediumAttachment
12191 * based rollback logic. */
12192 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12193
12194 return;
12195}
12196
12197/**
12198 * Returns true if the settings file is located in the directory named exactly
12199 * as the machine; this means, among other things, that the machine directory
12200 * should be auto-renamed.
12201 *
12202 * @param aSettingsDir if not NULL, the full machine settings file directory
12203 * name will be assigned there.
12204 *
12205 * @note Doesn't lock anything.
12206 * @note Not thread safe (must be called from this object's lock).
12207 */
12208bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12209{
12210 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12211 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12212 if (aSettingsDir)
12213 *aSettingsDir = strMachineDirName;
12214 strMachineDirName.stripPath(); // vmname
12215 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12216 strConfigFileOnly.stripPath() // vmname.vbox
12217 .stripSuffix(); // vmname
12218 /** @todo hack, make somehow use of ComposeMachineFilename */
12219 if (mUserData->s.fDirectoryIncludesUUID)
12220 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12221
12222 AssertReturn(!strMachineDirName.isEmpty(), false);
12223 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12224
12225 return strMachineDirName == strConfigFileOnly;
12226}
12227
12228/**
12229 * Discards all changes to machine settings.
12230 *
12231 * @param aNotify Whether to notify the direct session about changes or not.
12232 *
12233 * @note Locks objects for writing!
12234 */
12235void Machine::i_rollback(bool aNotify)
12236{
12237 AutoCaller autoCaller(this);
12238 AssertComRCReturn(autoCaller.hrc(), (void)0);
12239
12240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12241
12242 if (!mStorageControllers.isNull())
12243 {
12244 if (mStorageControllers.isBackedUp())
12245 {
12246 /* unitialize all new devices (absent in the backed up list). */
12247 StorageControllerList *backedList = mStorageControllers.backedUpData();
12248 for (StorageControllerList::const_iterator
12249 it = mStorageControllers->begin();
12250 it != mStorageControllers->end();
12251 ++it)
12252 {
12253 if ( std::find(backedList->begin(), backedList->end(), *it)
12254 == backedList->end()
12255 )
12256 {
12257 (*it)->uninit();
12258 }
12259 }
12260
12261 /* restore the list */
12262 mStorageControllers.rollback();
12263 }
12264
12265 /* rollback any changes to devices after restoring the list */
12266 if (mData->flModifications & IsModified_Storage)
12267 {
12268 for (StorageControllerList::const_iterator
12269 it = mStorageControllers->begin();
12270 it != mStorageControllers->end();
12271 ++it)
12272 {
12273 (*it)->i_rollback();
12274 }
12275 }
12276 }
12277
12278 if (!mUSBControllers.isNull())
12279 {
12280 if (mUSBControllers.isBackedUp())
12281 {
12282 /* unitialize all new devices (absent in the backed up list). */
12283 USBControllerList *backedList = mUSBControllers.backedUpData();
12284 for (USBControllerList::const_iterator
12285 it = mUSBControllers->begin();
12286 it != mUSBControllers->end();
12287 ++it)
12288 {
12289 if ( std::find(backedList->begin(), backedList->end(), *it)
12290 == backedList->end()
12291 )
12292 {
12293 (*it)->uninit();
12294 }
12295 }
12296
12297 /* restore the list */
12298 mUSBControllers.rollback();
12299 }
12300
12301 /* rollback any changes to devices after restoring the list */
12302 if (mData->flModifications & IsModified_USB)
12303 {
12304 for (USBControllerList::const_iterator
12305 it = mUSBControllers->begin();
12306 it != mUSBControllers->end();
12307 ++it)
12308 {
12309 (*it)->i_rollback();
12310 }
12311 }
12312 }
12313
12314 mUserData.rollback();
12315
12316 mHWData.rollback();
12317
12318 if (mData->flModifications & IsModified_Storage)
12319 i_rollbackMedia();
12320
12321 if (mBIOSSettings)
12322 mBIOSSettings->i_rollback();
12323
12324 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12325 mRecordingSettings->i_rollback();
12326
12327 if (mTrustedPlatformModule)
12328 mTrustedPlatformModule->i_rollback();
12329
12330 if (mNvramStore)
12331 mNvramStore->i_rollback();
12332
12333 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12334 mGraphicsAdapter->i_rollback();
12335
12336 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12337 mVRDEServer->i_rollback();
12338
12339 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12340 mAudioSettings->i_rollback();
12341
12342 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12343 mUSBDeviceFilters->i_rollback();
12344
12345 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12346 mBandwidthControl->i_rollback();
12347
12348 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12349 mGuestDebugControl->i_rollback();
12350
12351 if (!mHWData.isNull())
12352 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12353 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12354 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12355 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12356
12357 if (mData->flModifications & IsModified_NetworkAdapters)
12358 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12359 if ( mNetworkAdapters[slot]
12360 && mNetworkAdapters[slot]->i_isModified())
12361 {
12362 mNetworkAdapters[slot]->i_rollback();
12363 networkAdapters[slot] = mNetworkAdapters[slot];
12364 }
12365
12366 if (mData->flModifications & IsModified_SerialPorts)
12367 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12368 if ( mSerialPorts[slot]
12369 && mSerialPorts[slot]->i_isModified())
12370 {
12371 mSerialPorts[slot]->i_rollback();
12372 serialPorts[slot] = mSerialPorts[slot];
12373 }
12374
12375 if (mData->flModifications & IsModified_ParallelPorts)
12376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12377 if ( mParallelPorts[slot]
12378 && mParallelPorts[slot]->i_isModified())
12379 {
12380 mParallelPorts[slot]->i_rollback();
12381 parallelPorts[slot] = mParallelPorts[slot];
12382 }
12383
12384 if (aNotify)
12385 {
12386 /* inform the direct session about changes */
12387
12388 ComObjPtr<Machine> that = this;
12389 uint32_t flModifications = mData->flModifications;
12390 alock.release();
12391
12392 if (flModifications & IsModified_SharedFolders)
12393 that->i_onSharedFolderChange();
12394
12395 if (flModifications & IsModified_VRDEServer)
12396 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12397 if (flModifications & IsModified_USB)
12398 that->i_onUSBControllerChange();
12399
12400 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12401 if (networkAdapters[slot])
12402 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12403 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12404 if (serialPorts[slot])
12405 that->i_onSerialPortChange(serialPorts[slot]);
12406 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12407 if (parallelPorts[slot])
12408 that->i_onParallelPortChange(parallelPorts[slot]);
12409
12410 if (flModifications & IsModified_Storage)
12411 {
12412 for (StorageControllerList::const_iterator
12413 it = mStorageControllers->begin();
12414 it != mStorageControllers->end();
12415 ++it)
12416 {
12417 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12418 }
12419 }
12420
12421 if (flModifications & IsModified_GuestDebugControl)
12422 that->i_onGuestDebugControlChange(mGuestDebugControl);
12423
12424#if 0
12425 if (flModifications & IsModified_BandwidthControl)
12426 that->onBandwidthControlChange();
12427#endif
12428 }
12429}
12430
12431/**
12432 * Commits all the changes to machine settings.
12433 *
12434 * Note that this operation is supposed to never fail.
12435 *
12436 * @note Locks this object and children for writing.
12437 */
12438void Machine::i_commit()
12439{
12440 AutoCaller autoCaller(this);
12441 AssertComRCReturnVoid(autoCaller.hrc());
12442
12443 AutoCaller peerCaller(mPeer);
12444 AssertComRCReturnVoid(peerCaller.hrc());
12445
12446 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12447
12448 /*
12449 * use safe commit to ensure Snapshot machines (that share mUserData)
12450 * will still refer to a valid memory location
12451 */
12452 mUserData.commitCopy();
12453
12454 mHWData.commit();
12455
12456 if (mMediumAttachments.isBackedUp())
12457 i_commitMedia(Global::IsOnline(mData->mMachineState));
12458
12459 mBIOSSettings->i_commit();
12460 mRecordingSettings->i_commit();
12461 mTrustedPlatformModule->i_commit();
12462 mNvramStore->i_commit();
12463 mGraphicsAdapter->i_commit();
12464 mVRDEServer->i_commit();
12465 mAudioSettings->i_commit();
12466 mUSBDeviceFilters->i_commit();
12467 mBandwidthControl->i_commit();
12468 mGuestDebugControl->i_commit();
12469
12470 /* Since mNetworkAdapters is a list which might have been changed (resized)
12471 * without using the Backupable<> template we need to handle the copying
12472 * of the list entries manually, including the creation of peers for the
12473 * new objects. */
12474 bool commitNetworkAdapters = false;
12475 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12476 if (mPeer)
12477 {
12478 /* commit everything, even the ones which will go away */
12479 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12480 mNetworkAdapters[slot]->i_commit();
12481 /* copy over the new entries, creating a peer and uninit the original */
12482 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12483 for (size_t slot = 0; slot < newSize; slot++)
12484 {
12485 /* look if this adapter has a peer device */
12486 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12487 if (!peer)
12488 {
12489 /* no peer means the adapter is a newly created one;
12490 * create a peer owning data this data share it with */
12491 peer.createObject();
12492 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12493 }
12494 mPeer->mNetworkAdapters[slot] = peer;
12495 }
12496 /* uninit any no longer needed network adapters */
12497 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12498 mNetworkAdapters[slot]->uninit();
12499 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12500 {
12501 if (mPeer->mNetworkAdapters[slot])
12502 mPeer->mNetworkAdapters[slot]->uninit();
12503 }
12504 /* Keep the original network adapter count until this point, so that
12505 * discarding a chipset type change will not lose settings. */
12506 mNetworkAdapters.resize(newSize);
12507 mPeer->mNetworkAdapters.resize(newSize);
12508 }
12509 else
12510 {
12511 /* we have no peer (our parent is the newly created machine);
12512 * just commit changes to the network adapters */
12513 commitNetworkAdapters = true;
12514 }
12515 if (commitNetworkAdapters)
12516 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12517 mNetworkAdapters[slot]->i_commit();
12518
12519 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12520 mSerialPorts[slot]->i_commit();
12521 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12522 mParallelPorts[slot]->i_commit();
12523
12524 bool commitStorageControllers = false;
12525
12526 if (mStorageControllers.isBackedUp())
12527 {
12528 mStorageControllers.commit();
12529
12530 if (mPeer)
12531 {
12532 /* Commit all changes to new controllers (this will reshare data with
12533 * peers for those who have peers) */
12534 StorageControllerList *newList = new StorageControllerList();
12535 for (StorageControllerList::const_iterator
12536 it = mStorageControllers->begin();
12537 it != mStorageControllers->end();
12538 ++it)
12539 {
12540 (*it)->i_commit();
12541
12542 /* look if this controller has a peer device */
12543 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12544 if (!peer)
12545 {
12546 /* no peer means the device is a newly created one;
12547 * create a peer owning data this device share it with */
12548 peer.createObject();
12549 peer->init(mPeer, *it, true /* aReshare */);
12550 }
12551 else
12552 {
12553 /* remove peer from the old list */
12554 mPeer->mStorageControllers->remove(peer);
12555 }
12556 /* and add it to the new list */
12557 newList->push_back(peer);
12558 }
12559
12560 /* uninit old peer's controllers that are left */
12561 for (StorageControllerList::const_iterator
12562 it = mPeer->mStorageControllers->begin();
12563 it != mPeer->mStorageControllers->end();
12564 ++it)
12565 {
12566 (*it)->uninit();
12567 }
12568
12569 /* attach new list of controllers to our peer */
12570 mPeer->mStorageControllers.attach(newList);
12571 }
12572 else
12573 {
12574 /* we have no peer (our parent is the newly created machine);
12575 * just commit changes to devices */
12576 commitStorageControllers = true;
12577 }
12578 }
12579 else
12580 {
12581 /* the list of controllers itself is not changed,
12582 * just commit changes to controllers themselves */
12583 commitStorageControllers = true;
12584 }
12585
12586 if (commitStorageControllers)
12587 {
12588 for (StorageControllerList::const_iterator
12589 it = mStorageControllers->begin();
12590 it != mStorageControllers->end();
12591 ++it)
12592 {
12593 (*it)->i_commit();
12594 }
12595 }
12596
12597 bool commitUSBControllers = false;
12598
12599 if (mUSBControllers.isBackedUp())
12600 {
12601 mUSBControllers.commit();
12602
12603 if (mPeer)
12604 {
12605 /* Commit all changes to new controllers (this will reshare data with
12606 * peers for those who have peers) */
12607 USBControllerList *newList = new USBControllerList();
12608 for (USBControllerList::const_iterator
12609 it = mUSBControllers->begin();
12610 it != mUSBControllers->end();
12611 ++it)
12612 {
12613 (*it)->i_commit();
12614
12615 /* look if this controller has a peer device */
12616 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12617 if (!peer)
12618 {
12619 /* no peer means the device is a newly created one;
12620 * create a peer owning data this device share it with */
12621 peer.createObject();
12622 peer->init(mPeer, *it, true /* aReshare */);
12623 }
12624 else
12625 {
12626 /* remove peer from the old list */
12627 mPeer->mUSBControllers->remove(peer);
12628 }
12629 /* and add it to the new list */
12630 newList->push_back(peer);
12631 }
12632
12633 /* uninit old peer's controllers that are left */
12634 for (USBControllerList::const_iterator
12635 it = mPeer->mUSBControllers->begin();
12636 it != mPeer->mUSBControllers->end();
12637 ++it)
12638 {
12639 (*it)->uninit();
12640 }
12641
12642 /* attach new list of controllers to our peer */
12643 mPeer->mUSBControllers.attach(newList);
12644 }
12645 else
12646 {
12647 /* we have no peer (our parent is the newly created machine);
12648 * just commit changes to devices */
12649 commitUSBControllers = true;
12650 }
12651 }
12652 else
12653 {
12654 /* the list of controllers itself is not changed,
12655 * just commit changes to controllers themselves */
12656 commitUSBControllers = true;
12657 }
12658
12659 if (commitUSBControllers)
12660 {
12661 for (USBControllerList::const_iterator
12662 it = mUSBControllers->begin();
12663 it != mUSBControllers->end();
12664 ++it)
12665 {
12666 (*it)->i_commit();
12667 }
12668 }
12669
12670 if (i_isSessionMachine())
12671 {
12672 /* attach new data to the primary machine and reshare it */
12673 mPeer->mUserData.attach(mUserData);
12674 mPeer->mHWData.attach(mHWData);
12675 /* mmMediumAttachments is reshared by fixupMedia */
12676 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12677 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12678 }
12679}
12680
12681/**
12682 * Copies all the hardware data from the given machine.
12683 *
12684 * Currently, only called when the VM is being restored from a snapshot. In
12685 * particular, this implies that the VM is not running during this method's
12686 * call.
12687 *
12688 * @note This method must be called from under this object's lock.
12689 *
12690 * @note This method doesn't call #i_commit(), so all data remains backed up and
12691 * unsaved.
12692 */
12693void Machine::i_copyFrom(Machine *aThat)
12694{
12695 AssertReturnVoid(!i_isSnapshotMachine());
12696 AssertReturnVoid(aThat->i_isSnapshotMachine());
12697
12698 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12699
12700 mHWData.assignCopy(aThat->mHWData);
12701
12702 // create copies of all shared folders (mHWData after attaching a copy
12703 // contains just references to original objects)
12704 for (HWData::SharedFolderList::iterator
12705 it = mHWData->mSharedFolders.begin();
12706 it != mHWData->mSharedFolders.end();
12707 ++it)
12708 {
12709 ComObjPtr<SharedFolder> folder;
12710 folder.createObject();
12711 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12712 AssertComRC(hrc);
12713 *it = folder;
12714 }
12715
12716 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12717 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12718 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12719 mNvramStore->i_copyFrom(aThat->mNvramStore);
12720 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12721 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12722 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12723 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12724 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12725 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12726
12727 /* create private copies of all controllers */
12728 mStorageControllers.backup();
12729 mStorageControllers->clear();
12730 for (StorageControllerList::const_iterator
12731 it = aThat->mStorageControllers->begin();
12732 it != aThat->mStorageControllers->end();
12733 ++it)
12734 {
12735 ComObjPtr<StorageController> ctrl;
12736 ctrl.createObject();
12737 ctrl->initCopy(this, *it);
12738 mStorageControllers->push_back(ctrl);
12739 }
12740
12741 /* create private copies of all USB controllers */
12742 mUSBControllers.backup();
12743 mUSBControllers->clear();
12744 for (USBControllerList::const_iterator
12745 it = aThat->mUSBControllers->begin();
12746 it != aThat->mUSBControllers->end();
12747 ++it)
12748 {
12749 ComObjPtr<USBController> ctrl;
12750 ctrl.createObject();
12751 ctrl->initCopy(this, *it);
12752 mUSBControllers->push_back(ctrl);
12753 }
12754
12755 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12756 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12757 {
12758 if (mNetworkAdapters[slot].isNotNull())
12759 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12760 else
12761 {
12762 unconst(mNetworkAdapters[slot]).createObject();
12763 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12764 }
12765 }
12766 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12767 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12768 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12769 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12770}
12771
12772/**
12773 * Returns whether the given storage controller is hotplug capable.
12774 *
12775 * @returns true if the controller supports hotplugging
12776 * false otherwise.
12777 * @param enmCtrlType The controller type to check for.
12778 */
12779bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12780{
12781 ComPtr<ISystemProperties> systemProperties;
12782 HRESULT hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12783 if (FAILED(hrc))
12784 return false;
12785
12786 BOOL aHotplugCapable = FALSE;
12787 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12788
12789 return RT_BOOL(aHotplugCapable);
12790}
12791
12792#ifdef VBOX_WITH_RESOURCE_USAGE_API
12793
12794void Machine::i_getDiskList(MediaList &list)
12795{
12796 for (MediumAttachmentList::const_iterator
12797 it = mMediumAttachments->begin();
12798 it != mMediumAttachments->end();
12799 ++it)
12800 {
12801 MediumAttachment *pAttach = *it;
12802 /* just in case */
12803 AssertContinue(pAttach);
12804
12805 AutoCaller localAutoCallerA(pAttach);
12806 if (FAILED(localAutoCallerA.hrc())) continue;
12807
12808 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12809
12810 if (pAttach->i_getType() == DeviceType_HardDisk)
12811 list.push_back(pAttach->i_getMedium());
12812 }
12813}
12814
12815void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12816{
12817 AssertReturnVoid(isWriteLockOnCurrentThread());
12818 AssertPtrReturnVoid(aCollector);
12819
12820 pm::CollectorHAL *hal = aCollector->getHAL();
12821 /* Create sub metrics */
12822 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12823 "Percentage of processor time spent in user mode by the VM process.");
12824 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12825 "Percentage of processor time spent in kernel mode by the VM process.");
12826 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12827 "Size of resident portion of VM process in memory.");
12828 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12829 "Actual size of all VM disks combined.");
12830 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12831 "Network receive rate.");
12832 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12833 "Network transmit rate.");
12834 /* Create and register base metrics */
12835 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12836 cpuLoadUser, cpuLoadKernel);
12837 aCollector->registerBaseMetric(cpuLoad);
12838 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12839 ramUsageUsed);
12840 aCollector->registerBaseMetric(ramUsage);
12841 MediaList disks;
12842 i_getDiskList(disks);
12843 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12844 diskUsageUsed);
12845 aCollector->registerBaseMetric(diskUsage);
12846
12847 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12848 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12849 new pm::AggregateAvg()));
12850 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12851 new pm::AggregateMin()));
12852 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12853 new pm::AggregateMax()));
12854 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12855 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12856 new pm::AggregateAvg()));
12857 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12858 new pm::AggregateMin()));
12859 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12860 new pm::AggregateMax()));
12861
12862 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12863 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12864 new pm::AggregateAvg()));
12865 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12866 new pm::AggregateMin()));
12867 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12868 new pm::AggregateMax()));
12869
12870 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12871 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12872 new pm::AggregateAvg()));
12873 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12874 new pm::AggregateMin()));
12875 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12876 new pm::AggregateMax()));
12877
12878
12879 /* Guest metrics collector */
12880 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12881 aCollector->registerGuest(mCollectorGuest);
12882 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12883
12884 /* Create sub metrics */
12885 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12886 "Percentage of processor time spent in user mode as seen by the guest.");
12887 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12888 "Percentage of processor time spent in kernel mode as seen by the guest.");
12889 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12890 "Percentage of processor time spent idling as seen by the guest.");
12891
12892 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12893 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12894 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12895 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12896 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12897 pm::SubMetric *guestMemCache = new pm::SubMetric(
12898 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12899
12900 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12901 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12902
12903 /* Create and register base metrics */
12904 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12905 machineNetRx, machineNetTx);
12906 aCollector->registerBaseMetric(machineNetRate);
12907
12908 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12909 guestLoadUser, guestLoadKernel, guestLoadIdle);
12910 aCollector->registerBaseMetric(guestCpuLoad);
12911
12912 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12913 guestMemTotal, guestMemFree,
12914 guestMemBalloon, guestMemShared,
12915 guestMemCache, guestPagedTotal);
12916 aCollector->registerBaseMetric(guestCpuMem);
12917
12918 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12919 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12920 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12921 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12922
12923 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12924 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12925 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12926 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12927
12928 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12929 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12930 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12931 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12932
12933 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12934 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12935 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12936 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12937
12938 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12939 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12940 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12941 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12942
12943 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12944 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12945 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12946 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12947
12948 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12949 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12950 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12951 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12952
12953 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12954 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12955 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12956 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12957
12958 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12959 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12960 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12961 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12962
12963 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12964 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12965 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12966 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12967
12968 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12969 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12970 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12971 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12972}
12973
12974void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12975{
12976 AssertReturnVoid(isWriteLockOnCurrentThread());
12977
12978 if (aCollector)
12979 {
12980 aCollector->unregisterMetricsFor(aMachine);
12981 aCollector->unregisterBaseMetricsFor(aMachine);
12982 }
12983}
12984
12985#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12986
12987
12988////////////////////////////////////////////////////////////////////////////////
12989
12990DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12991
12992HRESULT SessionMachine::FinalConstruct()
12993{
12994 LogFlowThisFunc(("\n"));
12995
12996 mClientToken = NULL;
12997
12998 return BaseFinalConstruct();
12999}
13000
13001void SessionMachine::FinalRelease()
13002{
13003 LogFlowThisFunc(("\n"));
13004
13005 Assert(!mClientToken);
13006 /* paranoia, should not hang around any more */
13007 if (mClientToken)
13008 {
13009 delete mClientToken;
13010 mClientToken = NULL;
13011 }
13012
13013 uninit(Uninit::Unexpected);
13014
13015 BaseFinalRelease();
13016}
13017
13018/**
13019 * @note Must be called only by Machine::LockMachine() from its own write lock.
13020 */
13021HRESULT SessionMachine::init(Machine *aMachine)
13022{
13023 LogFlowThisFuncEnter();
13024 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13025
13026 AssertReturn(aMachine, E_INVALIDARG);
13027
13028 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13029
13030 /* Enclose the state transition NotReady->InInit->Ready */
13031 AutoInitSpan autoInitSpan(this);
13032 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13033
13034 HRESULT hrc = S_OK;
13035
13036 RT_ZERO(mAuthLibCtx);
13037
13038 /* create the machine client token */
13039 try
13040 {
13041 mClientToken = new ClientToken(aMachine, this);
13042 if (!mClientToken->isReady())
13043 {
13044 delete mClientToken;
13045 mClientToken = NULL;
13046 hrc = E_FAIL;
13047 }
13048 }
13049 catch (std::bad_alloc &)
13050 {
13051 hrc = E_OUTOFMEMORY;
13052 }
13053 if (FAILED(hrc))
13054 return hrc;
13055
13056 /* memorize the peer Machine */
13057 unconst(mPeer) = aMachine;
13058 /* share the parent pointer */
13059 unconst(mParent) = aMachine->mParent;
13060
13061 /* take the pointers to data to share */
13062 mData.share(aMachine->mData);
13063 mSSData.share(aMachine->mSSData);
13064
13065 mUserData.share(aMachine->mUserData);
13066 mHWData.share(aMachine->mHWData);
13067 mMediumAttachments.share(aMachine->mMediumAttachments);
13068
13069 mStorageControllers.allocate();
13070 for (StorageControllerList::const_iterator
13071 it = aMachine->mStorageControllers->begin();
13072 it != aMachine->mStorageControllers->end();
13073 ++it)
13074 {
13075 ComObjPtr<StorageController> ctl;
13076 ctl.createObject();
13077 ctl->init(this, *it);
13078 mStorageControllers->push_back(ctl);
13079 }
13080
13081 mUSBControllers.allocate();
13082 for (USBControllerList::const_iterator
13083 it = aMachine->mUSBControllers->begin();
13084 it != aMachine->mUSBControllers->end();
13085 ++it)
13086 {
13087 ComObjPtr<USBController> ctl;
13088 ctl.createObject();
13089 ctl->init(this, *it);
13090 mUSBControllers->push_back(ctl);
13091 }
13092
13093 unconst(mBIOSSettings).createObject();
13094 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13095
13096 unconst(mRecordingSettings).createObject();
13097 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13098
13099 unconst(mTrustedPlatformModule).createObject();
13100 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13101
13102 unconst(mNvramStore).createObject();
13103 mNvramStore->init(this, aMachine->mNvramStore);
13104
13105 /* create another GraphicsAdapter object that will be mutable */
13106 unconst(mGraphicsAdapter).createObject();
13107 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13108 /* create another VRDEServer object that will be mutable */
13109 unconst(mVRDEServer).createObject();
13110 mVRDEServer->init(this, aMachine->mVRDEServer);
13111 /* create another audio settings object that will be mutable */
13112 unconst(mAudioSettings).createObject();
13113 mAudioSettings->init(this, aMachine->mAudioSettings);
13114 /* create a list of serial ports that will be mutable */
13115 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13116 {
13117 unconst(mSerialPorts[slot]).createObject();
13118 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13119 }
13120 /* create a list of parallel ports that will be mutable */
13121 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13122 {
13123 unconst(mParallelPorts[slot]).createObject();
13124 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13125 }
13126
13127 /* create another USB device filters object that will be mutable */
13128 unconst(mUSBDeviceFilters).createObject();
13129 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13130
13131 /* create a list of network adapters that will be mutable */
13132 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13133 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13134 {
13135 unconst(mNetworkAdapters[slot]).createObject();
13136 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13137 }
13138
13139 /* create another bandwidth control object that will be mutable */
13140 unconst(mBandwidthControl).createObject();
13141 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13142
13143 unconst(mGuestDebugControl).createObject();
13144 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13145
13146 /* default is to delete saved state on Saved -> PoweredOff transition */
13147 mRemoveSavedState = true;
13148
13149 /* Confirm a successful initialization when it's the case */
13150 autoInitSpan.setSucceeded();
13151
13152 miNATNetworksStarted = 0;
13153
13154 LogFlowThisFuncLeave();
13155 return hrc;
13156}
13157
13158/**
13159 * Uninitializes this session object. If the reason is other than
13160 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13161 * or the client watcher code.
13162 *
13163 * @param aReason uninitialization reason
13164 *
13165 * @note Locks mParent + this object for writing.
13166 */
13167void SessionMachine::uninit(Uninit::Reason aReason)
13168{
13169 LogFlowThisFuncEnter();
13170 LogFlowThisFunc(("reason=%d\n", aReason));
13171
13172 /*
13173 * Strongly reference ourselves to prevent this object deletion after
13174 * mData->mSession.mMachine.setNull() below (which can release the last
13175 * reference and call the destructor). Important: this must be done before
13176 * accessing any members (and before AutoUninitSpan that does it as well).
13177 * This self reference will be released as the very last step on return.
13178 */
13179 ComObjPtr<SessionMachine> selfRef;
13180 if (aReason != Uninit::Unexpected)
13181 selfRef = this;
13182
13183 /* Enclose the state transition Ready->InUninit->NotReady */
13184 AutoUninitSpan autoUninitSpan(this);
13185 if (autoUninitSpan.uninitDone())
13186 {
13187 LogFlowThisFunc(("Already uninitialized\n"));
13188 LogFlowThisFuncLeave();
13189 return;
13190 }
13191
13192 if (autoUninitSpan.initFailed())
13193 {
13194 /* We've been called by init() because it's failed. It's not really
13195 * necessary (nor it's safe) to perform the regular uninit sequence
13196 * below, the following is enough.
13197 */
13198 LogFlowThisFunc(("Initialization failed.\n"));
13199 /* destroy the machine client token */
13200 if (mClientToken)
13201 {
13202 delete mClientToken;
13203 mClientToken = NULL;
13204 }
13205 uninitDataAndChildObjects();
13206 mData.free();
13207 unconst(mParent) = NULL;
13208 unconst(mPeer) = NULL;
13209 LogFlowThisFuncLeave();
13210 return;
13211 }
13212
13213 MachineState_T lastState;
13214 {
13215 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13216 lastState = mData->mMachineState;
13217 }
13218 NOREF(lastState);
13219
13220#ifdef VBOX_WITH_USB
13221 // release all captured USB devices, but do this before requesting the locks below
13222 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13223 {
13224 /* Console::captureUSBDevices() is called in the VM process only after
13225 * setting the machine state to Starting or Restoring.
13226 * Console::detachAllUSBDevices() will be called upon successful
13227 * termination. So, we need to release USB devices only if there was
13228 * an abnormal termination of a running VM.
13229 *
13230 * This is identical to SessionMachine::DetachAllUSBDevices except
13231 * for the aAbnormal argument. */
13232 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13233 AssertComRC(hrc);
13234 NOREF(hrc);
13235
13236 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13237 if (service)
13238 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13239 }
13240#endif /* VBOX_WITH_USB */
13241
13242 // we need to lock this object in uninit() because the lock is shared
13243 // with mPeer (as well as data we modify below). mParent lock is needed
13244 // by several calls to it.
13245 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13246
13247#ifdef VBOX_WITH_RESOURCE_USAGE_API
13248 /*
13249 * It is safe to call Machine::i_unregisterMetrics() here because
13250 * PerformanceCollector::samplerCallback no longer accesses guest methods
13251 * holding the lock.
13252 */
13253 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13254 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13255 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13256 if (mCollectorGuest)
13257 {
13258 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13259 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13260 mCollectorGuest = NULL;
13261 }
13262#endif
13263
13264 if (aReason == Uninit::Abnormal)
13265 {
13266 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13267
13268 /*
13269 * Move the VM to the 'Aborted' machine state unless we are restoring a
13270 * VM that was in the 'Saved' machine state. In that case, if the VM
13271 * fails before reaching either the 'Restoring' machine state or the
13272 * 'Running' machine state then we set the machine state to
13273 * 'AbortedSaved' in order to preserve the saved state file so that the
13274 * VM can be restored in the future.
13275 */
13276 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13277 i_setMachineState(MachineState_AbortedSaved);
13278 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13279 i_setMachineState(MachineState_Aborted);
13280 }
13281
13282 // any machine settings modified?
13283 if (mData->flModifications)
13284 {
13285 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13286 i_rollback(false /* aNotify */);
13287 }
13288
13289 mData->mSession.mPID = NIL_RTPROCESS;
13290
13291 if (aReason == Uninit::Unexpected)
13292 {
13293 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13294 * client watcher thread to update the set of machines that have open
13295 * sessions. */
13296 mParent->i_updateClientWatcher();
13297 }
13298
13299 /* uninitialize all remote controls */
13300 if (mData->mSession.mRemoteControls.size())
13301 {
13302 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13303 mData->mSession.mRemoteControls.size()));
13304
13305 /* Always restart a the beginning, since the iterator is invalidated
13306 * by using erase(). */
13307 for (Data::Session::RemoteControlList::iterator
13308 it = mData->mSession.mRemoteControls.begin();
13309 it != mData->mSession.mRemoteControls.end();
13310 it = mData->mSession.mRemoteControls.begin())
13311 {
13312 ComPtr<IInternalSessionControl> pControl = *it;
13313 mData->mSession.mRemoteControls.erase(it);
13314 multilock.release();
13315 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13316 HRESULT hrc = pControl->Uninitialize();
13317 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
13318 if (FAILED(hrc))
13319 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13320 multilock.acquire();
13321 }
13322 mData->mSession.mRemoteControls.clear();
13323 }
13324
13325 /* Remove all references to the NAT network service. The service will stop
13326 * if all references (also from other VMs) are removed. */
13327 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13328 {
13329 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13330 {
13331 BOOL enabled;
13332 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13333 if ( FAILED(hrc)
13334 || !enabled)
13335 continue;
13336
13337 NetworkAttachmentType_T type;
13338 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13339 if ( SUCCEEDED(hrc)
13340 && type == NetworkAttachmentType_NATNetwork)
13341 {
13342 Bstr name;
13343 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13344 if (SUCCEEDED(hrc))
13345 {
13346 multilock.release();
13347 Utf8Str strName(name);
13348 LogRel(("VM '%s' stops using NAT network '%s'\n",
13349 mUserData->s.strName.c_str(), strName.c_str()));
13350 mParent->i_natNetworkRefDec(strName);
13351 multilock.acquire();
13352 }
13353 }
13354 }
13355 }
13356
13357 /*
13358 * An expected uninitialization can come only from #i_checkForDeath().
13359 * Otherwise it means that something's gone really wrong (for example,
13360 * the Session implementation has released the VirtualBox reference
13361 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13362 * etc). However, it's also possible, that the client releases the IPC
13363 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13364 * but the VirtualBox release event comes first to the server process.
13365 * This case is practically possible, so we should not assert on an
13366 * unexpected uninit, just log a warning.
13367 */
13368
13369 if (aReason == Uninit::Unexpected)
13370 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13371
13372 if (aReason != Uninit::Normal)
13373 {
13374 mData->mSession.mDirectControl.setNull();
13375 }
13376 else
13377 {
13378 /* this must be null here (see #OnSessionEnd()) */
13379 Assert(mData->mSession.mDirectControl.isNull());
13380 Assert(mData->mSession.mState == SessionState_Unlocking);
13381 Assert(!mData->mSession.mProgress.isNull());
13382 }
13383 if (mData->mSession.mProgress)
13384 {
13385 if (aReason == Uninit::Normal)
13386 mData->mSession.mProgress->i_notifyComplete(S_OK);
13387 else
13388 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13389 COM_IIDOF(ISession),
13390 getComponentName(),
13391 tr("The VM session was aborted"));
13392 mData->mSession.mProgress.setNull();
13393 }
13394
13395 if (mConsoleTaskData.mProgress)
13396 {
13397 Assert(aReason == Uninit::Abnormal);
13398 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13399 COM_IIDOF(ISession),
13400 getComponentName(),
13401 tr("The VM session was aborted"));
13402 mConsoleTaskData.mProgress.setNull();
13403 }
13404
13405 /* remove the association between the peer machine and this session machine */
13406 Assert( (SessionMachine*)mData->mSession.mMachine == this
13407 || aReason == Uninit::Unexpected);
13408
13409 /* reset the rest of session data */
13410 mData->mSession.mLockType = LockType_Null;
13411 mData->mSession.mMachine.setNull();
13412 mData->mSession.mState = SessionState_Unlocked;
13413 mData->mSession.mName.setNull();
13414
13415 /* destroy the machine client token before leaving the exclusive lock */
13416 if (mClientToken)
13417 {
13418 delete mClientToken;
13419 mClientToken = NULL;
13420 }
13421
13422 /* fire an event */
13423 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13424
13425 uninitDataAndChildObjects();
13426
13427 /* free the essential data structure last */
13428 mData.free();
13429
13430 /* release the exclusive lock before setting the below two to NULL */
13431 multilock.release();
13432
13433 unconst(mParent) = NULL;
13434 unconst(mPeer) = NULL;
13435
13436 AuthLibUnload(&mAuthLibCtx);
13437
13438 LogFlowThisFuncLeave();
13439}
13440
13441// util::Lockable interface
13442////////////////////////////////////////////////////////////////////////////////
13443
13444/**
13445 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13446 * with the primary Machine instance (mPeer).
13447 */
13448RWLockHandle *SessionMachine::lockHandle() const
13449{
13450 AssertReturn(mPeer != NULL, NULL);
13451 return mPeer->lockHandle();
13452}
13453
13454// IInternalMachineControl methods
13455////////////////////////////////////////////////////////////////////////////////
13456
13457/**
13458 * Passes collected guest statistics to performance collector object
13459 */
13460HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13461 ULONG aCpuKernel, ULONG aCpuIdle,
13462 ULONG aMemTotal, ULONG aMemFree,
13463 ULONG aMemBalloon, ULONG aMemShared,
13464 ULONG aMemCache, ULONG aPageTotal,
13465 ULONG aAllocVMM, ULONG aFreeVMM,
13466 ULONG aBalloonedVMM, ULONG aSharedVMM,
13467 ULONG aVmNetRx, ULONG aVmNetTx)
13468{
13469#ifdef VBOX_WITH_RESOURCE_USAGE_API
13470 if (mCollectorGuest)
13471 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13472 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13473 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13474 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13475
13476 return S_OK;
13477#else
13478 NOREF(aValidStats);
13479 NOREF(aCpuUser);
13480 NOREF(aCpuKernel);
13481 NOREF(aCpuIdle);
13482 NOREF(aMemTotal);
13483 NOREF(aMemFree);
13484 NOREF(aMemBalloon);
13485 NOREF(aMemShared);
13486 NOREF(aMemCache);
13487 NOREF(aPageTotal);
13488 NOREF(aAllocVMM);
13489 NOREF(aFreeVMM);
13490 NOREF(aBalloonedVMM);
13491 NOREF(aSharedVMM);
13492 NOREF(aVmNetRx);
13493 NOREF(aVmNetTx);
13494 return E_NOTIMPL;
13495#endif
13496}
13497
13498////////////////////////////////////////////////////////////////////////////////
13499//
13500// SessionMachine task records
13501//
13502////////////////////////////////////////////////////////////////////////////////
13503
13504/**
13505 * Task record for saving the machine state.
13506 */
13507class SessionMachine::SaveStateTask
13508 : public Machine::Task
13509{
13510public:
13511 SaveStateTask(SessionMachine *m,
13512 Progress *p,
13513 const Utf8Str &t,
13514 Reason_T enmReason,
13515 const Utf8Str &strStateFilePath)
13516 : Task(m, p, t),
13517 m_enmReason(enmReason),
13518 m_strStateFilePath(strStateFilePath)
13519 {}
13520
13521private:
13522 void handler()
13523 {
13524 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13525 }
13526
13527 Reason_T m_enmReason;
13528 Utf8Str m_strStateFilePath;
13529
13530 friend class SessionMachine;
13531};
13532
13533/**
13534 * Task thread implementation for SessionMachine::SaveState(), called from
13535 * SessionMachine::taskHandler().
13536 *
13537 * @note Locks this object for writing.
13538 *
13539 * @param task
13540 * @return
13541 */
13542void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13543{
13544 LogFlowThisFuncEnter();
13545
13546 AutoCaller autoCaller(this);
13547 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13548 if (FAILED(autoCaller.hrc()))
13549 {
13550 /* we might have been uninitialized because the session was accidentally
13551 * closed by the client, so don't assert */
13552 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
13553 task.m_pProgress->i_notifyComplete(hrc);
13554 LogFlowThisFuncLeave();
13555 return;
13556 }
13557
13558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13559
13560 HRESULT hrc = S_OK;
13561
13562 try
13563 {
13564 ComPtr<IInternalSessionControl> directControl;
13565 if (mData->mSession.mLockType == LockType_VM)
13566 directControl = mData->mSession.mDirectControl;
13567 if (directControl.isNull())
13568 throw setError(VBOX_E_INVALID_VM_STATE,
13569 tr("Trying to save state without a running VM"));
13570 alock.release();
13571 BOOL fSuspendedBySave;
13572 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13573 Assert(!fSuspendedBySave);
13574 alock.acquire();
13575
13576 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
13577 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
13578 throw E_FAIL);
13579
13580 if (SUCCEEDED(hrc))
13581 {
13582 mSSData->strStateFilePath = task.m_strStateFilePath;
13583
13584 /* save all VM settings */
13585 hrc = i_saveSettings(NULL, alock);
13586 // no need to check whether VirtualBox.xml needs saving also since
13587 // we can't have a name change pending at this point
13588 }
13589 else
13590 {
13591 // On failure, set the state to the state we had at the beginning.
13592 i_setMachineState(task.m_machineStateBackup);
13593 i_updateMachineStateOnClient();
13594
13595 // Delete the saved state file (might have been already created).
13596 // No need to check whether this is shared with a snapshot here
13597 // because we certainly created a fresh saved state file here.
13598 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13599 }
13600 }
13601 catch (HRESULT hrcXcpt)
13602 {
13603 hrc = hrcXcpt;
13604 }
13605
13606 task.m_pProgress->i_notifyComplete(hrc);
13607
13608 LogFlowThisFuncLeave();
13609}
13610
13611/**
13612 * @note Locks this object for writing.
13613 */
13614HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13615{
13616 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13617}
13618
13619HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13620{
13621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13622
13623 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13624 if (FAILED(hrc)) return hrc;
13625
13626 if ( mData->mMachineState != MachineState_Running
13627 && mData->mMachineState != MachineState_Paused
13628 )
13629 return setError(VBOX_E_INVALID_VM_STATE,
13630 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13631 Global::stringifyMachineState(mData->mMachineState));
13632
13633 ComObjPtr<Progress> pProgress;
13634 pProgress.createObject();
13635 hrc = pProgress->init(i_getVirtualBox(),
13636 static_cast<IMachine *>(this) /* aInitiator */,
13637 tr("Saving the execution state of the virtual machine"),
13638 FALSE /* aCancelable */);
13639 if (FAILED(hrc))
13640 return hrc;
13641
13642 Utf8Str strStateFilePath;
13643 i_composeSavedStateFilename(strStateFilePath);
13644
13645 /* create and start the task on a separate thread (note that it will not
13646 * start working until we release alock) */
13647 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13648 hrc = pTask->createThread();
13649 if (FAILED(hrc))
13650 return hrc;
13651
13652 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13653 i_setMachineState(MachineState_Saving);
13654 i_updateMachineStateOnClient();
13655
13656 pProgress.queryInterfaceTo(aProgress.asOutParam());
13657
13658 return S_OK;
13659}
13660
13661/**
13662 * @note Locks this object for writing.
13663 */
13664HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13665{
13666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13667
13668 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13669 if (FAILED(hrc)) return hrc;
13670
13671 if ( mData->mMachineState != MachineState_PoweredOff
13672 && mData->mMachineState != MachineState_Teleported
13673 && mData->mMachineState != MachineState_Aborted
13674 )
13675 return setError(VBOX_E_INVALID_VM_STATE,
13676 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13677 Global::stringifyMachineState(mData->mMachineState));
13678
13679 com::Utf8Str stateFilePathFull;
13680 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13681 if (RT_FAILURE(vrc))
13682 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13683 tr("Invalid saved state file path '%s' (%Rrc)"),
13684 aSavedStateFile.c_str(),
13685 vrc);
13686
13687 mSSData->strStateFilePath = stateFilePathFull;
13688
13689 /* The below i_setMachineState() will detect the state transition and will
13690 * update the settings file */
13691
13692 return i_setMachineState(MachineState_Saved);
13693}
13694
13695/**
13696 * @note Locks this object for writing.
13697 */
13698HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13699{
13700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13701
13702 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13703 if (FAILED(hrc)) return hrc;
13704
13705 if ( mData->mMachineState != MachineState_Saved
13706 && mData->mMachineState != MachineState_AbortedSaved)
13707 return setError(VBOX_E_INVALID_VM_STATE,
13708 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13709 Global::stringifyMachineState(mData->mMachineState));
13710
13711 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13712
13713 /*
13714 * Saved -> PoweredOff transition will be detected in the SessionMachine
13715 * and properly handled.
13716 */
13717 hrc = i_setMachineState(MachineState_PoweredOff);
13718 return hrc;
13719}
13720
13721
13722/**
13723 * @note Locks the same as #i_setMachineState() does.
13724 */
13725HRESULT SessionMachine::updateState(MachineState_T aState)
13726{
13727 return i_setMachineState(aState);
13728}
13729
13730/**
13731 * @note Locks this object for writing.
13732 */
13733HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13734{
13735 IProgress *pProgress(aProgress);
13736
13737 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13738
13739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13740
13741 if (mData->mSession.mState != SessionState_Locked)
13742 return VBOX_E_INVALID_OBJECT_STATE;
13743
13744 if (!mData->mSession.mProgress.isNull())
13745 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13746
13747 /* If we didn't reference the NAT network service yet, add a reference to
13748 * force a start */
13749 if (miNATNetworksStarted < 1)
13750 {
13751 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13752 {
13753 BOOL enabled;
13754 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13755 if ( FAILED(hrc)
13756 || !enabled)
13757 continue;
13758
13759 NetworkAttachmentType_T type;
13760 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13761 if ( SUCCEEDED(hrc)
13762 && type == NetworkAttachmentType_NATNetwork)
13763 {
13764 Bstr name;
13765 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13766 if (SUCCEEDED(hrc))
13767 {
13768 Utf8Str strName(name);
13769 LogRel(("VM '%s' starts using NAT network '%s'\n",
13770 mUserData->s.strName.c_str(), strName.c_str()));
13771 mPeer->lockHandle()->unlockWrite();
13772 mParent->i_natNetworkRefInc(strName);
13773#ifdef RT_LOCK_STRICT
13774 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13775#else
13776 mPeer->lockHandle()->lockWrite();
13777#endif
13778 }
13779 }
13780 }
13781 miNATNetworksStarted++;
13782 }
13783
13784 LogFlowThisFunc(("returns S_OK.\n"));
13785 return S_OK;
13786}
13787
13788/**
13789 * @note Locks this object for writing.
13790 */
13791HRESULT SessionMachine::endPowerUp(LONG aResult)
13792{
13793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13794
13795 if (mData->mSession.mState != SessionState_Locked)
13796 return VBOX_E_INVALID_OBJECT_STATE;
13797
13798 /* Finalize the LaunchVMProcess progress object. */
13799 if (mData->mSession.mProgress)
13800 {
13801 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13802 mData->mSession.mProgress.setNull();
13803 }
13804
13805 if (SUCCEEDED((HRESULT)aResult))
13806 {
13807#ifdef VBOX_WITH_RESOURCE_USAGE_API
13808 /* The VM has been powered up successfully, so it makes sense
13809 * now to offer the performance metrics for a running machine
13810 * object. Doing it earlier wouldn't be safe. */
13811 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13812 mData->mSession.mPID);
13813#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13814 }
13815
13816 return S_OK;
13817}
13818
13819/**
13820 * @note Locks this object for writing.
13821 */
13822HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13823{
13824 LogFlowThisFuncEnter();
13825
13826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13827
13828 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13829 E_FAIL);
13830
13831 /* create a progress object to track operation completion */
13832 ComObjPtr<Progress> pProgress;
13833 pProgress.createObject();
13834 pProgress->init(i_getVirtualBox(),
13835 static_cast<IMachine *>(this) /* aInitiator */,
13836 tr("Stopping the virtual machine"),
13837 FALSE /* aCancelable */);
13838
13839 /* fill in the console task data */
13840 mConsoleTaskData.mLastState = mData->mMachineState;
13841 mConsoleTaskData.mProgress = pProgress;
13842
13843 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13844 i_setMachineState(MachineState_Stopping);
13845
13846 pProgress.queryInterfaceTo(aProgress.asOutParam());
13847
13848 return S_OK;
13849}
13850
13851/**
13852 * @note Locks this object for writing.
13853 */
13854HRESULT SessionMachine::endPoweringDown(LONG aResult,
13855 const com::Utf8Str &aErrMsg)
13856{
13857 HRESULT const hrcResult = (HRESULT)aResult;
13858 LogFlowThisFuncEnter();
13859
13860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13861
13862 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13863 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13864 && mConsoleTaskData.mLastState != MachineState_Null,
13865 E_FAIL);
13866
13867 /*
13868 * On failure, set the state to the state we had when BeginPoweringDown()
13869 * was called (this is expected by Console::PowerDown() and the associated
13870 * task). On success the VM process already changed the state to
13871 * MachineState_PoweredOff, so no need to do anything.
13872 */
13873 if (FAILED(hrcResult))
13874 i_setMachineState(mConsoleTaskData.mLastState);
13875
13876 /* notify the progress object about operation completion */
13877 Assert(mConsoleTaskData.mProgress);
13878 if (SUCCEEDED(hrcResult))
13879 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13880 else
13881 {
13882 if (aErrMsg.length())
13883 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13884 COM_IIDOF(ISession),
13885 getComponentName(),
13886 aErrMsg.c_str());
13887 else
13888 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13889 }
13890
13891 /* clear out the temporary saved state data */
13892 mConsoleTaskData.mLastState = MachineState_Null;
13893 mConsoleTaskData.mProgress.setNull();
13894
13895 LogFlowThisFuncLeave();
13896 return S_OK;
13897}
13898
13899
13900/**
13901 * Goes through the USB filters of the given machine to see if the given
13902 * device matches any filter or not.
13903 *
13904 * @note Locks the same as USBController::hasMatchingFilter() does.
13905 */
13906HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13907 BOOL *aMatched,
13908 ULONG *aMaskedInterfaces)
13909{
13910 LogFlowThisFunc(("\n"));
13911
13912#ifdef VBOX_WITH_USB
13913 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13914#else
13915 NOREF(aDevice);
13916 NOREF(aMaskedInterfaces);
13917 *aMatched = FALSE;
13918#endif
13919
13920 return S_OK;
13921}
13922
13923/**
13924 * @note Locks the same as Host::captureUSBDevice() does.
13925 */
13926HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13927{
13928 LogFlowThisFunc(("\n"));
13929
13930#ifdef VBOX_WITH_USB
13931 /* if captureDeviceForVM() fails, it must have set extended error info */
13932 clearError();
13933 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13934 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13935 return hrc;
13936
13937 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13938 AssertReturn(service, E_FAIL);
13939 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13940#else
13941 RT_NOREF(aId, aCaptureFilename);
13942 return E_NOTIMPL;
13943#endif
13944}
13945
13946/**
13947 * @note Locks the same as Host::detachUSBDevice() does.
13948 */
13949HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13950 BOOL aDone)
13951{
13952 LogFlowThisFunc(("\n"));
13953
13954#ifdef VBOX_WITH_USB
13955 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13956 AssertReturn(service, E_FAIL);
13957 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13958#else
13959 NOREF(aId);
13960 NOREF(aDone);
13961 return E_NOTIMPL;
13962#endif
13963}
13964
13965/**
13966 * Inserts all machine filters to the USB proxy service and then calls
13967 * Host::autoCaptureUSBDevices().
13968 *
13969 * Called by Console from the VM process upon VM startup.
13970 *
13971 * @note Locks what called methods lock.
13972 */
13973HRESULT SessionMachine::autoCaptureUSBDevices()
13974{
13975 LogFlowThisFunc(("\n"));
13976
13977#ifdef VBOX_WITH_USB
13978 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13979 AssertComRC(hrc);
13980 NOREF(hrc);
13981
13982 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13983 AssertReturn(service, E_FAIL);
13984 return service->autoCaptureDevicesForVM(this);
13985#else
13986 return S_OK;
13987#endif
13988}
13989
13990/**
13991 * Removes all machine filters from the USB proxy service and then calls
13992 * Host::detachAllUSBDevices().
13993 *
13994 * Called by Console from the VM process upon normal VM termination or by
13995 * SessionMachine::uninit() upon abnormal VM termination (from under the
13996 * Machine/SessionMachine lock).
13997 *
13998 * @note Locks what called methods lock.
13999 */
14000HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
14001{
14002 LogFlowThisFunc(("\n"));
14003
14004#ifdef VBOX_WITH_USB
14005 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14006 AssertComRC(hrc);
14007 NOREF(hrc);
14008
14009 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14010 AssertReturn(service, E_FAIL);
14011 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14012#else
14013 NOREF(aDone);
14014 return S_OK;
14015#endif
14016}
14017
14018/**
14019 * @note Locks this object for writing.
14020 */
14021HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14022 ComPtr<IProgress> &aProgress)
14023{
14024 LogFlowThisFuncEnter();
14025
14026 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14027 /*
14028 * We don't assert below because it might happen that a non-direct session
14029 * informs us it is closed right after we've been uninitialized -- it's ok.
14030 */
14031
14032 /* get IInternalSessionControl interface */
14033 ComPtr<IInternalSessionControl> control(aSession);
14034
14035 ComAssertRet(!control.isNull(), E_INVALIDARG);
14036
14037 /* Creating a Progress object requires the VirtualBox lock, and
14038 * thus locking it here is required by the lock order rules. */
14039 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14040
14041 if (control == mData->mSession.mDirectControl)
14042 {
14043 /* The direct session is being normally closed by the client process
14044 * ----------------------------------------------------------------- */
14045
14046 /* go to the closing state (essential for all open*Session() calls and
14047 * for #i_checkForDeath()) */
14048 Assert(mData->mSession.mState == SessionState_Locked);
14049 mData->mSession.mState = SessionState_Unlocking;
14050
14051 /* set direct control to NULL to release the remote instance */
14052 mData->mSession.mDirectControl.setNull();
14053 LogFlowThisFunc(("Direct control is set to NULL\n"));
14054
14055 if (mData->mSession.mProgress)
14056 {
14057 /* finalize the progress, someone might wait if a frontend
14058 * closes the session before powering on the VM. */
14059 mData->mSession.mProgress->notifyComplete(E_FAIL,
14060 COM_IIDOF(ISession),
14061 getComponentName(),
14062 tr("The VM session was closed before any attempt to power it on"));
14063 mData->mSession.mProgress.setNull();
14064 }
14065
14066 /* Create the progress object the client will use to wait until
14067 * #i_checkForDeath() is called to uninitialize this session object after
14068 * it releases the IPC semaphore.
14069 * Note! Because we're "reusing" mProgress here, this must be a proxy
14070 * object just like for LaunchVMProcess. */
14071 Assert(mData->mSession.mProgress.isNull());
14072 ComObjPtr<ProgressProxy> progress;
14073 progress.createObject();
14074 ComPtr<IUnknown> pPeer(mPeer);
14075 progress->init(mParent, pPeer,
14076 Bstr(tr("Closing session")).raw(),
14077 FALSE /* aCancelable */);
14078 progress.queryInterfaceTo(aProgress.asOutParam());
14079 mData->mSession.mProgress = progress;
14080 }
14081 else
14082 {
14083 /* the remote session is being normally closed */
14084 bool found = false;
14085 for (Data::Session::RemoteControlList::iterator
14086 it = mData->mSession.mRemoteControls.begin();
14087 it != mData->mSession.mRemoteControls.end();
14088 ++it)
14089 {
14090 if (control == *it)
14091 {
14092 found = true;
14093 // This MUST be erase(it), not remove(*it) as the latter
14094 // triggers a very nasty use after free due to the place where
14095 // the value "lives".
14096 mData->mSession.mRemoteControls.erase(it);
14097 break;
14098 }
14099 }
14100 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14101 E_INVALIDARG);
14102 }
14103
14104 /* signal the client watcher thread, because the client is going away */
14105 mParent->i_updateClientWatcher();
14106
14107 LogFlowThisFuncLeave();
14108 return S_OK;
14109}
14110
14111HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14112 std::vector<com::Utf8Str> &aValues,
14113 std::vector<LONG64> &aTimestamps,
14114 std::vector<com::Utf8Str> &aFlags)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118#ifdef VBOX_WITH_GUEST_PROPS
14119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14120
14121 size_t cEntries = mHWData->mGuestProperties.size();
14122 aNames.resize(cEntries);
14123 aValues.resize(cEntries);
14124 aTimestamps.resize(cEntries);
14125 aFlags.resize(cEntries);
14126
14127 size_t i = 0;
14128 for (HWData::GuestPropertyMap::const_iterator
14129 it = mHWData->mGuestProperties.begin();
14130 it != mHWData->mGuestProperties.end();
14131 ++it, ++i)
14132 {
14133 aNames[i] = it->first;
14134 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14135 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14136
14137 aValues[i] = it->second.strValue;
14138 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14139 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14140
14141 aTimestamps[i] = it->second.mTimestamp;
14142
14143 /* If it is NULL, keep it NULL. */
14144 if (it->second.mFlags)
14145 {
14146 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14147 GuestPropWriteFlags(it->second.mFlags, szFlags);
14148 aFlags[i] = szFlags;
14149 }
14150 else
14151 aFlags[i] = "";
14152 }
14153 return S_OK;
14154#else
14155 ReturnComNotImplemented();
14156#endif
14157}
14158
14159HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14160 const com::Utf8Str &aValue,
14161 LONG64 aTimestamp,
14162 const com::Utf8Str &aFlags,
14163 BOOL fWasDeleted)
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167#ifdef VBOX_WITH_GUEST_PROPS
14168 try
14169 {
14170 /*
14171 * Convert input up front.
14172 */
14173 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14174 if (aFlags.length())
14175 {
14176 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14177 AssertRCReturn(vrc, E_INVALIDARG);
14178 }
14179
14180 /*
14181 * Now grab the object lock, validate the state and do the update.
14182 */
14183
14184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14185
14186 if (!Global::IsOnline(mData->mMachineState))
14187 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14188
14189 i_setModified(IsModified_MachineData);
14190 mHWData.backup();
14191
14192 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14193 if (it != mHWData->mGuestProperties.end())
14194 {
14195 if (!fWasDeleted)
14196 {
14197 it->second.strValue = aValue;
14198 it->second.mTimestamp = aTimestamp;
14199 it->second.mFlags = fFlags;
14200 }
14201 else
14202 mHWData->mGuestProperties.erase(it);
14203
14204 mData->mGuestPropertiesModified = TRUE;
14205 }
14206 else if (!fWasDeleted)
14207 {
14208 HWData::GuestProperty prop;
14209 prop.strValue = aValue;
14210 prop.mTimestamp = aTimestamp;
14211 prop.mFlags = fFlags;
14212
14213 mHWData->mGuestProperties[aName] = prop;
14214 mData->mGuestPropertiesModified = TRUE;
14215 }
14216
14217 alock.release();
14218
14219 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14220 }
14221 catch (...)
14222 {
14223 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14224 }
14225 return S_OK;
14226#else
14227 ReturnComNotImplemented();
14228#endif
14229}
14230
14231
14232HRESULT SessionMachine::lockMedia()
14233{
14234 AutoMultiWriteLock2 alock(this->lockHandle(),
14235 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14236
14237 AssertReturn( mData->mMachineState == MachineState_Starting
14238 || mData->mMachineState == MachineState_Restoring
14239 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14240
14241 clearError();
14242 alock.release();
14243 return i_lockMedia();
14244}
14245
14246HRESULT SessionMachine::unlockMedia()
14247{
14248 HRESULT hrc = i_unlockMedia();
14249 return hrc;
14250}
14251
14252HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14253 ComPtr<IMediumAttachment> &aNewAttachment)
14254{
14255 // request the host lock first, since might be calling Host methods for getting host drives;
14256 // next, protect the media tree all the while we're in here, as well as our member variables
14257 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14258 this->lockHandle(),
14259 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14260
14261 IMediumAttachment *iAttach = aAttachment;
14262 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14263
14264 Utf8Str ctrlName;
14265 LONG lPort;
14266 LONG lDevice;
14267 bool fTempEject;
14268 {
14269 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14270
14271 /* Need to query the details first, as the IMediumAttachment reference
14272 * might be to the original settings, which we are going to change. */
14273 ctrlName = pAttach->i_getControllerName();
14274 lPort = pAttach->i_getPort();
14275 lDevice = pAttach->i_getDevice();
14276 fTempEject = pAttach->i_getTempEject();
14277 }
14278
14279 if (!fTempEject)
14280 {
14281 /* Remember previously mounted medium. The medium before taking the
14282 * backup is not necessarily the same thing. */
14283 ComObjPtr<Medium> oldmedium;
14284 oldmedium = pAttach->i_getMedium();
14285
14286 i_setModified(IsModified_Storage);
14287 mMediumAttachments.backup();
14288
14289 // The backup operation makes the pAttach reference point to the
14290 // old settings. Re-get the correct reference.
14291 pAttach = i_findAttachment(*mMediumAttachments.data(),
14292 ctrlName,
14293 lPort,
14294 lDevice);
14295
14296 {
14297 AutoCaller autoAttachCaller(this);
14298 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
14299
14300 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14301 if (!oldmedium.isNull())
14302 oldmedium->i_removeBackReference(mData->mUuid);
14303
14304 pAttach->i_updateMedium(NULL);
14305 pAttach->i_updateEjected();
14306 }
14307
14308 i_setModified(IsModified_Storage);
14309 }
14310 else
14311 {
14312 {
14313 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14314 pAttach->i_updateEjected();
14315 }
14316 }
14317
14318 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14319
14320 return S_OK;
14321}
14322
14323HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14324 com::Utf8Str &aResult)
14325{
14326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14327
14328 HRESULT hr = S_OK;
14329
14330 if (!mAuthLibCtx.hAuthLibrary)
14331 {
14332 /* Load the external authentication library. */
14333 Bstr authLibrary;
14334 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14335
14336 Utf8Str filename = authLibrary;
14337
14338 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14339 if (RT_FAILURE(vrc))
14340 hr = setErrorBoth(E_FAIL, vrc,
14341 tr("Could not load the external authentication library '%s' (%Rrc)"),
14342 filename.c_str(), vrc);
14343 }
14344
14345 /* The auth library might need the machine lock. */
14346 alock.release();
14347
14348 if (FAILED(hr))
14349 return hr;
14350
14351 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14352 {
14353 enum VRDEAuthParams
14354 {
14355 parmUuid = 1,
14356 parmGuestJudgement,
14357 parmUser,
14358 parmPassword,
14359 parmDomain,
14360 parmClientId
14361 };
14362
14363 AuthResult result = AuthResultAccessDenied;
14364
14365 Guid uuid(aAuthParams[parmUuid]);
14366 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14367 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14368
14369 result = AuthLibAuthenticate(&mAuthLibCtx,
14370 uuid.raw(), guestJudgement,
14371 aAuthParams[parmUser].c_str(),
14372 aAuthParams[parmPassword].c_str(),
14373 aAuthParams[parmDomain].c_str(),
14374 u32ClientId);
14375
14376 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14377 size_t cbPassword = aAuthParams[parmPassword].length();
14378 if (cbPassword)
14379 {
14380 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14381 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14382 }
14383
14384 if (result == AuthResultAccessGranted)
14385 aResult = "granted";
14386 else
14387 aResult = "denied";
14388
14389 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14390 aAuthParams[parmUser].c_str(), aResult.c_str()));
14391 }
14392 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14393 {
14394 enum VRDEAuthDisconnectParams
14395 {
14396 parmUuid = 1,
14397 parmClientId
14398 };
14399
14400 Guid uuid(aAuthParams[parmUuid]);
14401 uint32_t u32ClientId = 0;
14402 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14403 }
14404 else
14405 {
14406 hr = E_INVALIDARG;
14407 }
14408
14409 return hr;
14410}
14411
14412// public methods only for internal purposes
14413/////////////////////////////////////////////////////////////////////////////
14414
14415#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14416/**
14417 * Called from the client watcher thread to check for expected or unexpected
14418 * death of the client process that has a direct session to this machine.
14419 *
14420 * On Win32 and on OS/2, this method is called only when we've got the
14421 * mutex (i.e. the client has either died or terminated normally) so it always
14422 * returns @c true (the client is terminated, the session machine is
14423 * uninitialized).
14424 *
14425 * On other platforms, the method returns @c true if the client process has
14426 * terminated normally or abnormally and the session machine was uninitialized,
14427 * and @c false if the client process is still alive.
14428 *
14429 * @note Locks this object for writing.
14430 */
14431bool SessionMachine::i_checkForDeath()
14432{
14433 Uninit::Reason reason;
14434 bool terminated = false;
14435
14436 /* Enclose autoCaller with a block because calling uninit() from under it
14437 * will deadlock. */
14438 {
14439 AutoCaller autoCaller(this);
14440 if (!autoCaller.isOk())
14441 {
14442 /* return true if not ready, to cause the client watcher to exclude
14443 * the corresponding session from watching */
14444 LogFlowThisFunc(("Already uninitialized!\n"));
14445 return true;
14446 }
14447
14448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14449
14450 /* Determine the reason of death: if the session state is Closing here,
14451 * everything is fine. Otherwise it means that the client did not call
14452 * OnSessionEnd() before it released the IPC semaphore. This may happen
14453 * either because the client process has abnormally terminated, or
14454 * because it simply forgot to call ISession::Close() before exiting. We
14455 * threat the latter also as an abnormal termination (see
14456 * Session::uninit() for details). */
14457 reason = mData->mSession.mState == SessionState_Unlocking ?
14458 Uninit::Normal :
14459 Uninit::Abnormal;
14460
14461 if (mClientToken)
14462 terminated = mClientToken->release();
14463 } /* AutoCaller block */
14464
14465 if (terminated)
14466 uninit(reason);
14467
14468 return terminated;
14469}
14470
14471void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14472{
14473 LogFlowThisFunc(("\n"));
14474
14475 strTokenId.setNull();
14476
14477 AutoCaller autoCaller(this);
14478 AssertComRCReturnVoid(autoCaller.hrc());
14479
14480 Assert(mClientToken);
14481 if (mClientToken)
14482 mClientToken->getId(strTokenId);
14483}
14484#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14485IToken *SessionMachine::i_getToken()
14486{
14487 LogFlowThisFunc(("\n"));
14488
14489 AutoCaller autoCaller(this);
14490 AssertComRCReturn(autoCaller.hrc(), NULL);
14491
14492 Assert(mClientToken);
14493 if (mClientToken)
14494 return mClientToken->getToken();
14495 else
14496 return NULL;
14497}
14498#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14499
14500Machine::ClientToken *SessionMachine::i_getClientToken()
14501{
14502 LogFlowThisFunc(("\n"));
14503
14504 AutoCaller autoCaller(this);
14505 AssertComRCReturn(autoCaller.hrc(), NULL);
14506
14507 return mClientToken;
14508}
14509
14510
14511/**
14512 * @note Locks this object for reading.
14513 */
14514HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14515{
14516 LogFlowThisFunc(("\n"));
14517
14518 AutoCaller autoCaller(this);
14519 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14520
14521 ComPtr<IInternalSessionControl> directControl;
14522 {
14523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14524 if (mData->mSession.mLockType == LockType_VM)
14525 directControl = mData->mSession.mDirectControl;
14526 }
14527
14528 /* ignore notifications sent after #OnSessionEnd() is called */
14529 if (!directControl)
14530 return S_OK;
14531
14532 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14533}
14534
14535/**
14536 * @note Locks this object for reading.
14537 */
14538HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14539 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14540 const Utf8Str &aGuestIp, LONG aGuestPort)
14541{
14542 LogFlowThisFunc(("\n"));
14543
14544 AutoCaller autoCaller(this);
14545 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14546
14547 ComPtr<IInternalSessionControl> directControl;
14548 {
14549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14550 if (mData->mSession.mLockType == LockType_VM)
14551 directControl = mData->mSession.mDirectControl;
14552 }
14553
14554 /* ignore notifications sent after #OnSessionEnd() is called */
14555 if (!directControl)
14556 return S_OK;
14557 /*
14558 * instead acting like callback we ask IVirtualBox deliver corresponding event
14559 */
14560
14561 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14562 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14563 return S_OK;
14564}
14565
14566/**
14567 * @note Locks this object for reading.
14568 */
14569HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14570{
14571 LogFlowThisFunc(("\n"));
14572
14573 AutoCaller autoCaller(this);
14574 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14575
14576 ComPtr<IInternalSessionControl> directControl;
14577 {
14578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14579 if (mData->mSession.mLockType == LockType_VM)
14580 directControl = mData->mSession.mDirectControl;
14581 }
14582
14583 /* ignore notifications sent after #OnSessionEnd() is called */
14584 if (!directControl)
14585 return S_OK;
14586
14587 return directControl->OnAudioAdapterChange(audioAdapter);
14588}
14589
14590/**
14591 * @note Locks this object for reading.
14592 */
14593HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14594{
14595 LogFlowThisFunc(("\n"));
14596
14597 AutoCaller autoCaller(this);
14598 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14599
14600 ComPtr<IInternalSessionControl> directControl;
14601 {
14602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14603 if (mData->mSession.mLockType == LockType_VM)
14604 directControl = mData->mSession.mDirectControl;
14605 }
14606
14607 /* ignore notifications sent after #OnSessionEnd() is called */
14608 if (!directControl)
14609 return S_OK;
14610
14611 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14612}
14613
14614/**
14615 * @note Locks this object for reading.
14616 */
14617HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14618{
14619 LogFlowThisFunc(("\n"));
14620
14621 AutoCaller autoCaller(this);
14622 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14623
14624 ComPtr<IInternalSessionControl> directControl;
14625 {
14626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14627 if (mData->mSession.mLockType == LockType_VM)
14628 directControl = mData->mSession.mDirectControl;
14629 }
14630
14631 /* ignore notifications sent after #OnSessionEnd() is called */
14632 if (!directControl)
14633 return S_OK;
14634
14635 return directControl->OnSerialPortChange(serialPort);
14636}
14637
14638/**
14639 * @note Locks this object for reading.
14640 */
14641HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14642{
14643 LogFlowThisFunc(("\n"));
14644
14645 AutoCaller autoCaller(this);
14646 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14647
14648 ComPtr<IInternalSessionControl> directControl;
14649 {
14650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14651 if (mData->mSession.mLockType == LockType_VM)
14652 directControl = mData->mSession.mDirectControl;
14653 }
14654
14655 /* ignore notifications sent after #OnSessionEnd() is called */
14656 if (!directControl)
14657 return S_OK;
14658
14659 return directControl->OnParallelPortChange(parallelPort);
14660}
14661
14662/**
14663 * @note Locks this object for reading.
14664 */
14665HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14666{
14667 LogFlowThisFunc(("\n"));
14668
14669 AutoCaller autoCaller(this);
14670 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14671
14672 ComPtr<IInternalSessionControl> directControl;
14673 {
14674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14675 if (mData->mSession.mLockType == LockType_VM)
14676 directControl = mData->mSession.mDirectControl;
14677 }
14678
14679 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14680
14681 /* ignore notifications sent after #OnSessionEnd() is called */
14682 if (!directControl)
14683 return S_OK;
14684
14685 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14686}
14687
14688/**
14689 * @note Locks this object for reading.
14690 */
14691HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14692{
14693 LogFlowThisFunc(("\n"));
14694
14695 AutoCaller autoCaller(this);
14696 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14697
14698 ComPtr<IInternalSessionControl> directControl;
14699 {
14700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14701 if (mData->mSession.mLockType == LockType_VM)
14702 directControl = mData->mSession.mDirectControl;
14703 }
14704
14705 mParent->i_onMediumChanged(aAttachment);
14706
14707 /* ignore notifications sent after #OnSessionEnd() is called */
14708 if (!directControl)
14709 return S_OK;
14710
14711 return directControl->OnMediumChange(aAttachment, aForce);
14712}
14713
14714HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14715{
14716 LogFlowThisFunc(("\n"));
14717
14718 AutoCaller autoCaller(this);
14719 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14720
14721 ComPtr<IInternalSessionControl> directControl;
14722 {
14723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14724 if (mData->mSession.mLockType == LockType_VM)
14725 directControl = mData->mSession.mDirectControl;
14726 }
14727
14728 /* ignore notifications sent after #OnSessionEnd() is called */
14729 if (!directControl)
14730 return S_OK;
14731
14732 return directControl->OnVMProcessPriorityChange(aPriority);
14733}
14734
14735/**
14736 * @note Locks this object for reading.
14737 */
14738HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14739{
14740 LogFlowThisFunc(("\n"));
14741
14742 AutoCaller autoCaller(this);
14743 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14744
14745 ComPtr<IInternalSessionControl> directControl;
14746 {
14747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14748 if (mData->mSession.mLockType == LockType_VM)
14749 directControl = mData->mSession.mDirectControl;
14750 }
14751
14752 /* ignore notifications sent after #OnSessionEnd() is called */
14753 if (!directControl)
14754 return S_OK;
14755
14756 return directControl->OnCPUChange(aCPU, aRemove);
14757}
14758
14759HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14760{
14761 LogFlowThisFunc(("\n"));
14762
14763 AutoCaller autoCaller(this);
14764 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14765
14766 ComPtr<IInternalSessionControl> directControl;
14767 {
14768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14769 if (mData->mSession.mLockType == LockType_VM)
14770 directControl = mData->mSession.mDirectControl;
14771 }
14772
14773 /* ignore notifications sent after #OnSessionEnd() is called */
14774 if (!directControl)
14775 return S_OK;
14776
14777 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14778}
14779
14780/**
14781 * @note Locks this object for reading.
14782 */
14783HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14784{
14785 LogFlowThisFunc(("\n"));
14786
14787 AutoCaller autoCaller(this);
14788 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14789
14790 ComPtr<IInternalSessionControl> directControl;
14791 {
14792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14793 if (mData->mSession.mLockType == LockType_VM)
14794 directControl = mData->mSession.mDirectControl;
14795 }
14796
14797 /* ignore notifications sent after #OnSessionEnd() is called */
14798 if (!directControl)
14799 return S_OK;
14800
14801 return directControl->OnVRDEServerChange(aRestart);
14802}
14803
14804/**
14805 * @note Locks this object for reading.
14806 */
14807HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14808{
14809 LogFlowThisFunc(("\n"));
14810
14811 AutoCaller autoCaller(this);
14812 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14813
14814 ComPtr<IInternalSessionControl> directControl;
14815 {
14816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14817 if (mData->mSession.mLockType == LockType_VM)
14818 directControl = mData->mSession.mDirectControl;
14819 }
14820
14821 /* ignore notifications sent after #OnSessionEnd() is called */
14822 if (!directControl)
14823 return S_OK;
14824
14825 return directControl->OnRecordingChange(aEnable);
14826}
14827
14828/**
14829 * @note Locks this object for reading.
14830 */
14831HRESULT SessionMachine::i_onUSBControllerChange()
14832{
14833 LogFlowThisFunc(("\n"));
14834
14835 AutoCaller autoCaller(this);
14836 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14837
14838 ComPtr<IInternalSessionControl> directControl;
14839 {
14840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14841 if (mData->mSession.mLockType == LockType_VM)
14842 directControl = mData->mSession.mDirectControl;
14843 }
14844
14845 /* ignore notifications sent after #OnSessionEnd() is called */
14846 if (!directControl)
14847 return S_OK;
14848
14849 return directControl->OnUSBControllerChange();
14850}
14851
14852/**
14853 * @note Locks this object for reading.
14854 */
14855HRESULT SessionMachine::i_onSharedFolderChange()
14856{
14857 LogFlowThisFunc(("\n"));
14858
14859 AutoCaller autoCaller(this);
14860 AssertComRCReturnRC(autoCaller.hrc());
14861
14862 ComPtr<IInternalSessionControl> directControl;
14863 {
14864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14865 if (mData->mSession.mLockType == LockType_VM)
14866 directControl = mData->mSession.mDirectControl;
14867 }
14868
14869 /* ignore notifications sent after #OnSessionEnd() is called */
14870 if (!directControl)
14871 return S_OK;
14872
14873 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14874}
14875
14876/**
14877 * @note Locks this object for reading.
14878 */
14879HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14880{
14881 LogFlowThisFunc(("\n"));
14882
14883 AutoCaller autoCaller(this);
14884 AssertComRCReturnRC(autoCaller.hrc());
14885
14886 ComPtr<IInternalSessionControl> directControl;
14887 {
14888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14889 if (mData->mSession.mLockType == LockType_VM)
14890 directControl = mData->mSession.mDirectControl;
14891 }
14892
14893 /* ignore notifications sent after #OnSessionEnd() is called */
14894 if (!directControl)
14895 return S_OK;
14896
14897 return directControl->OnClipboardModeChange(aClipboardMode);
14898}
14899
14900/**
14901 * @note Locks this object for reading.
14902 */
14903HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14904{
14905 LogFlowThisFunc(("\n"));
14906
14907 AutoCaller autoCaller(this);
14908 AssertComRCReturnRC(autoCaller.hrc());
14909
14910 ComPtr<IInternalSessionControl> directControl;
14911 {
14912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14913 if (mData->mSession.mLockType == LockType_VM)
14914 directControl = mData->mSession.mDirectControl;
14915 }
14916
14917 /* ignore notifications sent after #OnSessionEnd() is called */
14918 if (!directControl)
14919 return S_OK;
14920
14921 return directControl->OnClipboardFileTransferModeChange(aEnable);
14922}
14923
14924/**
14925 * @note Locks this object for reading.
14926 */
14927HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14928{
14929 LogFlowThisFunc(("\n"));
14930
14931 AutoCaller autoCaller(this);
14932 AssertComRCReturnRC(autoCaller.hrc());
14933
14934 ComPtr<IInternalSessionControl> directControl;
14935 {
14936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14937 if (mData->mSession.mLockType == LockType_VM)
14938 directControl = mData->mSession.mDirectControl;
14939 }
14940
14941 /* ignore notifications sent after #OnSessionEnd() is called */
14942 if (!directControl)
14943 return S_OK;
14944
14945 return directControl->OnDnDModeChange(aDnDMode);
14946}
14947
14948/**
14949 * @note Locks this object for reading.
14950 */
14951HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14952{
14953 LogFlowThisFunc(("\n"));
14954
14955 AutoCaller autoCaller(this);
14956 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14957
14958 ComPtr<IInternalSessionControl> directControl;
14959 {
14960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14961 if (mData->mSession.mLockType == LockType_VM)
14962 directControl = mData->mSession.mDirectControl;
14963 }
14964
14965 /* ignore notifications sent after #OnSessionEnd() is called */
14966 if (!directControl)
14967 return S_OK;
14968
14969 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14970}
14971
14972/**
14973 * @note Locks this object for reading.
14974 */
14975HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14976{
14977 LogFlowThisFunc(("\n"));
14978
14979 AutoCaller autoCaller(this);
14980 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14981
14982 ComPtr<IInternalSessionControl> directControl;
14983 {
14984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14985 if (mData->mSession.mLockType == LockType_VM)
14986 directControl = mData->mSession.mDirectControl;
14987 }
14988
14989 /* ignore notifications sent after #OnSessionEnd() is called */
14990 if (!directControl)
14991 return S_OK;
14992
14993 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14994}
14995
14996/**
14997 * @note Locks this object for reading.
14998 */
14999HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
15000{
15001 LogFlowThisFunc(("\n"));
15002
15003 AutoCaller autoCaller(this);
15004 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15005
15006 ComPtr<IInternalSessionControl> directControl;
15007 {
15008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15009 if (mData->mSession.mLockType == LockType_VM)
15010 directControl = mData->mSession.mDirectControl;
15011 }
15012
15013 /* ignore notifications sent after #OnSessionEnd() is called */
15014 if (!directControl)
15015 return S_OK;
15016
15017 return directControl->OnGuestDebugControlChange(guestDebugControl);
15018}
15019
15020/**
15021 * Returns @c true if this machine's USB controller reports it has a matching
15022 * filter for the given USB device and @c false otherwise.
15023 *
15024 * @note locks this object for reading.
15025 */
15026bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15027{
15028 AutoCaller autoCaller(this);
15029 /* silently return if not ready -- this method may be called after the
15030 * direct machine session has been called */
15031 if (!autoCaller.isOk())
15032 return false;
15033
15034#ifdef VBOX_WITH_USB
15035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15036
15037 switch (mData->mMachineState)
15038 {
15039 case MachineState_Starting:
15040 case MachineState_Restoring:
15041 case MachineState_TeleportingIn:
15042 case MachineState_Paused:
15043 case MachineState_Running:
15044 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15045 * elsewhere... */
15046 alock.release();
15047 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15048 default: break;
15049 }
15050#else
15051 NOREF(aDevice);
15052 NOREF(aMaskedIfs);
15053#endif
15054 return false;
15055}
15056
15057/**
15058 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15059 */
15060HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15061 IVirtualBoxErrorInfo *aError,
15062 ULONG aMaskedIfs,
15063 const com::Utf8Str &aCaptureFilename)
15064{
15065 LogFlowThisFunc(("\n"));
15066
15067 AutoCaller autoCaller(this);
15068
15069 /* This notification may happen after the machine object has been
15070 * uninitialized (the session was closed), so don't assert. */
15071 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15072
15073 ComPtr<IInternalSessionControl> directControl;
15074 {
15075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15076 if (mData->mSession.mLockType == LockType_VM)
15077 directControl = mData->mSession.mDirectControl;
15078 }
15079
15080 /* fail on notifications sent after #OnSessionEnd() is called, it is
15081 * expected by the caller */
15082 if (!directControl)
15083 return E_FAIL;
15084
15085 /* No locks should be held at this point. */
15086 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15087 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15088
15089 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15090}
15091
15092/**
15093 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15094 */
15095HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15096 IVirtualBoxErrorInfo *aError)
15097{
15098 LogFlowThisFunc(("\n"));
15099
15100 AutoCaller autoCaller(this);
15101
15102 /* This notification may happen after the machine object has been
15103 * uninitialized (the session was closed), so don't assert. */
15104 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15105
15106 ComPtr<IInternalSessionControl> directControl;
15107 {
15108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15109 if (mData->mSession.mLockType == LockType_VM)
15110 directControl = mData->mSession.mDirectControl;
15111 }
15112
15113 /* fail on notifications sent after #OnSessionEnd() is called, it is
15114 * expected by the caller */
15115 if (!directControl)
15116 return E_FAIL;
15117
15118 /* No locks should be held at this point. */
15119 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15120 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15121
15122 return directControl->OnUSBDeviceDetach(aId, aError);
15123}
15124
15125// protected methods
15126/////////////////////////////////////////////////////////////////////////////
15127
15128/**
15129 * Deletes the given file if it is no longer in use by either the current machine state
15130 * (if the machine is "saved") or any of the machine's snapshots.
15131 *
15132 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15133 * but is different for each SnapshotMachine. When calling this, the order of calling this
15134 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15135 * is therefore critical. I know, it's all rather messy.
15136 *
15137 * @param strStateFile
15138 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15139 * the test for whether the saved state file is in use.
15140 */
15141void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15142 Snapshot *pSnapshotToIgnore)
15143{
15144 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15145 if ( (strStateFile.isNotEmpty())
15146 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15147 )
15148 // ... and it must also not be shared with other snapshots
15149 if ( !mData->mFirstSnapshot
15150 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15151 // this checks the SnapshotMachine's state file paths
15152 )
15153 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15154}
15155
15156/**
15157 * Locks the attached media.
15158 *
15159 * All attached hard disks are locked for writing and DVD/floppy are locked for
15160 * reading. Parents of attached hard disks (if any) are locked for reading.
15161 *
15162 * This method also performs accessibility check of all media it locks: if some
15163 * media is inaccessible, the method will return a failure and a bunch of
15164 * extended error info objects per each inaccessible medium.
15165 *
15166 * Note that this method is atomic: if it returns a success, all media are
15167 * locked as described above; on failure no media is locked at all (all
15168 * succeeded individual locks will be undone).
15169 *
15170 * The caller is responsible for doing the necessary state sanity checks.
15171 *
15172 * The locks made by this method must be undone by calling #unlockMedia() when
15173 * no more needed.
15174 */
15175HRESULT SessionMachine::i_lockMedia()
15176{
15177 AutoCaller autoCaller(this);
15178 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15179
15180 AutoMultiWriteLock2 alock(this->lockHandle(),
15181 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15182
15183 /* bail out if trying to lock things with already set up locking */
15184 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15185
15186 MultiResult hrcMult(S_OK);
15187
15188 /* Collect locking information for all medium objects attached to the VM. */
15189 for (MediumAttachmentList::const_iterator
15190 it = mMediumAttachments->begin();
15191 it != mMediumAttachments->end();
15192 ++it)
15193 {
15194 MediumAttachment *pAtt = *it;
15195 DeviceType_T devType = pAtt->i_getType();
15196 Medium *pMedium = pAtt->i_getMedium();
15197
15198 MediumLockList *pMediumLockList(new MediumLockList());
15199 // There can be attachments without a medium (floppy/dvd), and thus
15200 // it's impossible to create a medium lock list. It still makes sense
15201 // to have the empty medium lock list in the map in case a medium is
15202 // attached later.
15203 if (pMedium != NULL)
15204 {
15205 MediumType_T mediumType = pMedium->i_getType();
15206 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15207 || mediumType == MediumType_Shareable;
15208 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15209
15210 alock.release();
15211 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15212 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15213 false /* fMediumLockWriteAll */,
15214 NULL,
15215 *pMediumLockList);
15216 alock.acquire();
15217 if (FAILED(hrcMult))
15218 {
15219 delete pMediumLockList;
15220 mData->mSession.mLockedMedia.Clear();
15221 break;
15222 }
15223 }
15224
15225 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15226 if (FAILED(hrc))
15227 {
15228 mData->mSession.mLockedMedia.Clear();
15229 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
15230 break;
15231 }
15232 }
15233
15234 if (SUCCEEDED(hrcMult))
15235 {
15236 /* Now lock all media. If this fails, nothing is locked. */
15237 alock.release();
15238 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
15239 alock.acquire();
15240 if (FAILED(hrc))
15241 hrcMult = setError(hrc,
15242 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15243 }
15244
15245 return hrcMult;
15246}
15247
15248/**
15249 * Undoes the locks made by by #lockMedia().
15250 */
15251HRESULT SessionMachine::i_unlockMedia()
15252{
15253 AutoCaller autoCaller(this);
15254 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15255
15256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15257
15258 /* we may be holding important error info on the current thread;
15259 * preserve it */
15260 ErrorInfoKeeper eik;
15261
15262 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
15263 AssertComRC(hrc);
15264 return hrc;
15265}
15266
15267/**
15268 * Helper to change the machine state (reimplementation).
15269 *
15270 * @note Locks this object for writing.
15271 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15272 * it can cause crashes in random places due to unexpectedly committing
15273 * the current settings. The caller is responsible for that. The call
15274 * to saveStateSettings is fine, because this method does not commit.
15275 */
15276HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15277{
15278 LogFlowThisFuncEnter();
15279
15280 AutoCaller autoCaller(this);
15281 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15282
15283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15284
15285 MachineState_T oldMachineState = mData->mMachineState;
15286
15287 AssertMsgReturn(oldMachineState != aMachineState,
15288 ("oldMachineState=%s, aMachineState=%s\n",
15289 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15290 E_FAIL);
15291
15292 HRESULT hrc = S_OK;
15293
15294 int stsFlags = 0;
15295 bool deleteSavedState = false;
15296
15297 /* detect some state transitions */
15298
15299 if ( ( ( oldMachineState == MachineState_Saved
15300 || oldMachineState == MachineState_AbortedSaved
15301 )
15302 && aMachineState == MachineState_Restoring
15303 )
15304 || ( ( oldMachineState == MachineState_PoweredOff
15305 || oldMachineState == MachineState_Teleported
15306 || oldMachineState == MachineState_Aborted
15307 )
15308 && ( aMachineState == MachineState_TeleportingIn
15309 || aMachineState == MachineState_Starting
15310 )
15311 )
15312 )
15313 {
15314 /* The EMT thread is about to start */
15315
15316 /* Nothing to do here for now... */
15317
15318 /// @todo NEWMEDIA don't let mDVDDrive and other children
15319 /// change anything when in the Starting/Restoring state
15320 }
15321 else if ( ( oldMachineState == MachineState_Running
15322 || oldMachineState == MachineState_Paused
15323 || oldMachineState == MachineState_Teleporting
15324 || oldMachineState == MachineState_OnlineSnapshotting
15325 || oldMachineState == MachineState_LiveSnapshotting
15326 || oldMachineState == MachineState_Stuck
15327 || oldMachineState == MachineState_Starting
15328 || oldMachineState == MachineState_Stopping
15329 || oldMachineState == MachineState_Saving
15330 || oldMachineState == MachineState_Restoring
15331 || oldMachineState == MachineState_TeleportingPausedVM
15332 || oldMachineState == MachineState_TeleportingIn
15333 )
15334 && ( aMachineState == MachineState_PoweredOff
15335 || aMachineState == MachineState_Saved
15336 || aMachineState == MachineState_Teleported
15337 || aMachineState == MachineState_Aborted
15338 || aMachineState == MachineState_AbortedSaved
15339 )
15340 )
15341 {
15342 /* The EMT thread has just stopped, unlock attached media. Note that as
15343 * opposed to locking that is done from Console, we do unlocking here
15344 * because the VM process may have aborted before having a chance to
15345 * properly unlock all media it locked. */
15346
15347 unlockMedia();
15348 }
15349
15350 if (oldMachineState == MachineState_Restoring)
15351 {
15352 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15353 {
15354 /*
15355 * delete the saved state file once the machine has finished
15356 * restoring from it (note that Console sets the state from
15357 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15358 * to give the user an ability to fix an error and retry --
15359 * we keep the saved state file in this case)
15360 */
15361 deleteSavedState = true;
15362 }
15363 }
15364 else if ( oldMachineState == MachineState_Saved
15365 && ( aMachineState == MachineState_PoweredOff
15366 || aMachineState == MachineState_Teleported
15367 )
15368 )
15369 {
15370 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15371 deleteSavedState = true;
15372 mData->mCurrentStateModified = TRUE;
15373 stsFlags |= SaveSTS_CurStateModified;
15374 }
15375 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15376 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15377
15378 if ( aMachineState == MachineState_Starting
15379 || aMachineState == MachineState_Restoring
15380 || aMachineState == MachineState_TeleportingIn
15381 )
15382 {
15383 /* set the current state modified flag to indicate that the current
15384 * state is no more identical to the state in the
15385 * current snapshot */
15386 if (!mData->mCurrentSnapshot.isNull())
15387 {
15388 mData->mCurrentStateModified = TRUE;
15389 stsFlags |= SaveSTS_CurStateModified;
15390 }
15391 }
15392
15393 if (deleteSavedState)
15394 {
15395 if (mRemoveSavedState)
15396 {
15397 Assert(!mSSData->strStateFilePath.isEmpty());
15398
15399 // it is safe to delete the saved state file if ...
15400 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15401 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15402 // ... none of the snapshots share the saved state file
15403 )
15404 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15405 }
15406
15407 mSSData->strStateFilePath.setNull();
15408 stsFlags |= SaveSTS_StateFilePath;
15409 }
15410
15411 /* redirect to the underlying peer machine */
15412 mPeer->i_setMachineState(aMachineState);
15413
15414 if ( oldMachineState != MachineState_RestoringSnapshot
15415 && ( aMachineState == MachineState_PoweredOff
15416 || aMachineState == MachineState_Teleported
15417 || aMachineState == MachineState_Aborted
15418 || aMachineState == MachineState_AbortedSaved
15419 || aMachineState == MachineState_Saved))
15420 {
15421 /* the machine has stopped execution
15422 * (or the saved state file was adopted) */
15423 stsFlags |= SaveSTS_StateTimeStamp;
15424 }
15425
15426 if ( ( oldMachineState == MachineState_PoweredOff
15427 || oldMachineState == MachineState_Aborted
15428 || oldMachineState == MachineState_Teleported
15429 )
15430 && aMachineState == MachineState_Saved)
15431 {
15432 /* the saved state file was adopted */
15433 Assert(!mSSData->strStateFilePath.isEmpty());
15434 stsFlags |= SaveSTS_StateFilePath;
15435 }
15436
15437#ifdef VBOX_WITH_GUEST_PROPS
15438 if ( aMachineState == MachineState_PoweredOff
15439 || aMachineState == MachineState_Aborted
15440 || aMachineState == MachineState_Teleported)
15441 {
15442 /* Make sure any transient guest properties get removed from the
15443 * property store on shutdown. */
15444 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15445
15446 /* remove it from the settings representation */
15447 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15448 for (settings::GuestPropertiesList::iterator
15449 it = llGuestProperties.begin();
15450 it != llGuestProperties.end();
15451 /*nothing*/)
15452 {
15453 const settings::GuestProperty &prop = *it;
15454 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15455 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15456 {
15457 it = llGuestProperties.erase(it);
15458 fNeedsSaving = true;
15459 }
15460 else
15461 {
15462 ++it;
15463 }
15464 }
15465
15466 /* Additionally remove it from the HWData representation. Required to
15467 * keep everything in sync, as this is what the API keeps using. */
15468 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15469 for (HWData::GuestPropertyMap::iterator
15470 it = llHWGuestProperties.begin();
15471 it != llHWGuestProperties.end();
15472 /*nothing*/)
15473 {
15474 uint32_t fFlags = it->second.mFlags;
15475 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15476 {
15477 /* iterator where we need to continue after the erase call
15478 * (C++03 is a fact still, and it doesn't return the iterator
15479 * which would allow continuing) */
15480 HWData::GuestPropertyMap::iterator it2 = it;
15481 ++it2;
15482 llHWGuestProperties.erase(it);
15483 it = it2;
15484 fNeedsSaving = true;
15485 }
15486 else
15487 {
15488 ++it;
15489 }
15490 }
15491
15492 if (fNeedsSaving)
15493 {
15494 mData->mCurrentStateModified = TRUE;
15495 stsFlags |= SaveSTS_CurStateModified;
15496 }
15497 }
15498#endif /* VBOX_WITH_GUEST_PROPS */
15499
15500 hrc = i_saveStateSettings(stsFlags);
15501
15502 if ( ( oldMachineState != MachineState_PoweredOff
15503 && oldMachineState != MachineState_Aborted
15504 && oldMachineState != MachineState_Teleported
15505 )
15506 && ( aMachineState == MachineState_PoweredOff
15507 || aMachineState == MachineState_Aborted
15508 || aMachineState == MachineState_Teleported
15509 )
15510 )
15511 {
15512 /* we've been shut down for any reason */
15513 /* no special action so far */
15514 }
15515
15516 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
15517 LogFlowThisFuncLeave();
15518 return hrc;
15519}
15520
15521/**
15522 * Sends the current machine state value to the VM process.
15523 *
15524 * @note Locks this object for reading, then calls a client process.
15525 */
15526HRESULT SessionMachine::i_updateMachineStateOnClient()
15527{
15528 AutoCaller autoCaller(this);
15529 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15530
15531 ComPtr<IInternalSessionControl> directControl;
15532 {
15533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15534 AssertReturn(!!mData, E_FAIL);
15535 if (mData->mSession.mLockType == LockType_VM)
15536 directControl = mData->mSession.mDirectControl;
15537
15538 /* directControl may be already set to NULL here in #OnSessionEnd()
15539 * called too early by the direct session process while there is still
15540 * some operation (like deleting the snapshot) in progress. The client
15541 * process in this case is waiting inside Session::close() for the
15542 * "end session" process object to complete, while #uninit() called by
15543 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15544 * operation to complete. For now, we accept this inconsistent behavior
15545 * and simply do nothing here. */
15546
15547 if (mData->mSession.mState == SessionState_Unlocking)
15548 return S_OK;
15549 }
15550
15551 /* ignore notifications sent after #OnSessionEnd() is called */
15552 if (!directControl)
15553 return S_OK;
15554
15555 return directControl->UpdateMachineState(mData->mMachineState);
15556}
15557
15558
15559/*static*/
15560HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15561{
15562 va_list args;
15563 va_start(args, pcszMsg);
15564 HRESULT hrc = setErrorInternalV(aResultCode,
15565 getStaticClassIID(),
15566 getStaticComponentName(),
15567 pcszMsg, args,
15568 false /* aWarning */,
15569 true /* aLogIt */);
15570 va_end(args);
15571 return hrc;
15572}
15573
15574
15575HRESULT Machine::updateState(MachineState_T aState)
15576{
15577 NOREF(aState);
15578 ReturnComNotImplemented();
15579}
15580
15581HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15582{
15583 NOREF(aProgress);
15584 ReturnComNotImplemented();
15585}
15586
15587HRESULT Machine::endPowerUp(LONG aResult)
15588{
15589 NOREF(aResult);
15590 ReturnComNotImplemented();
15591}
15592
15593HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15594{
15595 NOREF(aProgress);
15596 ReturnComNotImplemented();
15597}
15598
15599HRESULT Machine::endPoweringDown(LONG aResult,
15600 const com::Utf8Str &aErrMsg)
15601{
15602 NOREF(aResult);
15603 NOREF(aErrMsg);
15604 ReturnComNotImplemented();
15605}
15606
15607HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15608 BOOL *aMatched,
15609 ULONG *aMaskedInterfaces)
15610{
15611 NOREF(aDevice);
15612 NOREF(aMatched);
15613 NOREF(aMaskedInterfaces);
15614 ReturnComNotImplemented();
15615
15616}
15617
15618HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15619{
15620 NOREF(aId); NOREF(aCaptureFilename);
15621 ReturnComNotImplemented();
15622}
15623
15624HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15625 BOOL aDone)
15626{
15627 NOREF(aId);
15628 NOREF(aDone);
15629 ReturnComNotImplemented();
15630}
15631
15632HRESULT Machine::autoCaptureUSBDevices()
15633{
15634 ReturnComNotImplemented();
15635}
15636
15637HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15638{
15639 NOREF(aDone);
15640 ReturnComNotImplemented();
15641}
15642
15643HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15644 ComPtr<IProgress> &aProgress)
15645{
15646 NOREF(aSession);
15647 NOREF(aProgress);
15648 ReturnComNotImplemented();
15649}
15650
15651HRESULT Machine::finishOnlineMergeMedium()
15652{
15653 ReturnComNotImplemented();
15654}
15655
15656HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15657 std::vector<com::Utf8Str> &aValues,
15658 std::vector<LONG64> &aTimestamps,
15659 std::vector<com::Utf8Str> &aFlags)
15660{
15661 NOREF(aNames);
15662 NOREF(aValues);
15663 NOREF(aTimestamps);
15664 NOREF(aFlags);
15665 ReturnComNotImplemented();
15666}
15667
15668HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15669 const com::Utf8Str &aValue,
15670 LONG64 aTimestamp,
15671 const com::Utf8Str &aFlags,
15672 BOOL fWasDeleted)
15673{
15674 NOREF(aName);
15675 NOREF(aValue);
15676 NOREF(aTimestamp);
15677 NOREF(aFlags);
15678 NOREF(fWasDeleted);
15679 ReturnComNotImplemented();
15680}
15681
15682HRESULT Machine::lockMedia()
15683{
15684 ReturnComNotImplemented();
15685}
15686
15687HRESULT Machine::unlockMedia()
15688{
15689 ReturnComNotImplemented();
15690}
15691
15692HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15693 ComPtr<IMediumAttachment> &aNewAttachment)
15694{
15695 NOREF(aAttachment);
15696 NOREF(aNewAttachment);
15697 ReturnComNotImplemented();
15698}
15699
15700HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15701 ULONG aCpuUser,
15702 ULONG aCpuKernel,
15703 ULONG aCpuIdle,
15704 ULONG aMemTotal,
15705 ULONG aMemFree,
15706 ULONG aMemBalloon,
15707 ULONG aMemShared,
15708 ULONG aMemCache,
15709 ULONG aPagedTotal,
15710 ULONG aMemAllocTotal,
15711 ULONG aMemFreeTotal,
15712 ULONG aMemBalloonTotal,
15713 ULONG aMemSharedTotal,
15714 ULONG aVmNetRx,
15715 ULONG aVmNetTx)
15716{
15717 NOREF(aValidStats);
15718 NOREF(aCpuUser);
15719 NOREF(aCpuKernel);
15720 NOREF(aCpuIdle);
15721 NOREF(aMemTotal);
15722 NOREF(aMemFree);
15723 NOREF(aMemBalloon);
15724 NOREF(aMemShared);
15725 NOREF(aMemCache);
15726 NOREF(aPagedTotal);
15727 NOREF(aMemAllocTotal);
15728 NOREF(aMemFreeTotal);
15729 NOREF(aMemBalloonTotal);
15730 NOREF(aMemSharedTotal);
15731 NOREF(aVmNetRx);
15732 NOREF(aVmNetTx);
15733 ReturnComNotImplemented();
15734}
15735
15736HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15737 com::Utf8Str &aResult)
15738{
15739 NOREF(aAuthParams);
15740 NOREF(aResult);
15741 ReturnComNotImplemented();
15742}
15743
15744com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15745{
15746 com::Utf8Str strControllerName = "Unknown";
15747 switch (aBusType)
15748 {
15749 case StorageBus_IDE:
15750 {
15751 strControllerName = "IDE";
15752 break;
15753 }
15754 case StorageBus_SATA:
15755 {
15756 strControllerName = "SATA";
15757 break;
15758 }
15759 case StorageBus_SCSI:
15760 {
15761 strControllerName = "SCSI";
15762 break;
15763 }
15764 case StorageBus_Floppy:
15765 {
15766 strControllerName = "Floppy";
15767 break;
15768 }
15769 case StorageBus_SAS:
15770 {
15771 strControllerName = "SAS";
15772 break;
15773 }
15774 case StorageBus_USB:
15775 {
15776 strControllerName = "USB";
15777 break;
15778 }
15779 default:
15780 break;
15781 }
15782 return strControllerName;
15783}
15784
15785HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15786{
15787 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15788
15789 AutoCaller autoCaller(this);
15790 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15791
15792 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15793 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15794 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15795 if (FAILED(hrc)) return hrc;
15796
15797 NOREF(aFlags);
15798 com::Utf8Str osTypeId;
15799 ComObjPtr<GuestOSType> osType = NULL;
15800
15801 /* Get the guest os type as a string from the VB. */
15802 hrc = getOSTypeId(osTypeId);
15803 if (FAILED(hrc)) return hrc;
15804
15805 /* Get the os type obj that coresponds, can be used to get
15806 * the defaults for this guest OS. */
15807 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15808 if (FAILED(hrc)) return hrc;
15809
15810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15811
15812 /* Let the OS type select 64-bit ness. */
15813 mHWData->mLongMode = osType->i_is64Bit()
15814 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15815
15816 /* Let the OS type enable the X2APIC */
15817 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15818
15819 /* This one covers IOAPICEnabled. */
15820 mBIOSSettings->i_applyDefaults(osType);
15821
15822 /* Initialize default record settings. */
15823 mRecordingSettings->i_applyDefaults();
15824
15825 /* Initialize default BIOS settings here */
15826 /* Hardware virtualization must be ON by default */
15827 mHWData->mAPIC = true;
15828 mHWData->mHWVirtExEnabled = true;
15829
15830 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15831 if (FAILED(hrc)) return hrc;
15832
15833 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15834 if (FAILED(hrc)) return hrc;
15835
15836 /* Graphics stuff. */
15837 GraphicsControllerType_T graphicsController;
15838 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15839 if (FAILED(hrc)) return hrc;
15840
15841 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15842 if (FAILED(hrc)) return hrc;
15843
15844 ULONG vramSize;
15845 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15846 if (FAILED(hrc)) return hrc;
15847
15848 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15849 if (FAILED(hrc)) return hrc;
15850
15851 BOOL fAccelerate2DVideoEnabled;
15852 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15853 if (FAILED(hrc)) return hrc;
15854
15855 hrc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15856 if (FAILED(hrc)) return hrc;
15857
15858 BOOL fAccelerate3DEnabled;
15859 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15860 if (FAILED(hrc)) return hrc;
15861
15862 hrc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15863 if (FAILED(hrc)) return hrc;
15864
15865 hrc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15866 if (FAILED(hrc)) return hrc;
15867
15868 hrc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15869 if (FAILED(hrc)) return hrc;
15870
15871 hrc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15872 if (FAILED(hrc)) return hrc;
15873
15874 BOOL mRTCUseUTC;
15875 hrc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15876 if (FAILED(hrc)) return hrc;
15877
15878 setRTCUseUTC(mRTCUseUTC);
15879 if (FAILED(hrc)) return hrc;
15880
15881 /* the setter does more than just the assignment, so use it */
15882 ChipsetType_T enmChipsetType;
15883 hrc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15884 if (FAILED(hrc)) return hrc;
15885
15886 hrc = COMSETTER(ChipsetType)(enmChipsetType);
15887 if (FAILED(hrc)) return hrc;
15888
15889 hrc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15890 if (FAILED(hrc)) return hrc;
15891
15892 /* Apply IOMMU defaults. */
15893 IommuType_T enmIommuType;
15894 hrc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15895 if (FAILED(hrc)) return hrc;
15896
15897 hrc = COMSETTER(IommuType)(enmIommuType);
15898 if (FAILED(hrc)) return hrc;
15899
15900 /* Apply network adapters defaults */
15901 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15902 mNetworkAdapters[slot]->i_applyDefaults(osType);
15903
15904 /* Apply serial port defaults */
15905 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15906 mSerialPorts[slot]->i_applyDefaults(osType);
15907
15908 /* Apply parallel port defaults - not OS dependent*/
15909 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15910 mParallelPorts[slot]->i_applyDefaults();
15911
15912 /* This one covers the TPM type. */
15913 mTrustedPlatformModule->i_applyDefaults(osType);
15914
15915 /* This one covers secure boot. */
15916 hrc = mNvramStore->i_applyDefaults(osType);
15917 if (FAILED(hrc)) return hrc;
15918
15919 /* Audio stuff. */
15920 hrc = mAudioSettings->i_applyDefaults(osType);
15921 if (FAILED(hrc)) return hrc;
15922
15923 /* Storage Controllers */
15924 StorageControllerType_T hdStorageControllerType;
15925 StorageBus_T hdStorageBusType;
15926 StorageControllerType_T dvdStorageControllerType;
15927 StorageBus_T dvdStorageBusType;
15928 BOOL recommendedFloppy;
15929 ComPtr<IStorageController> floppyController;
15930 ComPtr<IStorageController> hdController;
15931 ComPtr<IStorageController> dvdController;
15932 Utf8Str strFloppyName, strDVDName, strHDName;
15933
15934 /* GUI auto generates controller names using bus type. Do the same*/
15935 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15936
15937 /* Floppy recommended? add one. */
15938 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15939 if (FAILED(hrc)) return hrc;
15940 if (recommendedFloppy)
15941 {
15942 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15943 if (FAILED(hrc)) return hrc;
15944 }
15945
15946 /* Setup one DVD storage controller. */
15947 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15948 if (FAILED(hrc)) return hrc;
15949
15950 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15951 if (FAILED(hrc)) return hrc;
15952
15953 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15954
15955 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15956 if (FAILED(hrc)) return hrc;
15957
15958 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15959 if (FAILED(hrc)) return hrc;
15960
15961 /* Setup one HDD storage controller. */
15962 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15963 if (FAILED(hrc)) return hrc;
15964
15965 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15966 if (FAILED(hrc)) return hrc;
15967
15968 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15969
15970 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15971 {
15972 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15973 if (FAILED(hrc)) return hrc;
15974
15975 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15976 if (FAILED(hrc)) return hrc;
15977 }
15978 else
15979 {
15980 /* The HD controller is the same as DVD: */
15981 hdController = dvdController;
15982 }
15983
15984 /* Limit the AHCI port count if it's used because windows has trouble with
15985 * too many ports and other guest (OS X in particular) may take extra long
15986 * boot: */
15987
15988 // pParent = static_cast<Medium*>(aP)
15989 IStorageController *temp = hdController;
15990 ComObjPtr<StorageController> storageController;
15991 storageController = static_cast<StorageController *>(temp);
15992
15993 // tempHDController = aHDController;
15994 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15995 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15996 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15997 storageController->COMSETTER(PortCount)(1);
15998
15999 /* USB stuff */
16000
16001 bool ohciEnabled = false;
16002
16003 ComPtr<IUSBController> usbController;
16004 BOOL recommendedUSB3;
16005 BOOL recommendedUSB;
16006 BOOL usbProxyAvailable;
16007
16008 getUSBProxyAvailable(&usbProxyAvailable);
16009 if (FAILED(hrc)) return hrc;
16010
16011 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16012 if (FAILED(hrc)) return hrc;
16013 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16014 if (FAILED(hrc)) return hrc;
16015
16016 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16017 {
16018 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16019 if (FAILED(hrc)) return hrc;
16020
16021 /* xHci includes OHCI */
16022 ohciEnabled = true;
16023 }
16024 if ( !ohciEnabled
16025 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16026 {
16027 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16028 if (FAILED(hrc)) return hrc;
16029 ohciEnabled = true;
16030
16031 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16032 if (FAILED(hrc)) return hrc;
16033 }
16034
16035 /* Set recommended human interface device types: */
16036 BOOL recommendedUSBHID;
16037 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16038 if (FAILED(hrc)) return hrc;
16039
16040 if (recommendedUSBHID)
16041 {
16042 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16043 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16044 if (!ohciEnabled && !usbDeviceFilters.isNull())
16045 {
16046 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16047 if (FAILED(hrc)) return hrc;
16048 }
16049 }
16050
16051 BOOL recommendedUSBTablet;
16052 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16053 if (FAILED(hrc)) return hrc;
16054
16055 if (recommendedUSBTablet)
16056 {
16057 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16058 if (!ohciEnabled && !usbDeviceFilters.isNull())
16059 {
16060 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16061 if (FAILED(hrc)) return hrc;
16062 }
16063 }
16064
16065 /* Enable the VMMDev testing feature for bootsector VMs: */
16066 if (osTypeId == "VBoxBS_64")
16067 {
16068 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16069 if (FAILED(hrc))
16070 return hrc;
16071 }
16072
16073 return S_OK;
16074}
16075
16076#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16077/**
16078 * Task record for change encryption settins.
16079 */
16080class Machine::ChangeEncryptionTask
16081 : public Machine::Task
16082{
16083public:
16084 ChangeEncryptionTask(Machine *m,
16085 Progress *p,
16086 const Utf8Str &t,
16087 const com::Utf8Str &aCurrentPassword,
16088 const com::Utf8Str &aCipher,
16089 const com::Utf8Str &aNewPassword,
16090 const com::Utf8Str &aNewPasswordId,
16091 const BOOL aForce,
16092 const MediaList &llMedia)
16093 : Task(m, p, t),
16094 mstrNewPassword(aNewPassword),
16095 mstrCurrentPassword(aCurrentPassword),
16096 mstrCipher(aCipher),
16097 mstrNewPasswordId(aNewPasswordId),
16098 mForce(aForce),
16099 mllMedia(llMedia)
16100 {}
16101
16102 ~ChangeEncryptionTask()
16103 {
16104 if (mstrNewPassword.length())
16105 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16106 if (mstrCurrentPassword.length())
16107 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16108 if (m_pCryptoIf)
16109 {
16110 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16111 m_pCryptoIf = NULL;
16112 }
16113 }
16114
16115 Utf8Str mstrNewPassword;
16116 Utf8Str mstrCurrentPassword;
16117 Utf8Str mstrCipher;
16118 Utf8Str mstrNewPasswordId;
16119 BOOL mForce;
16120 MediaList mllMedia;
16121 PCVBOXCRYPTOIF m_pCryptoIf;
16122private:
16123 void handler()
16124 {
16125 try
16126 {
16127 m_pMachine->i_changeEncryptionHandler(*this);
16128 }
16129 catch (...)
16130 {
16131 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16132 }
16133 }
16134
16135 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16136};
16137
16138/**
16139 * Scans specified directory and fills list by files found
16140 *
16141 * @returns VBox status code.
16142 * @param lstFiles
16143 * @param strDir
16144 * @param filePattern
16145 */
16146int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16147 const com::Utf8Str &strPattern)
16148{
16149 /* To get all entries including subdirectories. */
16150 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16151 if (!pszFilePattern)
16152 return VERR_NO_STR_MEMORY;
16153
16154 PRTDIRENTRYEX pDirEntry = NULL;
16155 RTDIR hDir;
16156 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16157 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16158 if (RT_SUCCESS(vrc))
16159 {
16160 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16161 if (pDirEntry)
16162 {
16163 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16164 != VERR_NO_MORE_FILES)
16165 {
16166 char *pszFilePath = NULL;
16167
16168 if (vrc == VERR_BUFFER_OVERFLOW)
16169 {
16170 /* allocate new buffer. */
16171 RTMemFree(pDirEntry);
16172 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16173 if (!pDirEntry)
16174 {
16175 vrc = VERR_NO_MEMORY;
16176 break;
16177 }
16178 /* Retry. */
16179 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16180 if (RT_FAILURE(vrc))
16181 break;
16182 }
16183 else if (RT_FAILURE(vrc))
16184 break;
16185
16186 /* Exclude . and .. */
16187 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16188 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16189 continue;
16190 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16191 {
16192 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16193 if (!pszSubDirPath)
16194 {
16195 vrc = VERR_NO_STR_MEMORY;
16196 break;
16197 }
16198 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16199 RTMemFree(pszSubDirPath);
16200 if (RT_FAILURE(vrc))
16201 break;
16202 continue;
16203 }
16204
16205 /* We got the new entry. */
16206 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16207 continue;
16208
16209 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16210 continue;
16211
16212 /* Prepend the path to the libraries. */
16213 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16214 if (!pszFilePath)
16215 {
16216 vrc = VERR_NO_STR_MEMORY;
16217 break;
16218 }
16219
16220 lstFiles.push_back(pszFilePath);
16221 RTStrFree(pszFilePath);
16222 }
16223
16224 RTMemFree(pDirEntry);
16225 }
16226 else
16227 vrc = VERR_NO_MEMORY;
16228
16229 RTDirClose(hDir);
16230 }
16231 else
16232 {
16233 /* On Windows the above immediately signals that there are no
16234 * files matching, while on other platforms enumerating the
16235 * files below fails. Either way: stop searching. */
16236 }
16237
16238 if ( vrc == VERR_NO_MORE_FILES
16239 || vrc == VERR_FILE_NOT_FOUND
16240 || vrc == VERR_PATH_NOT_FOUND)
16241 vrc = VINF_SUCCESS;
16242 RTStrFree(pszFilePattern);
16243 return vrc;
16244}
16245
16246/**
16247 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16248 *
16249 * @returns VBox status code.
16250 * @param pszFilename The file to open.
16251 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16252 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16253 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16254 * @param fOpen The open flags for the file.
16255 * @param phVfsIos Where to store the handle to the I/O stream on success.
16256 */
16257int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16258 const char *pszKeyStore, const char *pszPassword,
16259 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16260{
16261 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16262 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16263 if (RT_SUCCESS(vrc))
16264 {
16265 if (pCryptoIf)
16266 {
16267 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16268 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16269 if (RT_SUCCESS(vrc))
16270 {
16271 RTVfsFileRelease(hVfsFile);
16272 hVfsFile = hVfsFileCrypto;
16273 }
16274 }
16275
16276 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16277 RTVfsFileRelease(hVfsFile);
16278 }
16279
16280 return vrc;
16281}
16282
16283/**
16284 * Helper function processing all actions for one component (saved state files,
16285 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16286 *
16287 * @param task
16288 * @param strDirectory
16289 * @param strFilePattern
16290 * @param strMagic
16291 * @param strKeyStore
16292 * @param strKeyId
16293 * @return
16294 */
16295HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16296 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16297 com::Utf8Str &strKeyId, int iCipherMode)
16298{
16299 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16300 && task.mstrCipher.isEmpty()
16301 && task.mstrNewPassword.isEmpty()
16302 && task.mstrNewPasswordId.isEmpty();
16303 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16304 && task.mstrCipher.isNotEmpty()
16305 && task.mstrNewPassword.isNotEmpty()
16306 && task.mstrNewPasswordId.isNotEmpty();
16307
16308 /* check if the cipher is changed which causes the reencryption*/
16309
16310 const char *pszTaskCipher = NULL;
16311 if (task.mstrCipher.isNotEmpty())
16312 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16313
16314 if (!task.mForce && !fDecrypt && !fEncrypt)
16315 {
16316 char *pszCipher = NULL;
16317 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16318 NULL /*pszPassword*/,
16319 NULL /*ppbKey*/,
16320 NULL /*pcbKey*/,
16321 &pszCipher);
16322 if (RT_SUCCESS(vrc))
16323 {
16324 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16325 RTMemFree(pszCipher);
16326 }
16327 else
16328 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16329 strFilePattern.c_str(), vrc);
16330 }
16331
16332 /* Only the password needs to be changed */
16333 if (!task.mForce && !fDecrypt && !fEncrypt)
16334 {
16335 Assert(task.m_pCryptoIf);
16336
16337 VBOXCRYPTOCTX hCryptoCtx;
16338 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16339 if (RT_FAILURE(vrc))
16340 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16341 strFilePattern.c_str(), vrc);
16342 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16343 if (RT_FAILURE(vrc))
16344 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16345 strFilePattern.c_str(), vrc);
16346
16347 char *pszKeyStore = NULL;
16348 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16349 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16350 if (RT_FAILURE(vrc))
16351 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16352 strFilePattern.c_str(), vrc);
16353 strKeyStore = pszKeyStore;
16354 RTMemFree(pszKeyStore);
16355 strKeyId = task.mstrNewPasswordId;
16356 return S_OK;
16357 }
16358
16359 /* Reencryption required */
16360 HRESULT hrc = S_OK;
16361 int vrc = VINF_SUCCESS;
16362
16363 std::list<com::Utf8Str> lstFiles;
16364 if (SUCCEEDED(hrc))
16365 {
16366 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16367 if (RT_FAILURE(vrc))
16368 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
16369 }
16370 com::Utf8Str strNewKeyStore;
16371 if (SUCCEEDED(hrc))
16372 {
16373 if (!fDecrypt)
16374 {
16375 VBOXCRYPTOCTX hCryptoCtx;
16376 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16377 if (RT_FAILURE(vrc))
16378 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16379 strFilePattern.c_str(), vrc);
16380
16381 char *pszKeyStore = NULL;
16382 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16383 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16384 if (RT_FAILURE(vrc))
16385 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16386 strFilePattern.c_str(), vrc);
16387 strNewKeyStore = pszKeyStore;
16388 RTMemFree(pszKeyStore);
16389 }
16390
16391 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16392 it != lstFiles.end();
16393 ++it)
16394 {
16395 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16396 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16397
16398 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16399 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16400
16401 vrc = i_createIoStreamForFile((*it).c_str(),
16402 fEncrypt ? NULL : task.m_pCryptoIf,
16403 fEncrypt ? NULL : strKeyStore.c_str(),
16404 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16405 fOpenForRead, &hVfsIosOld);
16406 if (RT_SUCCESS(vrc))
16407 {
16408 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16409 fDecrypt ? NULL : task.m_pCryptoIf,
16410 fDecrypt ? NULL : strNewKeyStore.c_str(),
16411 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16412 fOpenForWrite, &hVfsIosNew);
16413 if (RT_FAILURE(vrc))
16414 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16415 (*it + ".tmp").c_str(), vrc);
16416 }
16417 else
16418 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
16419
16420 if (RT_SUCCESS(vrc))
16421 {
16422 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16423 if (RT_FAILURE(vrc))
16424 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16425 (*it).c_str(), vrc);
16426 }
16427
16428 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16429 RTVfsIoStrmRelease(hVfsIosOld);
16430 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16431 RTVfsIoStrmRelease(hVfsIosNew);
16432 }
16433 }
16434
16435 if (SUCCEEDED(hrc))
16436 {
16437 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16438 it != lstFiles.end();
16439 ++it)
16440 {
16441 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16442 if (RT_FAILURE(vrc))
16443 {
16444 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
16445 break;
16446 }
16447 }
16448 }
16449
16450 if (SUCCEEDED(hrc))
16451 {
16452 strKeyStore = strNewKeyStore;
16453 strKeyId = task.mstrNewPasswordId;
16454 }
16455
16456 return hrc;
16457}
16458
16459/**
16460 * Task thread implementation for Machine::changeEncryption(), called from
16461 * Machine::taskHandler().
16462 *
16463 * @note Locks this object for writing.
16464 *
16465 * @param task
16466 * @return
16467 */
16468void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16469{
16470 LogFlowThisFuncEnter();
16471
16472 AutoCaller autoCaller(this);
16473 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16474 if (FAILED(autoCaller.hrc()))
16475 {
16476 /* we might have been uninitialized because the session was accidentally
16477 * closed by the client, so don't assert */
16478 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
16479 task.m_pProgress->i_notifyComplete(hrc);
16480 LogFlowThisFuncLeave();
16481 return;
16482 }
16483
16484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16485
16486 HRESULT hrc = S_OK;
16487 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16488 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16489 try
16490 {
16491 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16492 if (FAILED(hrc))
16493 throw hrc;
16494
16495 if (task.mstrCurrentPassword.isEmpty())
16496 {
16497 if (mData->mstrKeyStore.isNotEmpty())
16498 throw setError(VBOX_E_PASSWORD_INCORRECT,
16499 tr("The password given for the encrypted VM is incorrect"));
16500 }
16501 else
16502 {
16503 if (mData->mstrKeyStore.isEmpty())
16504 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16505 tr("The VM is not configured for encryption"));
16506 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
16507 if (hrc == VBOX_E_PASSWORD_INCORRECT)
16508 throw setError(VBOX_E_PASSWORD_INCORRECT,
16509 tr("The password to decrypt the VM is incorrect"));
16510 }
16511
16512 if (task.mstrCipher.isNotEmpty())
16513 {
16514 if ( task.mstrNewPassword.isEmpty()
16515 && task.mstrNewPasswordId.isEmpty()
16516 && task.mstrCurrentPassword.isNotEmpty())
16517 {
16518 /* An empty password and password ID will default to the current password. */
16519 task.mstrNewPassword = task.mstrCurrentPassword;
16520 }
16521 else if (task.mstrNewPassword.isEmpty())
16522 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16523 tr("A password must be given for the VM encryption"));
16524 else if (task.mstrNewPasswordId.isEmpty())
16525 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16526 tr("A valid identifier for the password must be given"));
16527 }
16528 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16529 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16530 tr("The password and password identifier must be empty if the output should be unencrypted"));
16531
16532 /*
16533 * Save config.
16534 * Must be first operation to prevent making encrypted copies
16535 * for old version of the config file.
16536 */
16537 int fSave = Machine::SaveS_Force;
16538 if (task.mstrNewPassword.isNotEmpty())
16539 {
16540 VBOXCRYPTOCTX hCryptoCtx;
16541
16542 int vrc = VINF_SUCCESS;
16543 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16544 {
16545 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16546 task.mstrNewPassword.c_str(), &hCryptoCtx);
16547 if (RT_FAILURE(vrc))
16548 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16549 }
16550 else
16551 {
16552 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16553 task.mstrCurrentPassword.c_str(),
16554 &hCryptoCtx);
16555 if (RT_FAILURE(vrc))
16556 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16557 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16558 if (RT_FAILURE(vrc))
16559 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16560 }
16561
16562 char *pszKeyStore;
16563 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16564 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16565 if (RT_FAILURE(vrc))
16566 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16567 mData->mstrKeyStore = pszKeyStore;
16568 RTStrFree(pszKeyStore);
16569 mData->mstrKeyId = task.mstrNewPasswordId;
16570 size_t cbPassword = task.mstrNewPassword.length() + 1;
16571 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16572 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16573 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16574 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16575
16576 /*
16577 * Remove backuped config after saving because it can contain
16578 * unencrypted version of the config
16579 */
16580 fSave |= Machine::SaveS_RemoveBackup;
16581 }
16582 else
16583 {
16584 mData->mstrKeyId.setNull();
16585 mData->mstrKeyStore.setNull();
16586 }
16587
16588 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16589 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16590 Bstr bstrNewPassword(task.mstrNewPassword);
16591 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16592 /* encrypt mediums */
16593 alock.release();
16594 for (MediaList::iterator it = task.mllMedia.begin();
16595 it != task.mllMedia.end();
16596 ++it)
16597 {
16598 ComPtr<IProgress> pProgress1;
16599 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16600 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16601 pProgress1.asOutParam());
16602 if (FAILED(hrc)) throw hrc;
16603 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16604 if (FAILED(hrc)) throw hrc;
16605 }
16606 alock.acquire();
16607
16608 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16609
16610 Utf8Str strFullSnapshotFolder;
16611 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16612
16613 /* .sav files (main and snapshots) */
16614 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16615 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16616 if (FAILED(hrc))
16617 /* the helper function already sets error object */
16618 throw hrc;
16619
16620 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16621
16622 /* .nvram files */
16623 com::Utf8Str strNVRAMKeyId;
16624 com::Utf8Str strNVRAMKeyStore;
16625 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16626 if (FAILED(hrc))
16627 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16628
16629 Utf8Str strMachineFolder;
16630 i_calculateFullPath(".", strMachineFolder);
16631
16632 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16633 if (FAILED(hrc))
16634 /* the helper function already sets error object */
16635 throw hrc;
16636
16637 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16638 if (FAILED(hrc))
16639 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16640
16641 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16642
16643 /* .log files */
16644 com::Utf8Str strLogFolder;
16645 i_getLogFolder(strLogFolder);
16646 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16647 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16648 if (FAILED(hrc))
16649 /* the helper function already sets error object */
16650 throw hrc;
16651
16652 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16653
16654 i_saveSettings(NULL, alock, fSave);
16655 }
16656 catch (HRESULT hrcXcpt)
16657 {
16658 hrc = hrcXcpt;
16659 mData->mstrKeyId = strOldKeyId;
16660 mData->mstrKeyStore = strOldKeyStore;
16661 }
16662
16663 task.m_pProgress->i_notifyComplete(hrc);
16664
16665 LogFlowThisFuncLeave();
16666}
16667#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16668
16669HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16670 const com::Utf8Str &aCipher,
16671 const com::Utf8Str &aNewPassword,
16672 const com::Utf8Str &aNewPasswordId,
16673 BOOL aForce,
16674 ComPtr<IProgress> &aProgress)
16675{
16676 LogFlowFuncEnter();
16677
16678#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16679 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16680 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16681#else
16682 /* make the VM accessible */
16683 if (!mData->mAccessible)
16684 {
16685 if ( aCurrentPassword.isEmpty()
16686 || mData->mstrKeyId.isEmpty())
16687 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16688
16689 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16690 if (FAILED(hrc))
16691 return hrc;
16692 }
16693
16694 AutoLimitedCaller autoCaller(this);
16695 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16696
16697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16698
16699 /* define mediums to be change encryption */
16700
16701 MediaList llMedia;
16702 for (MediumAttachmentList::iterator
16703 it = mMediumAttachments->begin();
16704 it != mMediumAttachments->end();
16705 ++it)
16706 {
16707 ComObjPtr<MediumAttachment> &pAttach = *it;
16708 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16709
16710 if (!pMedium.isNull())
16711 {
16712 AutoCaller mac(pMedium);
16713 if (FAILED(mac.hrc())) return mac.hrc();
16714 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16715 DeviceType_T devType = pMedium->i_getDeviceType();
16716 if (devType == DeviceType_HardDisk)
16717 {
16718 /*
16719 * We need to move to last child because the Medium::changeEncryption
16720 * encrypts all chain of specified medium with its parents.
16721 * Also we perform cheking of back reference and children for
16722 * all media in the chain to raise error before we start any action.
16723 * So, we first move into root parent and then we will move to last child
16724 * keeping latter in the list for encryption.
16725 */
16726
16727 /* move to root parent */
16728 ComObjPtr<Medium> pTmpMedium = pMedium;
16729 while (pTmpMedium.isNotNull())
16730 {
16731 AutoCaller mediumAC(pTmpMedium);
16732 if (FAILED(mediumAC.hrc())) return mac.hrc();
16733 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16734
16735 /* Cannot encrypt media which are attached to more than one virtual machine. */
16736 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16737 if (cBackRefs > 1)
16738 return setError(VBOX_E_INVALID_OBJECT_STATE,
16739 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16740 pTmpMedium->i_getName().c_str(), cBackRefs);
16741
16742 size_t cChildren = pTmpMedium->i_getChildren().size();
16743 if (cChildren > 1)
16744 return setError(VBOX_E_INVALID_OBJECT_STATE,
16745 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16746 pTmpMedium->i_getName().c_str(), cChildren);
16747
16748 pTmpMedium = pTmpMedium->i_getParent();
16749 }
16750 /* move to last child */
16751 pTmpMedium = pMedium;
16752 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16753 {
16754 AutoCaller mediumAC(pTmpMedium);
16755 if (FAILED(mediumAC.hrc())) return mac.hrc();
16756 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16757
16758 /* Cannot encrypt media which are attached to more than one virtual machine. */
16759 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16760 if (cBackRefs > 1)
16761 return setError(VBOX_E_INVALID_OBJECT_STATE,
16762 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16763 pTmpMedium->i_getName().c_str(), cBackRefs);
16764
16765 size_t cChildren = pTmpMedium->i_getChildren().size();
16766 if (cChildren > 1)
16767 return setError(VBOX_E_INVALID_OBJECT_STATE,
16768 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16769 pTmpMedium->i_getName().c_str(), cChildren);
16770
16771 pTmpMedium = pTmpMedium->i_getChildren().front();
16772 }
16773 llMedia.push_back(pTmpMedium);
16774 }
16775 }
16776 }
16777
16778 ComObjPtr<Progress> pProgress;
16779 pProgress.createObject();
16780 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16781 static_cast<IMachine*>(this) /* aInitiator */,
16782 tr("Change encryption"),
16783 TRUE /* fCancellable */,
16784 (ULONG)(4 + + llMedia.size()), // cOperations
16785 tr("Change encryption of the mediuma"));
16786 if (FAILED(hrc))
16787 return hrc;
16788
16789 /* create and start the task on a separate thread (note that it will not
16790 * start working until we release alock) */
16791 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16792 aCurrentPassword, aCipher, aNewPassword,
16793 aNewPasswordId, aForce, llMedia);
16794 hrc = pTask->createThread();
16795 pTask = NULL;
16796 if (FAILED(hrc))
16797 return hrc;
16798
16799 pProgress.queryInterfaceTo(aProgress.asOutParam());
16800
16801 LogFlowFuncLeave();
16802
16803 return S_OK;
16804#endif
16805}
16806
16807HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16808 com::Utf8Str &aPasswordId)
16809{
16810#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16811 RT_NOREF(aCipher, aPasswordId);
16812 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16813#else
16814 AutoLimitedCaller autoCaller(this);
16815 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16816
16817 PCVBOXCRYPTOIF pCryptoIf = NULL;
16818 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16819 if (FAILED(hrc)) return hrc; /* Error is set */
16820
16821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16822
16823 if (mData->mstrKeyStore.isNotEmpty())
16824 {
16825 char *pszCipher = NULL;
16826 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16827 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16828 if (RT_SUCCESS(vrc))
16829 {
16830 aCipher = getCipherStringWithoutMode(pszCipher);
16831 RTStrFree(pszCipher);
16832 aPasswordId = mData->mstrKeyId;
16833 }
16834 else
16835 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16836 tr("Failed to query the encryption settings with %Rrc"),
16837 vrc);
16838 }
16839 else
16840 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16841
16842 mParent->i_releaseCryptoIf(pCryptoIf);
16843
16844 return hrc;
16845#endif
16846}
16847
16848HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16849{
16850#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16851 RT_NOREF(aPassword);
16852 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16853#else
16854 AutoLimitedCaller autoCaller(this);
16855 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16856
16857 PCVBOXCRYPTOIF pCryptoIf = NULL;
16858 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16859 if (FAILED(hrc)) return hrc; /* Error is set */
16860
16861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16862
16863 if (mData->mstrKeyStore.isNotEmpty())
16864 {
16865 char *pszCipher = NULL;
16866 uint8_t *pbDek = NULL;
16867 size_t cbDek = 0;
16868 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16869 &pbDek, &cbDek, &pszCipher);
16870 if (RT_SUCCESS(vrc))
16871 {
16872 RTStrFree(pszCipher);
16873 RTMemSaferFree(pbDek, cbDek);
16874 }
16875 else
16876 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16877 tr("The password supplied for the encrypted machine is incorrect"));
16878 }
16879 else
16880 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16881
16882 mParent->i_releaseCryptoIf(pCryptoIf);
16883
16884 return hrc;
16885#endif
16886}
16887
16888HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16889 const com::Utf8Str &aPassword)
16890{
16891#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16892 RT_NOREF(aId, aPassword);
16893 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16894#else
16895 AutoLimitedCaller autoCaller(this);
16896 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16897
16898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16899
16900 size_t cbPassword = aPassword.length() + 1;
16901 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16902
16903 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16904
16905 if ( mData->mAccessible
16906 && mData->mSession.mState == SessionState_Locked
16907 && mData->mSession.mLockType == LockType_VM
16908 && mData->mSession.mDirectControl != NULL)
16909 {
16910 /* get the console from the direct session */
16911 ComPtr<IConsole> console;
16912 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16913 ComAssertComRC(hrc);
16914 /* send passsword to console */
16915 console->AddEncryptionPassword(Bstr(aId).raw(),
16916 Bstr(aPassword).raw(),
16917 TRUE);
16918 }
16919
16920 if (mData->mstrKeyId == aId)
16921 {
16922 HRESULT hrc = checkEncryptionPassword(aPassword);
16923 if (FAILED(hrc))
16924 return hrc;
16925
16926 if (SUCCEEDED(hrc))
16927 {
16928 /*
16929 * Encryption is used and password is correct,
16930 * Reinit the machine if required.
16931 */
16932 BOOL fAccessible;
16933 alock.release();
16934 getAccessible(&fAccessible);
16935 alock.acquire();
16936 }
16937 }
16938
16939 /*
16940 * Add the password into the NvramStore only after
16941 * the machine becomes accessible and the NvramStore
16942 * contains key id and key store.
16943 */
16944 if (mNvramStore.isNotNull())
16945 mNvramStore->i_addPassword(aId, aPassword);
16946
16947 return S_OK;
16948#endif
16949}
16950
16951HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16952 const std::vector<com::Utf8Str> &aPasswords)
16953{
16954#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16955 RT_NOREF(aIds, aPasswords);
16956 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16957#else
16958 if (aIds.size() != aPasswords.size())
16959 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16960
16961 HRESULT hrc = S_OK;
16962 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16963 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16964
16965 return hrc;
16966#endif
16967}
16968
16969HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16970{
16971#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16972 RT_NOREF(autoCaller, aId);
16973 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16974#else
16975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16976
16977 if ( mData->mAccessible
16978 && mData->mSession.mState == SessionState_Locked
16979 && mData->mSession.mLockType == LockType_VM
16980 && mData->mSession.mDirectControl != NULL)
16981 {
16982 /* get the console from the direct session */
16983 ComPtr<IConsole> console;
16984 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16985 ComAssertComRC(hrc);
16986 /* send passsword to console */
16987 console->RemoveEncryptionPassword(Bstr(aId).raw());
16988 }
16989
16990 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16991 {
16992 if (Global::IsOnlineOrTransient(mData->mMachineState))
16993 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16994 alock.release();
16995 autoCaller.release();
16996 /* return because all passwords are purged when machine becomes inaccessible; */
16997 return i_setInaccessible();
16998 }
16999
17000 if (mNvramStore.isNotNull())
17001 mNvramStore->i_removePassword(aId);
17002 mData->mpKeyStore->deleteSecretKey(aId);
17003 return S_OK;
17004#endif
17005}
17006
17007HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17008{
17009#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17010 RT_NOREF(autoCaller);
17011 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17012#else
17013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17014
17015 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17016 {
17017 if (Global::IsOnlineOrTransient(mData->mMachineState))
17018 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17019 alock.release();
17020 autoCaller.release();
17021 /* return because all passwords are purged when machine becomes inaccessible; */
17022 return i_setInaccessible();
17023 }
17024
17025 mNvramStore->i_removeAllPasswords();
17026 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17027 return S_OK;
17028#endif
17029}
17030
17031#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17032HRESULT Machine::i_setInaccessible()
17033{
17034 if (!mData->mAccessible)
17035 return S_OK;
17036
17037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17038 VirtualBox *pParent = mParent;
17039 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17040 Guid id(i_getId());
17041
17042 alock.release();
17043
17044 uninit();
17045 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17046
17047 alock.acquire();
17048 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17049 return hrc;
17050}
17051#endif
17052
17053/* This isn't handled entirely by the wrapper generator yet. */
17054#ifdef VBOX_WITH_XPCOM
17055NS_DECL_CLASSINFO(SessionMachine)
17056NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17057
17058NS_DECL_CLASSINFO(SnapshotMachine)
17059NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17060#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