VirtualBox

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

最後變更 在這個檔案從101139是 101139,由 vboxsync 提交於 18 月 前

Main: Added new define VBOX_WITH_ARMV8_HARDCODED_DEFAULTS (enabled by default for darwin.arm64) which sets some hardcoded defaults for newly created VMs. This is needed at least for now to get new VMs off the ground. bugref:10384

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 582.3 KB
 
1/* $Id: MachineImpl.cpp 101139 2023-09-18 10:58:57Z 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.alldomusa.eu.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 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
245 mCpuIdPortabilityLevel = 0;
246 mCpuProfile = "host";
247
248 /* default boot order: floppy - DVD - HDD */
249 mBootOrder[0] = DeviceType_Floppy;
250 mBootOrder[1] = DeviceType_DVD;
251 mBootOrder[2] = DeviceType_HardDisk;
252 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
253 mBootOrder[i] = DeviceType_Null;
254
255 mClipboardMode = ClipboardMode_Disabled;
256 mClipboardFileTransfersEnabled = FALSE;
257
258 mDnDMode = DnDMode_Disabled;
259
260 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard; /** @todo BUGBUG Assumes x86! */
261 mPointingHIDType = PointingHIDType_PS2Mouse; /** @todo BUGBUG Assumes x86! */
262 mParavirtProvider = ParavirtProvider_Default;
263 mEmulatedUSBCardReaderEnabled = FALSE;
264
265 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
266 mCPUAttached[i] = false;
267
268 mIOCacheEnabled = true;
269 mIOCacheSize = 5; /* 5MB */
270}
271
272Machine::HWData::~HWData()
273{
274}
275
276/////////////////////////////////////////////////////////////////////////////
277// Machine class
278/////////////////////////////////////////////////////////////////////////////
279
280// constructor / destructor
281/////////////////////////////////////////////////////////////////////////////
282
283Machine::Machine() :
284#ifdef VBOX_WITH_RESOURCE_USAGE_API
285 mCollectorGuest(NULL),
286#endif
287 mPeer(NULL),
288 mParent(NULL),
289 mSerialPorts(),
290 mParallelPorts(),
291 uRegistryNeedsSaving(0)
292{}
293
294Machine::~Machine()
295{}
296
297HRESULT Machine::FinalConstruct()
298{
299 LogFlowThisFunc(("\n"));
300 return BaseFinalConstruct();
301}
302
303void Machine::FinalRelease()
304{
305 LogFlowThisFunc(("\n"));
306 uninit();
307 BaseFinalRelease();
308}
309
310/**
311 * Initializes a new machine instance; this init() variant creates a new, empty machine.
312 * This gets called from VirtualBox::CreateMachine().
313 *
314 * @param aParent Associated parent object.
315 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
316 * @param strName Name for the machine.
317 * @param aArchitecture Architecture to use for the machine.
318 * @param llGroups list of groups for the machine.
319 * @param strOsType OS Type string (stored as is if aOsType is NULL).
320 * @param aOsType OS Type of this machine or NULL.
321 * @param aId UUID for the new machine.
322 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
323 * @param fDirectoryIncludesUUID
324 * Whether the use a special VM directory naming scheme (includes the UUID).
325 * @param aCipher The cipher to encrypt the VM with.
326 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
327 * @param aPassword The password to encrypt the VM with.
328 *
329 * @return Success indicator. if not S_OK, the machine object is invalid.
330 */
331HRESULT Machine::init(VirtualBox *aParent,
332 const Utf8Str &strConfigFile,
333 const Utf8Str &strName,
334 PlatformArchitecture_T aArchitecture,
335 const StringsList &llGroups,
336 const Utf8Str &strOsType,
337 GuestOSType *aOsType,
338 const Guid &aId,
339 bool fForceOverwrite,
340 bool fDirectoryIncludesUUID,
341 const com::Utf8Str &aCipher,
342 const com::Utf8Str &aPasswordId,
343 const com::Utf8Str &aPassword)
344{
345 LogFlowThisFuncEnter();
346 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
347
348#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
349 RT_NOREF(aCipher);
350 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
351 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
352#endif
353
354 /* Enclose the state transition NotReady->InInit->Ready */
355 AutoInitSpan autoInitSpan(this);
356 AssertReturn(autoInitSpan.isOk(), E_FAIL);
357
358 HRESULT hrc = initImpl(aParent, strConfigFile);
359 if (FAILED(hrc)) return hrc;
360
361#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
362 com::Utf8Str strSsmKeyId;
363 com::Utf8Str strSsmKeyStore;
364 com::Utf8Str strNVRAMKeyId;
365 com::Utf8Str strNVRAMKeyStore;
366
367 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
368 {
369 /* Resolve the cryptographic interface. */
370 PCVBOXCRYPTOIF pCryptoIf = NULL;
371 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
372 if (SUCCEEDED(hrc))
373 {
374 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
375 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
376 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
377
378 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
379 {
380 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
381 if (!pszCipher)
382 {
383 hrc = setError(VBOX_E_NOT_SUPPORTED,
384 tr("The cipher '%s' is not supported"), aCipher.c_str());
385 break;
386 }
387
388 VBOXCRYPTOCTX hCryptoCtx;
389 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
390 if (RT_FAILURE(vrc))
391 {
392 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
393 break;
394 }
395
396 char *pszKeyStore;
397 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
398 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
399 AssertRC(vrc2);
400
401 if (RT_FAILURE(vrc))
402 {
403 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
404 break;
405 }
406
407 *(astrKeyStore[i]) = pszKeyStore;
408 RTMemFree(pszKeyStore);
409 *(astrKeyId[i]) = aPasswordId;
410 }
411
412 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
413 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
414
415 if (FAILED(hrc))
416 return hrc; /* Error is set. */
417 }
418 else
419 return hrc; /* Error is set. */
420 }
421#endif
422
423 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
424 if (FAILED(hrc)) return hrc;
425
426 if (SUCCEEDED(hrc))
427 {
428 // create an empty machine config
429 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
430
431 hrc = initDataAndChildObjects();
432 }
433
434 if (SUCCEEDED(hrc))
435 {
436#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
437 mSSData->strStateKeyId = strSsmKeyId;
438 mSSData->strStateKeyStore = strSsmKeyStore;
439#endif
440
441 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
442 mData->mAccessible = TRUE;
443
444 unconst(mData->mUuid) = aId;
445
446 mUserData->s.strName = strName;
447
448 if (llGroups.size())
449 mUserData->s.llGroups = llGroups;
450
451 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
452 // the "name sync" flag determines whether the machine directory gets renamed along
453 // with the machine file; say so if the settings file name is the same as the
454 // settings file parent directory (machine directory)
455 mUserData->s.fNameSync = i_isInOwnDir();
456
457 // initialize the default snapshots folder
458 hrc = COMSETTER(SnapshotFolder)(NULL);
459 AssertComRC(hrc);
460
461 if (aOsType)
462 {
463 /* Store OS type */
464 mUserData->s.strOsType = aOsType->i_id();
465 }
466 else if (!strOsType.isEmpty())
467 {
468 /* Store OS type */
469 mUserData->s.strOsType = strOsType;
470 }
471
472 /* Set the platform architecture first before applying the defaults below. */
473 hrc = mPlatform->i_initArchitecture(aArchitecture);
474 if (FAILED(hrc)) return hrc;
475
476 /* Apply platform defaults. */
477 mPlatform->i_applyDefaults(aOsType);
478 i_platformPropertiesUpdate();
479
480 /* Apply firmware defaults. */
481 mFirmwareSettings->i_applyDefaults(aOsType);
482
483 /* Apply TPM defaults. */
484 mTrustedPlatformModule->i_applyDefaults(aOsType);
485
486 /* Apply recording defaults. */
487 mRecordingSettings->i_applyDefaults();
488
489 /* Apply network adapters defaults */
490 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
491 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
492
493 /* Apply serial port defaults */
494 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
495 mSerialPorts[slot]->i_applyDefaults(aOsType);
496
497 /* Apply parallel port defaults */
498 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
499 mParallelPorts[slot]->i_applyDefaults();
500
501 /* Enable the VMMDev testing feature for bootsector VMs: */
502 if (aOsType && aOsType->i_id() == "VBoxBS_64")
503 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
504
505#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
506 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
507#endif
508
509#ifdef VBOX_WITH_ARMV8_HARDCODED_DEFAULTS
510 /* At least one serial port has to be enabled. */
511 char szPathTemp[RTPATH_MAX];
512 int vrc = RTPathTemp(szPathTemp, sizeof(szPathTemp)); /* Don't clutter the VM directory; instead use the temp dir. */
513 if (RT_SUCCESS(vrc))
514 {
515 vrc = RTPathAppend(szPathTemp, sizeof(szPathTemp), strName.c_str());
516 if (RT_SUCCESS(vrc))
517 {
518 vrc = RTStrCat(szPathTemp, sizeof(szPathTemp), "-serialPort0");
519 if (RT_SUCCESS(vrc))
520 {
521 mSerialPorts[0]->COMSETTER(Path)(Bstr(szPathTemp).raw()); /* Must be set first. */
522 mSerialPorts[0]->COMSETTER(HostMode)(PortMode_RawFile);
523 mSerialPorts[0]->COMSETTER(Enabled)(TRUE);
524 }
525 }
526 }
527 /* Always needs EFI. */
528 mFirmwareSettings->COMSETTER(FirmwareType)(FirmwareType_EFI);
529 /* Needs VMSVGA for now. */
530 mGraphicsAdapter->COMSETTER(GraphicsControllerType)(GraphicsControllerType_VMSVGA);
531 mGraphicsAdapter->COMSETTER(VRAMSize)(128 /* MB */);
532#endif /* VBOX_WITH_ARMV8_HARDCODED_DEFAULTS */
533
534 if (SUCCEEDED(hrc))
535 {
536 /* At this point the changing of the current state modification
537 * flag is allowed. */
538 i_allowStateModification();
539
540 /* commit all changes made during the initialization */
541 i_commit();
542 }
543 }
544
545 /* Confirm a successful initialization when it's the case */
546 if (SUCCEEDED(hrc))
547 {
548#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
549 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
550 {
551 size_t cbPassword = aPassword.length() + 1;
552 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
553 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
554 }
555#endif
556
557 if (mData->mAccessible)
558 autoInitSpan.setSucceeded();
559 else
560 autoInitSpan.setLimited();
561 }
562
563 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
564 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
565 mData->mRegistered,
566 mData->mAccessible,
567 hrc));
568
569 LogFlowThisFuncLeave();
570
571 return hrc;
572}
573
574/**
575 * Initializes a new instance with data from machine XML (formerly Init_Registered).
576 * Gets called in two modes:
577 *
578 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
579 * UUID is specified and we mark the machine as "registered";
580 *
581 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
582 * and the machine remains unregistered until RegisterMachine() is called.
583 *
584 * @param aParent Associated parent object
585 * @param strConfigFile Local file system path to the VM settings file (can
586 * be relative to the VirtualBox config directory).
587 * @param aId UUID of the machine or NULL (see above).
588 * @param strPassword Password for decrypting the config
589 *
590 * @return Success indicator. if not S_OK, the machine object is invalid
591 */
592HRESULT Machine::initFromSettings(VirtualBox *aParent,
593 const Utf8Str &strConfigFile,
594 const Guid *aId,
595 const com::Utf8Str &strPassword)
596{
597 LogFlowThisFuncEnter();
598 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
599
600 PCVBOXCRYPTOIF pCryptoIf = NULL;
601#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
602 if (strPassword.isNotEmpty())
603 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
604#else
605 if (strPassword.isNotEmpty())
606 {
607 /* Get at the crpytographic interface. */
608 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
609 if (FAILED(hrc))
610 return hrc; /* Error is set. */
611 }
612#endif
613
614 /* Enclose the state transition NotReady->InInit->Ready */
615 AutoInitSpan autoInitSpan(this);
616 AssertReturn(autoInitSpan.isOk(), E_FAIL);
617
618 HRESULT hrc = initImpl(aParent, strConfigFile);
619 if (FAILED(hrc)) return hrc;
620
621 if (aId)
622 {
623 // loading a registered VM:
624 unconst(mData->mUuid) = *aId;
625 mData->mRegistered = TRUE;
626 // now load the settings from XML:
627 hrc = i_registeredInit();
628 // this calls initDataAndChildObjects() and loadSettings()
629 }
630 else
631 {
632 // opening an unregistered VM (VirtualBox::OpenMachine()):
633 hrc = initDataAndChildObjects();
634 if (SUCCEEDED(hrc))
635 {
636 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
637 mData->mAccessible = TRUE;
638
639 try
640 {
641 // load and parse machine XML; this will throw on XML or logic errors
642 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
643 pCryptoIf,
644 strPassword.c_str());
645
646 // reject VM UUID duplicates, they can happen if someone
647 // tries to register an already known VM config again
648 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
649 true /* fPermitInaccessible */,
650 false /* aDoSetError */,
651 NULL) != VBOX_E_OBJECT_NOT_FOUND)
652 {
653 throw setError(E_FAIL,
654 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
655 mData->m_strConfigFile.c_str());
656 }
657
658 // use UUID from machine config
659 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
660
661#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
662 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
663 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
664 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
665 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
666#endif
667
668 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
669 {
670 // We just set the inaccessible state and fill the error info allowing the caller
671 // to register the machine with encrypted config even if the password is incorrect
672 mData->mAccessible = FALSE;
673
674 /* fetch the current error info */
675 mData->mAccessError = com::ErrorInfo();
676
677 setError(VBOX_E_PASSWORD_INCORRECT,
678 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
679 mData->pMachineConfigFile->uuid.raw());
680 }
681 else
682 {
683#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
684 if (strPassword.isNotEmpty())
685 {
686 size_t cbKey = strPassword.length() + 1; /* Include terminator */
687 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
688 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
689 }
690#endif
691
692 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
693 if (FAILED(hrc)) throw hrc;
694
695 /* At this point the changing of the current state modification
696 * flag is allowed. */
697 i_allowStateModification();
698
699 i_commit();
700 }
701 }
702 catch (HRESULT err)
703 {
704 /* we assume that error info is set by the thrower */
705 hrc = err;
706 }
707 catch (...)
708 {
709 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
710 }
711 }
712 }
713
714 /* Confirm a successful initialization when it's the case */
715 if (SUCCEEDED(hrc))
716 {
717 if (mData->mAccessible)
718 autoInitSpan.setSucceeded();
719 else
720 {
721 autoInitSpan.setLimited();
722
723 // uninit media from this machine's media registry, or else
724 // reloading the settings will fail
725 mParent->i_unregisterMachineMedia(i_getId());
726 }
727 }
728
729#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
730 if (pCryptoIf)
731 {
732 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
733 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
734 }
735#endif
736
737 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
738 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
739
740 LogFlowThisFuncLeave();
741
742 return hrc;
743}
744
745/**
746 * Initializes a new instance from a machine config that is already in memory
747 * (import OVF case). Since we are importing, the UUID in the machine
748 * config is ignored and we always generate a fresh one.
749 *
750 * @param aParent Associated parent object.
751 * @param strName Name for the new machine; this overrides what is specified in config.
752 * @param strSettingsFilename File name of .vbox file.
753 * @param config Machine configuration loaded and parsed from XML.
754 *
755 * @return Success indicator. if not S_OK, the machine object is invalid
756 */
757HRESULT Machine::init(VirtualBox *aParent,
758 const Utf8Str &strName,
759 const Utf8Str &strSettingsFilename,
760 const settings::MachineConfigFile &config)
761{
762 LogFlowThisFuncEnter();
763
764 /* Enclose the state transition NotReady->InInit->Ready */
765 AutoInitSpan autoInitSpan(this);
766 AssertReturn(autoInitSpan.isOk(), E_FAIL);
767
768 HRESULT hrc = initImpl(aParent, strSettingsFilename);
769 if (FAILED(hrc)) return hrc;
770
771 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
772 if (FAILED(hrc)) return hrc;
773
774 hrc = initDataAndChildObjects();
775 if (SUCCEEDED(hrc))
776 {
777 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
778 mData->mAccessible = TRUE;
779
780 // create empty machine config for instance data
781 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
782
783 // generate fresh UUID, ignore machine config
784 unconst(mData->mUuid).create();
785
786 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
787
788 // override VM name as well, it may be different
789 mUserData->s.strName = strName;
790
791 if (SUCCEEDED(hrc))
792 {
793 /* At this point the changing of the current state modification
794 * flag is allowed. */
795 i_allowStateModification();
796
797 /* commit all changes made during the initialization */
798 i_commit();
799 }
800 }
801
802 /* Confirm a successful initialization when it's the case */
803 if (SUCCEEDED(hrc))
804 {
805 if (mData->mAccessible)
806 autoInitSpan.setSucceeded();
807 else
808 {
809 /* Ignore all errors from unregistering, they would destroy
810- * the more interesting error information we already have,
811- * pinpointing the issue with the VM config. */
812 ErrorInfoKeeper eik;
813
814 autoInitSpan.setLimited();
815
816 // uninit media from this machine's media registry, or else
817 // reloading the settings will fail
818 mParent->i_unregisterMachineMedia(i_getId());
819 }
820 }
821
822 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
823 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
824
825 LogFlowThisFuncLeave();
826
827 return hrc;
828}
829
830/**
831 * Shared code between the various init() implementations.
832 *
833 * @returns HRESULT
834 * @param aParent The VirtualBox object.
835 * @param strConfigFile Settings file.
836 */
837HRESULT Machine::initImpl(VirtualBox *aParent,
838 const Utf8Str &strConfigFile)
839{
840 LogFlowThisFuncEnter();
841
842 AssertReturn(aParent, E_INVALIDARG);
843 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
844
845 HRESULT hrc = S_OK;
846
847 /* share the parent weakly */
848 unconst(mParent) = aParent;
849
850 /* allocate the essential machine data structure (the rest will be
851 * allocated later by initDataAndChildObjects() */
852 mData.allocate();
853
854 /* memorize the config file name (as provided) */
855 mData->m_strConfigFile = strConfigFile;
856
857 /* get the full file name */
858 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
859 if (RT_FAILURE(vrc1))
860 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
861 tr("Invalid machine settings file name '%s' (%Rrc)"),
862 strConfigFile.c_str(),
863 vrc1);
864
865#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
866 /** @todo Only create when the machine is going to be encrypted. */
867 /* Non-pageable memory is not accessible for non-VM process */
868 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
869 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
870#endif
871
872 LogFlowThisFuncLeave();
873
874 return hrc;
875}
876
877/**
878 * Tries to create a machine settings file in the path stored in the machine
879 * instance data. Used when a new machine is created to fail gracefully if
880 * the settings file could not be written (e.g. because machine dir is read-only).
881 * @return
882 */
883HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
884{
885 HRESULT hrc = S_OK;
886
887 // when we create a new machine, we must be able to create the settings file
888 RTFILE f = NIL_RTFILE;
889 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
890 if ( RT_SUCCESS(vrc)
891 || vrc == VERR_SHARING_VIOLATION
892 )
893 {
894 if (RT_SUCCESS(vrc))
895 RTFileClose(f);
896 if (!fForceOverwrite)
897 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
898 else
899 {
900 /* try to delete the config file, as otherwise the creation
901 * of a new settings file will fail. */
902 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
903 }
904 }
905 else if ( vrc != VERR_FILE_NOT_FOUND
906 && vrc != VERR_PATH_NOT_FOUND
907 )
908 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
909 mData->m_strConfigFileFull.c_str(), vrc);
910 return hrc;
911}
912
913/**
914 * Initializes the registered machine by loading the settings file.
915 * This method is separated from #init() in order to make it possible to
916 * retry the operation after VirtualBox startup instead of refusing to
917 * startup the whole VirtualBox server in case if the settings file of some
918 * registered VM is invalid or inaccessible.
919 *
920 * @note Must be always called from this object's write lock
921 * (unless called from #init() that doesn't need any locking).
922 * @note Locks the mUSBController method for writing.
923 * @note Subclasses must not call this method.
924 */
925HRESULT Machine::i_registeredInit()
926{
927 AssertReturn(!i_isSessionMachine(), E_FAIL);
928 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
929 AssertReturn(mData->mUuid.isValid(), E_FAIL);
930 AssertReturn(!mData->mAccessible, E_FAIL);
931
932 HRESULT hrc = initDataAndChildObjects();
933 if (SUCCEEDED(hrc))
934 {
935 /* Temporarily reset the registered flag in order to let setters
936 * potentially called from loadSettings() succeed (isMutable() used in
937 * all setters will return FALSE for a Machine instance if mRegistered
938 * is TRUE). */
939 mData->mRegistered = FALSE;
940
941 PCVBOXCRYPTOIF pCryptoIf = NULL;
942 SecretKey *pKey = NULL;
943 const char *pszPassword = NULL;
944#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
945 /* Resolve password and cryptographic support interface if machine is encrypted. */
946 if (mData->mstrKeyId.isNotEmpty())
947 {
948 /* Get at the crpytographic interface. */
949 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
950 if (SUCCEEDED(hrc))
951 {
952 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
953 if (RT_SUCCESS(vrc))
954 pszPassword = (const char *)pKey->getKeyBuffer();
955 else
956 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
957 mData->mstrKeyId.c_str(), vrc);
958 }
959 }
960#else
961 RT_NOREF(pKey);
962#endif
963
964 if (SUCCEEDED(hrc))
965 {
966 try
967 {
968 // load and parse machine XML; this will throw on XML or logic errors
969 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
970 pCryptoIf, pszPassword);
971
972 if (mData->mUuid != mData->pMachineConfigFile->uuid)
973 throw setError(E_FAIL,
974 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
975 mData->pMachineConfigFile->uuid.raw(),
976 mData->m_strConfigFileFull.c_str(),
977 mData->mUuid.toString().c_str(),
978 mParent->i_settingsFilePath().c_str());
979
980#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
981 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
982 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
983 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
984 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
985
986 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
987 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
988 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
989 mData->pMachineConfigFile->uuid.raw());
990 else
991#endif
992 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
993 if (FAILED(hrc)) throw hrc;
994 }
995 catch (HRESULT err)
996 {
997 /* we assume that error info is set by the thrower */
998 hrc = err;
999 }
1000 catch (...)
1001 {
1002 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1003 }
1004
1005 /* Restore the registered flag (even on failure) */
1006 mData->mRegistered = TRUE;
1007 }
1008
1009#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1010 if (pCryptoIf)
1011 mParent->i_releaseCryptoIf(pCryptoIf);
1012 if (pKey)
1013 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1014#endif
1015 }
1016
1017 if (SUCCEEDED(hrc))
1018 {
1019 /* Set mAccessible to TRUE only if we successfully locked and loaded
1020 * the settings file */
1021 mData->mAccessible = TRUE;
1022
1023 /* commit all changes made during loading the settings file */
1024 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1025 /// @todo r=klaus for some reason the settings loading logic backs up
1026 // the settings, and therefore a commit is needed. Should probably be changed.
1027 }
1028 else
1029 {
1030 /* If the machine is registered, then, instead of returning a
1031 * failure, we mark it as inaccessible and set the result to
1032 * success to give it a try later */
1033
1034 /* fetch the current error info */
1035 mData->mAccessError = com::ErrorInfo();
1036 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1037
1038 /* rollback all changes */
1039 i_rollback(false /* aNotify */);
1040
1041 // uninit media from this machine's media registry, or else
1042 // reloading the settings will fail
1043 mParent->i_unregisterMachineMedia(i_getId());
1044
1045 /* uninitialize the common part to make sure all data is reset to
1046 * default (null) values */
1047 uninitDataAndChildObjects();
1048
1049 hrc = S_OK;
1050 }
1051
1052 return hrc;
1053}
1054
1055/**
1056 * Uninitializes the instance.
1057 * Called either from FinalRelease() or by the parent when it gets destroyed.
1058 *
1059 * @note The caller of this method must make sure that this object
1060 * a) doesn't have active callers on the current thread and b) is not locked
1061 * by the current thread; otherwise uninit() will hang either a) due to
1062 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1063 * a dead-lock caused by this thread waiting for all callers on the other
1064 * threads are done but preventing them from doing so by holding a lock.
1065 */
1066void Machine::uninit()
1067{
1068 LogFlowThisFuncEnter();
1069
1070 Assert(!isWriteLockOnCurrentThread());
1071
1072 Assert(!uRegistryNeedsSaving);
1073 if (uRegistryNeedsSaving)
1074 {
1075 AutoCaller autoCaller(this);
1076 if (SUCCEEDED(autoCaller.hrc()))
1077 {
1078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1079 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1080 }
1081 }
1082
1083 /* Enclose the state transition Ready->InUninit->NotReady */
1084 AutoUninitSpan autoUninitSpan(this);
1085 if (autoUninitSpan.uninitDone())
1086 return;
1087
1088 Assert(!i_isSnapshotMachine());
1089 Assert(!i_isSessionMachine());
1090 Assert(!!mData);
1091
1092 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1093 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1094
1095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1096
1097 if (!mData->mSession.mMachine.isNull())
1098 {
1099 /* Theoretically, this can only happen if the VirtualBox server has been
1100 * terminated while there were clients running that owned open direct
1101 * sessions. Since in this case we are definitely called by
1102 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1103 * won't happen on the client watcher thread (because it has a
1104 * VirtualBox caller for the duration of the
1105 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1106 * cannot happen until the VirtualBox caller is released). This is
1107 * important, because SessionMachine::uninit() cannot correctly operate
1108 * after we return from this method (it expects the Machine instance is
1109 * still valid). We'll call it ourselves below.
1110 */
1111 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1112 (SessionMachine*)mData->mSession.mMachine));
1113
1114 if (Global::IsOnlineOrTransient(mData->mMachineState))
1115 {
1116 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1117 /* set machine state using SessionMachine reimplementation */
1118 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1119 }
1120
1121 /*
1122 * Uninitialize SessionMachine using public uninit() to indicate
1123 * an unexpected uninitialization.
1124 */
1125 mData->mSession.mMachine->uninit();
1126 /* SessionMachine::uninit() must set mSession.mMachine to null */
1127 Assert(mData->mSession.mMachine.isNull());
1128 }
1129
1130 // uninit media from this machine's media registry, if they're still there
1131 Guid uuidMachine(i_getId());
1132
1133 /* the lock is no more necessary (SessionMachine is uninitialized) */
1134 alock.release();
1135
1136 /* XXX This will fail with
1137 * "cannot be closed because it is still attached to 1 virtual machines"
1138 * because at this point we did not call uninitDataAndChildObjects() yet
1139 * and therefore also removeBackReference() for all these media was not called! */
1140
1141 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1142 mParent->i_unregisterMachineMedia(uuidMachine);
1143
1144 // has machine been modified?
1145 if (mData->flModifications)
1146 {
1147 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1148 i_rollback(false /* aNotify */);
1149 }
1150
1151 if (mData->mAccessible)
1152 uninitDataAndChildObjects();
1153
1154#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1155 if (mData->mpKeyStore != NULL)
1156 delete mData->mpKeyStore;
1157#endif
1158
1159 /* free the essential data structure last */
1160 mData.free();
1161
1162 LogFlowThisFuncLeave();
1163}
1164
1165// Wrapped IMachine properties
1166/////////////////////////////////////////////////////////////////////////////
1167HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1168{
1169 /* mParent is constant during life time, no need to lock */
1170 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1171 aParent = pVirtualBox;
1172
1173 return S_OK;
1174}
1175
1176HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1177{
1178 /* mPlatform is constant during life time, no need to lock */
1179 ComObjPtr<Platform> pPlatform(mPlatform);
1180 aPlatform = pPlatform;
1181
1182 return S_OK;
1183}
1184
1185HRESULT Machine::getAccessible(BOOL *aAccessible)
1186{
1187 /* In some cases (medium registry related), it is necessary to be able to
1188 * go through the list of all machines. Happens when an inaccessible VM
1189 * has a sensible medium registry. */
1190 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT hrc = S_OK;
1194
1195 if (!mData->mAccessible)
1196 {
1197 /* try to initialize the VM once more if not accessible */
1198
1199 AutoReinitSpan autoReinitSpan(this);
1200 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1201
1202#ifdef DEBUG
1203 LogFlowThisFunc(("Dumping media backreferences\n"));
1204 mParent->i_dumpAllBackRefs();
1205#endif
1206
1207 if (mData->pMachineConfigFile)
1208 {
1209 // reset the XML file to force loadSettings() (called from i_registeredInit())
1210 // to parse it again; the file might have changed
1211 delete mData->pMachineConfigFile;
1212 mData->pMachineConfigFile = NULL;
1213 }
1214
1215 hrc = i_registeredInit();
1216
1217 if (SUCCEEDED(hrc) && mData->mAccessible)
1218 {
1219 autoReinitSpan.setSucceeded();
1220
1221 /* make sure interesting parties will notice the accessibility
1222 * state change */
1223 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1224 mParent->i_onMachineDataChanged(mData->mUuid);
1225 }
1226 }
1227
1228 if (SUCCEEDED(hrc))
1229 *aAccessible = mData->mAccessible;
1230
1231 LogFlowThisFuncLeave();
1232
1233 return hrc;
1234}
1235
1236HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1237{
1238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1239
1240 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1241 {
1242 /* return shortly */
1243 aAccessError = NULL;
1244 return S_OK;
1245 }
1246
1247 HRESULT hrc = S_OK;
1248
1249 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1250 hrc = errorInfo.createObject();
1251 if (SUCCEEDED(hrc))
1252 {
1253 errorInfo->init(mData->mAccessError.getResultCode(),
1254 mData->mAccessError.getInterfaceID().ref(),
1255 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1256 Utf8Str(mData->mAccessError.getText()));
1257 aAccessError = errorInfo;
1258 }
1259
1260 return hrc;
1261}
1262
1263HRESULT Machine::getName(com::Utf8Str &aName)
1264{
1265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1266
1267 aName = mUserData->s.strName;
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::setName(const com::Utf8Str &aName)
1273{
1274 // prohibit setting a UUID only as the machine name, or else it can
1275 // never be found by findMachine()
1276 Guid test(aName);
1277
1278 if (test.isValid())
1279 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1280
1281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1284 if (FAILED(hrc)) return hrc;
1285
1286 i_setModified(IsModified_MachineData);
1287 mUserData.backup();
1288 mUserData->s.strName = aName;
1289
1290 return S_OK;
1291}
1292
1293HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1294{
1295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1296
1297 aDescription = mUserData->s.strDescription;
1298
1299 return S_OK;
1300}
1301
1302HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1303{
1304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1305
1306 // this can be done in principle in any state as it doesn't affect the VM
1307 // significantly, but play safe by not messing around while complex
1308 // activities are going on
1309 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1310 if (FAILED(hrc)) return hrc;
1311
1312 i_setModified(IsModified_MachineData);
1313 mUserData.backup();
1314 mUserData->s.strDescription = aDescription;
1315
1316 return S_OK;
1317}
1318
1319HRESULT Machine::getId(com::Guid &aId)
1320{
1321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1322
1323 aId = mData->mUuid;
1324
1325 return S_OK;
1326}
1327
1328HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1329{
1330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1331 aGroups.resize(mUserData->s.llGroups.size());
1332 size_t i = 0;
1333 for (StringsList::const_iterator
1334 it = mUserData->s.llGroups.begin();
1335 it != mUserData->s.llGroups.end();
1336 ++it, ++i)
1337 aGroups[i] = (*it);
1338
1339 return S_OK;
1340}
1341
1342HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1343{
1344 StringsList llGroups;
1345 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1346 if (FAILED(hrc))
1347 return hrc;
1348
1349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1350
1351 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1352 if (FAILED(hrc)) return hrc;
1353
1354 i_setModified(IsModified_MachineData);
1355 mUserData.backup();
1356 mUserData->s.llGroups = llGroups;
1357
1358 mParent->i_onMachineGroupsChanged(mData->mUuid);
1359 return S_OK;
1360}
1361
1362HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1363{
1364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1365
1366 aOSTypeId = mUserData->s.strOsType;
1367
1368 return S_OK;
1369}
1370
1371HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1372{
1373 /* look up the object by Id to check it is valid */
1374 ComObjPtr<GuestOSType> pGuestOSType;
1375 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1376
1377 /* when setting, always use the "etalon" value for consistency -- lookup
1378 * by ID is case-insensitive and the input value may have different case */
1379 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1380
1381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1384 if (FAILED(hrc)) return hrc;
1385
1386 i_setModified(IsModified_MachineData);
1387 mUserData.backup();
1388 mUserData->s.strOsType = osTypeId;
1389
1390 return S_OK;
1391}
1392
1393HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1394{
1395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1398
1399 return S_OK;
1400}
1401
1402HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1403{
1404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1405
1406 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1407 if (FAILED(hrc)) return hrc;
1408
1409 i_setModified(IsModified_MachineData);
1410 mHWData.backup();
1411 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1417{
1418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 *aPointingHIDType = mHWData->mPointingHIDType;
1421
1422 return S_OK;
1423}
1424
1425HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1426{
1427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1430 if (FAILED(hrc)) return hrc;
1431
1432 i_setModified(IsModified_MachineData);
1433 mHWData.backup();
1434 mHWData->mPointingHIDType = aPointingHIDType;
1435
1436 return S_OK;
1437}
1438
1439HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1440{
1441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1442
1443 aParavirtDebug = mHWData->mParavirtDebug;
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1448{
1449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1452 if (FAILED(hrc)) return hrc;
1453
1454 /** @todo Parse/validate options? */
1455 if (aParavirtDebug != mHWData->mParavirtDebug)
1456 {
1457 i_setModified(IsModified_MachineData);
1458 mHWData.backup();
1459 mHWData->mParavirtDebug = aParavirtDebug;
1460 }
1461
1462 return S_OK;
1463}
1464
1465HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1466{
1467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1468
1469 *aParavirtProvider = mHWData->mParavirtProvider;
1470
1471 return S_OK;
1472}
1473
1474HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1475{
1476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1477
1478 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1479 if (FAILED(hrc)) return hrc;
1480
1481 if (aParavirtProvider != mHWData->mParavirtProvider)
1482 {
1483 i_setModified(IsModified_MachineData);
1484 mHWData.backup();
1485 mHWData->mParavirtProvider = aParavirtProvider;
1486 }
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1492{
1493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
1495 *aParavirtProvider = mHWData->mParavirtProvider;
1496 switch (mHWData->mParavirtProvider)
1497 {
1498 case ParavirtProvider_None:
1499 case ParavirtProvider_HyperV:
1500 case ParavirtProvider_KVM:
1501 case ParavirtProvider_Minimal:
1502 break;
1503
1504 /* Resolve dynamic provider types to the effective types. */
1505 default:
1506 {
1507 ComObjPtr<GuestOSType> pGuestOSType;
1508 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1509 pGuestOSType);
1510 if (FAILED(hrc2) || pGuestOSType.isNull())
1511 {
1512 *aParavirtProvider = ParavirtProvider_None;
1513 break;
1514 }
1515
1516 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1517 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1518
1519 switch (mHWData->mParavirtProvider)
1520 {
1521 case ParavirtProvider_Legacy:
1522 {
1523 if (fOsXGuest)
1524 *aParavirtProvider = ParavirtProvider_Minimal;
1525 else
1526 *aParavirtProvider = ParavirtProvider_None;
1527 break;
1528 }
1529
1530 case ParavirtProvider_Default:
1531 {
1532 if (fOsXGuest)
1533 *aParavirtProvider = ParavirtProvider_Minimal;
1534 else if ( mUserData->s.strOsType == "Windows11_64"
1535 || mUserData->s.strOsType == "Windows10"
1536 || mUserData->s.strOsType == "Windows10_64"
1537 || mUserData->s.strOsType == "Windows81"
1538 || mUserData->s.strOsType == "Windows81_64"
1539 || mUserData->s.strOsType == "Windows8"
1540 || mUserData->s.strOsType == "Windows8_64"
1541 || mUserData->s.strOsType == "Windows7"
1542 || mUserData->s.strOsType == "Windows7_64"
1543 || mUserData->s.strOsType == "WindowsVista"
1544 || mUserData->s.strOsType == "WindowsVista_64"
1545 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1546 || mUserData->s.strOsType.startsWith("Windows201"))
1547 && mUserData->s.strOsType.endsWith("_64"))
1548 || mUserData->s.strOsType == "Windows2012"
1549 || mUserData->s.strOsType == "Windows2012_64"
1550 || mUserData->s.strOsType == "Windows2008"
1551 || mUserData->s.strOsType == "Windows2008_64")
1552 {
1553 *aParavirtProvider = ParavirtProvider_HyperV;
1554 }
1555 else if (guestTypeFamilyId == "Linux" &&
1556 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1557 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1558 mUserData->s.strOsType != "Linux24_64")
1559 {
1560 *aParavirtProvider = ParavirtProvider_KVM;
1561 }
1562 else
1563 *aParavirtProvider = ParavirtProvider_None;
1564 break;
1565 }
1566
1567 default: AssertFailedBreak(); /* Shut up MSC. */
1568 }
1569 break;
1570 }
1571 }
1572
1573 Assert( *aParavirtProvider == ParavirtProvider_None
1574 || *aParavirtProvider == ParavirtProvider_Minimal
1575 || *aParavirtProvider == ParavirtProvider_HyperV
1576 || *aParavirtProvider == ParavirtProvider_KVM);
1577 return S_OK;
1578}
1579
1580HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1581{
1582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 aHardwareVersion = mHWData->mHWVersion;
1585
1586 return S_OK;
1587}
1588
1589HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1590{
1591 /* check known version */
1592 Utf8Str hwVersion = aHardwareVersion;
1593 if ( hwVersion.compare("1") != 0
1594 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1595 return setError(E_INVALIDARG,
1596 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1597
1598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1599
1600 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1601 if (FAILED(hrc)) return hrc;
1602
1603 i_setModified(IsModified_MachineData);
1604 mHWData.backup();
1605 mHWData->mHWVersion = aHardwareVersion;
1606
1607 return S_OK;
1608}
1609
1610HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1611{
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 if (!mHWData->mHardwareUUID.isZero())
1615 aHardwareUUID = mHWData->mHardwareUUID;
1616 else
1617 aHardwareUUID = mData->mUuid;
1618
1619 return S_OK;
1620}
1621
1622HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1623{
1624 if (!aHardwareUUID.isValid())
1625 return E_INVALIDARG;
1626
1627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1628
1629 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1630 if (FAILED(hrc)) return hrc;
1631
1632 i_setModified(IsModified_MachineData);
1633 mHWData.backup();
1634 if (aHardwareUUID == mData->mUuid)
1635 mHWData->mHardwareUUID.clear();
1636 else
1637 mHWData->mHardwareUUID = aHardwareUUID;
1638
1639 return S_OK;
1640}
1641
1642HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1643{
1644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1645
1646 *aMemorySize = mHWData->mMemorySize;
1647
1648 return S_OK;
1649}
1650
1651HRESULT Machine::setMemorySize(ULONG aMemorySize)
1652{
1653 /* check RAM limits */
1654 if ( aMemorySize < MM_RAM_MIN_IN_MB
1655 || aMemorySize > MM_RAM_MAX_IN_MB
1656 )
1657 return setError(E_INVALIDARG,
1658 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1659 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1660
1661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
1663 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1664 if (FAILED(hrc)) return hrc;
1665
1666 i_setModified(IsModified_MachineData);
1667 mHWData.backup();
1668 mHWData->mMemorySize = aMemorySize;
1669
1670 return S_OK;
1671}
1672
1673HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1674{
1675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 *aCPUCount = mHWData->mCPUCount;
1678
1679 return S_OK;
1680}
1681
1682HRESULT Machine::setCPUCount(ULONG aCPUCount)
1683{
1684 /* check CPU limits */
1685 if ( aCPUCount < SchemaDefs::MinCPUCount
1686 || aCPUCount > SchemaDefs::MaxCPUCount
1687 )
1688 return setError(E_INVALIDARG,
1689 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1690 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1691
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1695 if (mHWData->mCPUHotPlugEnabled)
1696 {
1697 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1698 {
1699 if (mHWData->mCPUAttached[idx])
1700 return setError(E_INVALIDARG,
1701 tr("There is still a CPU attached to socket %lu."
1702 "Detach the CPU before removing the socket"),
1703 aCPUCount, idx+1);
1704 }
1705 }
1706
1707 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1708 if (FAILED(hrc)) return hrc;
1709
1710 i_setModified(IsModified_MachineData);
1711 mHWData.backup();
1712 mHWData->mCPUCount = aCPUCount;
1713
1714 return S_OK;
1715}
1716
1717HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1718{
1719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1727{
1728 /* check throttle limits */
1729 if ( aCPUExecutionCap < 1
1730 || aCPUExecutionCap > 100
1731 )
1732 return setError(E_INVALIDARG,
1733 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1734 aCPUExecutionCap, 1, 100);
1735
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1739 if (FAILED(hrc)) return hrc;
1740
1741 alock.release();
1742 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1743 alock.acquire();
1744 if (FAILED(hrc)) return hrc;
1745
1746 i_setModified(IsModified_MachineData);
1747 mHWData.backup();
1748 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1749
1750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1751 if (Global::IsOnline(mData->mMachineState))
1752 i_saveSettings(NULL, alock);
1753
1754 return S_OK;
1755}
1756
1757HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1758{
1759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1762
1763 return S_OK;
1764}
1765
1766HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1767{
1768 HRESULT hrc = S_OK;
1769
1770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 hrc = i_checkStateDependency(MutableStateDep);
1773 if (FAILED(hrc)) return hrc;
1774
1775 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1776 {
1777 if (aCPUHotPlugEnabled)
1778 {
1779 i_setModified(IsModified_MachineData);
1780 mHWData.backup();
1781
1782 /* Add the amount of CPUs currently attached */
1783 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1784 mHWData->mCPUAttached[i] = true;
1785 }
1786 else
1787 {
1788 /*
1789 * We can disable hotplug only if the amount of maximum CPUs is equal
1790 * to the amount of attached CPUs
1791 */
1792 unsigned cCpusAttached = 0;
1793 unsigned iHighestId = 0;
1794
1795 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1796 {
1797 if (mHWData->mCPUAttached[i])
1798 {
1799 cCpusAttached++;
1800 iHighestId = i;
1801 }
1802 }
1803
1804 if ( (cCpusAttached != mHWData->mCPUCount)
1805 || (iHighestId >= mHWData->mCPUCount))
1806 return setError(E_INVALIDARG,
1807 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1808
1809 i_setModified(IsModified_MachineData);
1810 mHWData.backup();
1811 }
1812 }
1813
1814 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1815
1816 return hrc;
1817}
1818
1819HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1820{
1821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1824
1825 return S_OK;
1826}
1827
1828HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1829{
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1833 if (SUCCEEDED(hrc))
1834 {
1835 i_setModified(IsModified_MachineData);
1836 mHWData.backup();
1837 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1838 }
1839 return hrc;
1840}
1841
1842HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1843{
1844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1845 aCPUProfile = mHWData->mCpuProfile;
1846 return S_OK;
1847}
1848
1849HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1850{
1851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1852 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1853 if (SUCCEEDED(hrc))
1854 {
1855 i_setModified(IsModified_MachineData);
1856 mHWData.backup();
1857 /* Empty equals 'host'. */
1858 if (aCPUProfile.isNotEmpty())
1859 mHWData->mCpuProfile = aCPUProfile;
1860 else
1861 mHWData->mCpuProfile = "host";
1862 }
1863 return hrc;
1864}
1865
1866HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1867{
1868#ifdef VBOX_WITH_USB_CARDREADER
1869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1872
1873 return S_OK;
1874#else
1875 NOREF(aEmulatedUSBCardReaderEnabled);
1876 return E_NOTIMPL;
1877#endif
1878}
1879
1880HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1881{
1882#ifdef VBOX_WITH_USB_CARDREADER
1883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1886 if (FAILED(hrc)) return hrc;
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1891
1892 return S_OK;
1893#else
1894 NOREF(aEmulatedUSBCardReaderEnabled);
1895 return E_NOTIMPL;
1896#endif
1897}
1898
1899/** @todo this method should not be public */
1900HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1901{
1902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1903
1904 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1905
1906 return S_OK;
1907}
1908
1909/**
1910 * Set the memory balloon size.
1911 *
1912 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1913 * we have to make sure that we never call IGuest from here.
1914 */
1915HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1916{
1917 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1918#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1919 /* check limits */
1920 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1921 return setError(E_INVALIDARG,
1922 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1923 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1924
1925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1928 if (FAILED(hrc)) return hrc;
1929
1930 i_setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1933
1934 return S_OK;
1935#else
1936 NOREF(aMemoryBalloonSize);
1937 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1938#endif
1939}
1940
1941HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1942{
1943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1944
1945 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1946 return S_OK;
1947}
1948
1949HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1950{
1951#ifdef VBOX_WITH_PAGE_SHARING
1952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1955 if (FAILED(hrc)) return hrc;
1956
1957 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1958 i_setModified(IsModified_MachineData);
1959 mHWData.backup();
1960 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1961 return S_OK;
1962#else
1963 NOREF(aPageFusionEnabled);
1964 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1965#endif
1966}
1967
1968HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1969{
1970 /* mFirmwareSettings is constant during life time, no need to lock */
1971 aFirmwareSettings = mFirmwareSettings;
1972
1973 return S_OK;
1974}
1975
1976HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1977{
1978 /* mTrustedPlatformModule is constant during life time, no need to lock */
1979 aTrustedPlatformModule = mTrustedPlatformModule;
1980
1981 return S_OK;
1982}
1983
1984HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1985{
1986 /* mNvramStore is constant during life time, no need to lock */
1987 aNvramStore = mNvramStore;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 aRecordingSettings = mRecordingSettings;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2002{
2003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 aGraphicsAdapter = mGraphicsAdapter;
2006
2007 return S_OK;
2008}
2009
2010HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2011{
2012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2013
2014 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2015
2016 return S_OK;
2017}
2018
2019HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2020{
2021 /** @todo (r=dmik):
2022 * 1. Allow to change the name of the snapshot folder containing snapshots
2023 * 2. Rename the folder on disk instead of just changing the property
2024 * value (to be smart and not to leave garbage). Note that it cannot be
2025 * done here because the change may be rolled back. Thus, the right
2026 * place is #saveSettings().
2027 */
2028
2029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2032 if (FAILED(hrc)) return hrc;
2033
2034 if (!mData->mCurrentSnapshot.isNull())
2035 return setError(E_FAIL,
2036 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2037
2038 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2039
2040 if (strSnapshotFolder.isEmpty())
2041 strSnapshotFolder = "Snapshots";
2042 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2043 if (RT_FAILURE(vrc))
2044 return setErrorBoth(E_FAIL, vrc,
2045 tr("Invalid snapshot folder '%s' (%Rrc)"),
2046 strSnapshotFolder.c_str(), vrc);
2047
2048 i_setModified(IsModified_MachineData);
2049 mUserData.backup();
2050
2051 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2052
2053 return S_OK;
2054}
2055
2056HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2057{
2058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 aMediumAttachments.resize(mMediumAttachments->size());
2061 size_t i = 0;
2062 for (MediumAttachmentList::const_iterator
2063 it = mMediumAttachments->begin();
2064 it != mMediumAttachments->end();
2065 ++it, ++i)
2066 aMediumAttachments[i] = *it;
2067
2068 return S_OK;
2069}
2070
2071HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2072{
2073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2074
2075 Assert(!!mVRDEServer);
2076
2077 aVRDEServer = mVRDEServer;
2078
2079 return S_OK;
2080}
2081
2082HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2083{
2084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2085
2086 aAudioSettings = mAudioSettings;
2087
2088 return S_OK;
2089}
2090
2091HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2092{
2093#ifdef VBOX_WITH_VUSB
2094 clearError();
2095 MultiResult hrcMult(S_OK);
2096
2097# ifdef VBOX_WITH_USB
2098 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2099 if (FAILED(hrcMult)) return hrcMult;
2100# endif
2101
2102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2103
2104 aUSBControllers.resize(mUSBControllers->size());
2105 size_t i = 0;
2106 for (USBControllerList::const_iterator
2107 it = mUSBControllers->begin();
2108 it != mUSBControllers->end();
2109 ++it, ++i)
2110 aUSBControllers[i] = *it;
2111
2112 return S_OK;
2113#else
2114 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2115 * extended error info to indicate that USB is simply not available
2116 * (w/o treating it as a failure), for example, as in OSE */
2117 NOREF(aUSBControllers);
2118 ReturnComNotImplemented();
2119#endif /* VBOX_WITH_VUSB */
2120}
2121
2122HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2123{
2124#ifdef VBOX_WITH_VUSB
2125 clearError();
2126 MultiResult hrcMult(S_OK);
2127
2128# ifdef VBOX_WITH_USB
2129 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2130 if (FAILED(hrcMult)) return hrcMult;
2131# endif
2132
2133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 aUSBDeviceFilters = mUSBDeviceFilters;
2136 return hrcMult;
2137#else
2138 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2139 * extended error info to indicate that USB is simply not available
2140 * (w/o treating it as a failure), for example, as in OSE */
2141 NOREF(aUSBDeviceFilters);
2142 ReturnComNotImplemented();
2143#endif /* VBOX_WITH_VUSB */
2144}
2145
2146HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2147{
2148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2149
2150 aSettingsFilePath = mData->m_strConfigFileFull;
2151
2152 return S_OK;
2153}
2154
2155HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2156{
2157 RT_NOREF(aSettingsFilePath);
2158 ReturnComNotImplemented();
2159}
2160
2161HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2162{
2163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2166 if (FAILED(hrc)) return hrc;
2167
2168 if (!mData->pMachineConfigFile->fileExists())
2169 // this is a new machine, and no config file exists yet:
2170 *aSettingsModified = TRUE;
2171 else
2172 *aSettingsModified = (mData->flModifications != 0);
2173
2174 return S_OK;
2175}
2176
2177HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2178{
2179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2180
2181 *aSessionState = mData->mSession.mState;
2182
2183 return S_OK;
2184}
2185
2186HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2187{
2188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 aSessionName = mData->mSession.mName;
2191
2192 return S_OK;
2193}
2194
2195HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2196{
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 *aSessionPID = mData->mSession.mPID;
2200
2201 return S_OK;
2202}
2203
2204HRESULT Machine::getState(MachineState_T *aState)
2205{
2206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 *aState = mData->mMachineState;
2209 Assert(mData->mMachineState != MachineState_Null);
2210
2211 return S_OK;
2212}
2213
2214HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2215{
2216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2217
2218 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2219
2220 return S_OK;
2221}
2222
2223HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2224{
2225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2226
2227 aStateFilePath = mSSData->strStateFilePath;
2228
2229 return S_OK;
2230}
2231
2232HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2233{
2234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2235
2236 i_getLogFolder(aLogFolder);
2237
2238 return S_OK;
2239}
2240
2241HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2242{
2243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 aCurrentSnapshot = mData->mCurrentSnapshot;
2246
2247 return S_OK;
2248}
2249
2250HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2251{
2252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2255 ? 0
2256 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2257
2258 return S_OK;
2259}
2260
2261HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2262{
2263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2264
2265 /* Note: for machines with no snapshots, we always return FALSE
2266 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2267 * reasons :) */
2268
2269 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2270 ? FALSE
2271 : mData->mCurrentStateModified;
2272
2273 return S_OK;
2274}
2275
2276HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2277{
2278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2279
2280 aSharedFolders.resize(mHWData->mSharedFolders.size());
2281 size_t i = 0;
2282 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2283 it = mHWData->mSharedFolders.begin();
2284 it != mHWData->mSharedFolders.end();
2285 ++it, ++i)
2286 aSharedFolders[i] = *it;
2287
2288 return S_OK;
2289}
2290
2291HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2292{
2293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 *aClipboardMode = mHWData->mClipboardMode;
2296
2297 return S_OK;
2298}
2299
2300HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2301{
2302 HRESULT hrc = S_OK;
2303
2304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2305
2306 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2307 if (FAILED(hrc)) return hrc;
2308
2309 alock.release();
2310 hrc = i_onClipboardModeChange(aClipboardMode);
2311 alock.acquire();
2312 if (FAILED(hrc)) return hrc;
2313
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mClipboardMode = aClipboardMode;
2317
2318 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2319 if (Global::IsOnline(mData->mMachineState))
2320 i_saveSettings(NULL, alock);
2321
2322 return S_OK;
2323}
2324
2325HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2326{
2327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2330
2331 return S_OK;
2332}
2333
2334HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2335{
2336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2337
2338 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2339 if (FAILED(hrc)) return hrc;
2340
2341 alock.release();
2342 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2343 alock.acquire();
2344 if (FAILED(hrc)) return hrc;
2345
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2349
2350 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2351 if (Global::IsOnline(mData->mMachineState))
2352 i_saveSettings(NULL, alock);
2353
2354 return S_OK;
2355}
2356
2357HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2358{
2359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 *aDnDMode = mHWData->mDnDMode;
2362
2363 return S_OK;
2364}
2365
2366HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2367{
2368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2369
2370 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2371 if (FAILED(hrc)) return hrc;
2372
2373 alock.release();
2374 hrc = i_onDnDModeChange(aDnDMode);
2375
2376 alock.acquire();
2377 if (FAILED(hrc)) return hrc;
2378
2379 i_setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mDnDMode = aDnDMode;
2382
2383 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2384 if (Global::IsOnline(mData->mMachineState))
2385 i_saveSettings(NULL, alock);
2386
2387 return S_OK;
2388}
2389
2390HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2391{
2392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2393
2394 aStorageControllers.resize(mStorageControllers->size());
2395 size_t i = 0;
2396 for (StorageControllerList::const_iterator
2397 it = mStorageControllers->begin();
2398 it != mStorageControllers->end();
2399 ++it, ++i)
2400 aStorageControllers[i] = *it;
2401
2402 return S_OK;
2403}
2404
2405HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2406{
2407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 *aEnabled = mUserData->s.fTeleporterEnabled;
2410
2411 return S_OK;
2412}
2413
2414HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2415{
2416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 /* Only allow it to be set to true when PoweredOff or Aborted.
2419 (Clearing it is always permitted.) */
2420 if ( aTeleporterEnabled
2421 && mData->mRegistered
2422 && ( !i_isSessionMachine()
2423 || ( mData->mMachineState != MachineState_PoweredOff
2424 && mData->mMachineState != MachineState_Teleported
2425 && mData->mMachineState != MachineState_Aborted
2426 )
2427 )
2428 )
2429 return setError(VBOX_E_INVALID_VM_STATE,
2430 tr("The machine is not powered off (state is %s)"),
2431 Global::stringifyMachineState(mData->mMachineState));
2432
2433 i_setModified(IsModified_MachineData);
2434 mUserData.backup();
2435 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2436
2437 return S_OK;
2438}
2439
2440HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2441{
2442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2445
2446 return S_OK;
2447}
2448
2449HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2450{
2451 if (aTeleporterPort >= _64K)
2452 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2453
2454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2455
2456 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2457 if (FAILED(hrc)) return hrc;
2458
2459 i_setModified(IsModified_MachineData);
2460 mUserData.backup();
2461 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2462
2463 return S_OK;
2464}
2465
2466HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2467{
2468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2471
2472 return S_OK;
2473}
2474
2475HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2476{
2477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2480 if (FAILED(hrc)) return hrc;
2481
2482 i_setModified(IsModified_MachineData);
2483 mUserData.backup();
2484 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2485
2486 return S_OK;
2487}
2488
2489HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2490{
2491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2492 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2493
2494 return S_OK;
2495}
2496
2497HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2498{
2499 /*
2500 * Hash the password first.
2501 */
2502 com::Utf8Str aT = aTeleporterPassword;
2503
2504 if (!aT.isEmpty())
2505 {
2506 if (VBoxIsPasswordHashed(&aT))
2507 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2508 VBoxHashPassword(&aT);
2509 }
2510
2511 /*
2512 * Do the update.
2513 */
2514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2515 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2516 if (SUCCEEDED(hrc))
2517 {
2518 i_setModified(IsModified_MachineData);
2519 mUserData.backup();
2520 mUserData->s.strTeleporterPassword = aT;
2521 }
2522
2523 return hrc;
2524}
2525
2526HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2527{
2528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2529
2530 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2531
2532 return S_OK;
2533}
2534
2535HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2536{
2537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2540 if (FAILED(hrc)) return hrc;
2541
2542 i_setModified(IsModified_MachineData);
2543 mHWData.backup();
2544 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2550{
2551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2552
2553 *aIOCacheSize = mHWData->mIOCacheSize;
2554
2555 return S_OK;
2556}
2557
2558HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2559{
2560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2563 if (FAILED(hrc)) return hrc;
2564
2565 i_setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mIOCacheSize = aIOCacheSize;
2568
2569 return S_OK;
2570}
2571
2572HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2573{
2574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2575
2576#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2577 aKeyId = mSSData->strStateKeyId;
2578#else
2579 aKeyId = com::Utf8Str::Empty;
2580#endif
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2586{
2587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2590 aKeyStore = mSSData->strStateKeyStore;
2591#else
2592 aKeyStore = com::Utf8Str::Empty;
2593#endif
2594
2595 return S_OK;
2596}
2597
2598HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2599{
2600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2603 aKeyId = mData->mstrLogKeyId;
2604#else
2605 aKeyId = com::Utf8Str::Empty;
2606#endif
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2616 aKeyStore = mData->mstrLogKeyStore;
2617#else
2618 aKeyStore = com::Utf8Str::Empty;
2619#endif
2620
2621 return S_OK;
2622}
2623
2624HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2625{
2626 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2627
2628 return S_OK;
2629}
2630
2631
2632/**
2633 * @note Locks objects!
2634 */
2635HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2636 LockType_T aLockType)
2637{
2638 /* check the session state */
2639 SessionState_T state;
2640 HRESULT hrc = aSession->COMGETTER(State)(&state);
2641 if (FAILED(hrc)) return hrc;
2642
2643 if (state != SessionState_Unlocked)
2644 return setError(VBOX_E_INVALID_OBJECT_STATE,
2645 tr("The given session is busy"));
2646
2647 // get the client's IInternalSessionControl interface
2648 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2649 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2650 E_INVALIDARG);
2651
2652 // session name (only used in some code paths)
2653 Utf8Str strSessionName;
2654
2655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 if (!mData->mRegistered)
2658 return setError(E_UNEXPECTED,
2659 tr("The machine '%s' is not registered"),
2660 mUserData->s.strName.c_str());
2661
2662 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2663
2664 SessionState_T oldState = mData->mSession.mState;
2665 /* Hack: in case the session is closing and there is a progress object
2666 * which allows waiting for the session to be closed, take the opportunity
2667 * and do a limited wait (max. 1 second). This helps a lot when the system
2668 * is busy and thus session closing can take a little while. */
2669 if ( mData->mSession.mState == SessionState_Unlocking
2670 && mData->mSession.mProgress)
2671 {
2672 alock.release();
2673 mData->mSession.mProgress->WaitForCompletion(1000);
2674 alock.acquire();
2675 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2676 }
2677
2678 // try again now
2679 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2680 // (i.e. session machine exists)
2681 && (aLockType == LockType_Shared) // caller wants a shared link to the
2682 // existing session that holds the write lock:
2683 )
2684 {
2685 // OK, share the session... we are now dealing with three processes:
2686 // 1) VBoxSVC (where this code runs);
2687 // 2) process C: the caller's client process (who wants a shared session);
2688 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2689
2690 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2691 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2692 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2693 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2694 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2695
2696 /*
2697 * Release the lock before calling the client process. It's safe here
2698 * since the only thing to do after we get the lock again is to add
2699 * the remote control to the list (which doesn't directly influence
2700 * anything).
2701 */
2702 alock.release();
2703
2704 // get the console of the session holding the write lock (this is a remote call)
2705 ComPtr<IConsole> pConsoleW;
2706 if (mData->mSession.mLockType == LockType_VM)
2707 {
2708 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2709 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2710 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2711 if (FAILED(hrc))
2712 // the failure may occur w/o any error info (from RPC), so provide one
2713 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2714 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2715 }
2716
2717 // share the session machine and W's console with the caller's session
2718 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2719 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2720 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2721
2722 if (FAILED(hrc))
2723 // the failure may occur w/o any error info (from RPC), so provide one
2724 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2725 alock.acquire();
2726
2727 // need to revalidate the state after acquiring the lock again
2728 if (mData->mSession.mState != SessionState_Locked)
2729 {
2730 pSessionControl->Uninitialize();
2731 return setError(VBOX_E_INVALID_SESSION_STATE,
2732 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2733 mUserData->s.strName.c_str());
2734 }
2735
2736 // add the caller's session to the list
2737 mData->mSession.mRemoteControls.push_back(pSessionControl);
2738 }
2739 else if ( mData->mSession.mState == SessionState_Locked
2740 || mData->mSession.mState == SessionState_Unlocking
2741 )
2742 {
2743 // sharing not permitted, or machine still unlocking:
2744 return setError(VBOX_E_INVALID_OBJECT_STATE,
2745 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2746 mUserData->s.strName.c_str());
2747 }
2748 else
2749 {
2750 // machine is not locked: then write-lock the machine (create the session machine)
2751
2752 // must not be busy
2753 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2754
2755 // get the caller's session PID
2756 RTPROCESS pid = NIL_RTPROCESS;
2757 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2758 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2759 Assert(pid != NIL_RTPROCESS);
2760
2761 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2762
2763 if (fLaunchingVMProcess)
2764 {
2765 if (mData->mSession.mPID == NIL_RTPROCESS)
2766 {
2767 // two or more clients racing for a lock, the one which set the
2768 // session state to Spawning will win, the others will get an
2769 // error as we can't decide here if waiting a little would help
2770 // (only for shared locks this would avoid an error)
2771 return setError(VBOX_E_INVALID_OBJECT_STATE,
2772 tr("The machine '%s' already has a lock request pending"),
2773 mUserData->s.strName.c_str());
2774 }
2775
2776 // this machine is awaiting for a spawning session to be opened:
2777 // then the calling process must be the one that got started by
2778 // LaunchVMProcess()
2779
2780 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2781 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2782
2783#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2784 /* Hardened windows builds spawns three processes when a VM is
2785 launched, the 3rd one is the one that will end up here. */
2786 RTPROCESS pidParent;
2787 int vrc = RTProcQueryParent(pid, &pidParent);
2788 if (RT_SUCCESS(vrc))
2789 vrc = RTProcQueryParent(pidParent, &pidParent);
2790 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2791 || vrc == VERR_ACCESS_DENIED)
2792 {
2793 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2794 mData->mSession.mPID = pid;
2795 }
2796#endif
2797
2798 if (mData->mSession.mPID != pid)
2799 return setError(E_ACCESSDENIED,
2800 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2801 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2802 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2803 }
2804
2805 // create the mutable SessionMachine from the current machine
2806 ComObjPtr<SessionMachine> sessionMachine;
2807 sessionMachine.createObject();
2808 hrc = sessionMachine->init(this);
2809 AssertComRC(hrc);
2810
2811 /* NOTE: doing return from this function after this point but
2812 * before the end is forbidden since it may call SessionMachine::uninit()
2813 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2814 * lock while still holding the Machine lock in alock so that a deadlock
2815 * is possible due to the wrong lock order. */
2816
2817 if (SUCCEEDED(hrc))
2818 {
2819 /*
2820 * Set the session state to Spawning to protect against subsequent
2821 * attempts to open a session and to unregister the machine after
2822 * we release the lock.
2823 */
2824 SessionState_T origState = mData->mSession.mState;
2825 mData->mSession.mState = SessionState_Spawning;
2826
2827#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2828 /* Get the client token ID to be passed to the client process */
2829 Utf8Str strTokenId;
2830 sessionMachine->i_getTokenId(strTokenId);
2831 Assert(!strTokenId.isEmpty());
2832#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2833 /* Get the client token to be passed to the client process */
2834 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2835 /* The token is now "owned" by pToken, fix refcount */
2836 if (!pToken.isNull())
2837 pToken->Release();
2838#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2839
2840 /*
2841 * Release the lock before calling the client process -- it will call
2842 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2843 * because the state is Spawning, so that LaunchVMProcess() and
2844 * LockMachine() calls will fail. This method, called before we
2845 * acquire the lock again, will fail because of the wrong PID.
2846 *
2847 * Note that mData->mSession.mRemoteControls accessed outside
2848 * the lock may not be modified when state is Spawning, so it's safe.
2849 */
2850 alock.release();
2851
2852 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2853#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2854 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2855#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2856 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2857 /* Now the token is owned by the client process. */
2858 pToken.setNull();
2859#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2860 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2861
2862 /* The failure may occur w/o any error info (from RPC), so provide one */
2863 if (FAILED(hrc))
2864 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2865
2866 // get session name, either to remember or to compare against
2867 // the already known session name.
2868 {
2869 Bstr bstrSessionName;
2870 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2871 if (SUCCEEDED(hrc2))
2872 strSessionName = bstrSessionName;
2873 }
2874
2875 if ( SUCCEEDED(hrc)
2876 && fLaunchingVMProcess
2877 )
2878 {
2879 /* complete the remote session initialization */
2880
2881 /* get the console from the direct session */
2882 ComPtr<IConsole> console;
2883 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2884 ComAssertComRC(hrc);
2885
2886 if (SUCCEEDED(hrc) && !console)
2887 {
2888 ComAssert(!!console);
2889 hrc = E_FAIL;
2890 }
2891
2892 /* assign machine & console to the remote session */
2893 if (SUCCEEDED(hrc))
2894 {
2895 /*
2896 * after LaunchVMProcess(), the first and the only
2897 * entry in remoteControls is that remote session
2898 */
2899 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2900 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2901 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2902
2903 /* The failure may occur w/o any error info (from RPC), so provide one */
2904 if (FAILED(hrc))
2905 setError(VBOX_E_VM_ERROR,
2906 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2907 }
2908
2909 if (FAILED(hrc))
2910 pSessionControl->Uninitialize();
2911 }
2912
2913 /* acquire the lock again */
2914 alock.acquire();
2915
2916 /* Restore the session state */
2917 mData->mSession.mState = origState;
2918 }
2919
2920 // finalize spawning anyway (this is why we don't return on errors above)
2921 if (fLaunchingVMProcess)
2922 {
2923 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2924 /* Note that the progress object is finalized later */
2925 /** @todo Consider checking mData->mSession.mProgress for cancellation
2926 * around here. */
2927
2928 /* We don't reset mSession.mPID here because it is necessary for
2929 * SessionMachine::uninit() to reap the child process later. */
2930
2931 if (FAILED(hrc))
2932 {
2933 /* Close the remote session, remove the remote control from the list
2934 * and reset session state to Closed (@note keep the code in sync
2935 * with the relevant part in checkForSpawnFailure()). */
2936
2937 Assert(mData->mSession.mRemoteControls.size() == 1);
2938 if (mData->mSession.mRemoteControls.size() == 1)
2939 {
2940 ErrorInfoKeeper eik;
2941 mData->mSession.mRemoteControls.front()->Uninitialize();
2942 }
2943
2944 mData->mSession.mRemoteControls.clear();
2945 mData->mSession.mState = SessionState_Unlocked;
2946 }
2947 }
2948 else
2949 {
2950 /* memorize PID of the directly opened session */
2951 if (SUCCEEDED(hrc))
2952 mData->mSession.mPID = pid;
2953 }
2954
2955 if (SUCCEEDED(hrc))
2956 {
2957 mData->mSession.mLockType = aLockType;
2958 /* memorize the direct session control and cache IUnknown for it */
2959 mData->mSession.mDirectControl = pSessionControl;
2960 mData->mSession.mState = SessionState_Locked;
2961 if (!fLaunchingVMProcess)
2962 mData->mSession.mName = strSessionName;
2963 /* associate the SessionMachine with this Machine */
2964 mData->mSession.mMachine = sessionMachine;
2965
2966 /* request an IUnknown pointer early from the remote party for later
2967 * identity checks (it will be internally cached within mDirectControl
2968 * at least on XPCOM) */
2969 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2970 NOREF(unk);
2971
2972#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2973 if (aLockType == LockType_VM)
2974 {
2975 /* get the console from the direct session */
2976 ComPtr<IConsole> console;
2977 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2978 ComAssertComRC(hrc);
2979 /* send passswords to console */
2980 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2981 it != mData->mpKeyStore->end();
2982 ++it)
2983 {
2984 SecretKey *pKey = it->second;
2985 pKey->retain();
2986 console->AddEncryptionPassword(Bstr(it->first).raw(),
2987 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2988 TRUE);
2989 pKey->release();
2990 }
2991
2992 }
2993#endif
2994 }
2995
2996 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2997 * would break the lock order */
2998 alock.release();
2999
3000 /* uninitialize the created session machine on failure */
3001 if (FAILED(hrc))
3002 sessionMachine->uninit();
3003 }
3004
3005 if (SUCCEEDED(hrc))
3006 {
3007 /*
3008 * tell the client watcher thread to update the set of
3009 * machines that have open sessions
3010 */
3011 mParent->i_updateClientWatcher();
3012
3013 if (oldState != SessionState_Locked)
3014 /* fire an event */
3015 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3016 }
3017
3018 return hrc;
3019}
3020
3021/**
3022 * @note Locks objects!
3023 */
3024HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3025 const com::Utf8Str &aName,
3026 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3027 ComPtr<IProgress> &aProgress)
3028{
3029 Utf8Str strFrontend(aName);
3030 /* "emergencystop" doesn't need the session, so skip the checks/interface
3031 * retrieval. This code doesn't quite fit in here, but introducing a
3032 * special API method would be even more effort, and would require explicit
3033 * support by every API client. It's better to hide the feature a bit. */
3034 if (strFrontend != "emergencystop")
3035 CheckComArgNotNull(aSession);
3036
3037 HRESULT hrc = S_OK;
3038 if (strFrontend.isEmpty())
3039 {
3040 Bstr bstrFrontend;
3041 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3042 if (FAILED(hrc))
3043 return hrc;
3044 strFrontend = bstrFrontend;
3045 if (strFrontend.isEmpty())
3046 {
3047 ComPtr<ISystemProperties> systemProperties;
3048 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3049 if (FAILED(hrc))
3050 return hrc;
3051 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3052 if (FAILED(hrc))
3053 return hrc;
3054 strFrontend = bstrFrontend;
3055 }
3056 /* paranoia - emergencystop is not a valid default */
3057 if (strFrontend == "emergencystop")
3058 strFrontend = Utf8Str::Empty;
3059 }
3060 /* default frontend: Qt GUI */
3061 if (strFrontend.isEmpty())
3062 strFrontend = "GUI/Qt";
3063
3064 if (strFrontend != "emergencystop")
3065 {
3066 /* check the session state */
3067 SessionState_T state;
3068 hrc = aSession->COMGETTER(State)(&state);
3069 if (FAILED(hrc))
3070 return hrc;
3071
3072 if (state != SessionState_Unlocked)
3073 return setError(VBOX_E_INVALID_OBJECT_STATE,
3074 tr("The given session is busy"));
3075
3076 /* get the IInternalSessionControl interface */
3077 ComPtr<IInternalSessionControl> control(aSession);
3078 ComAssertMsgRet(!control.isNull(),
3079 ("No IInternalSessionControl interface"),
3080 E_INVALIDARG);
3081
3082 /* get the teleporter enable state for the progress object init. */
3083 BOOL fTeleporterEnabled;
3084 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3085 if (FAILED(hrc))
3086 return hrc;
3087
3088 /* create a progress object */
3089 ComObjPtr<ProgressProxy> progress;
3090 progress.createObject();
3091 hrc = progress->init(mParent,
3092 static_cast<IMachine*>(this),
3093 Bstr(tr("Starting VM")).raw(),
3094 TRUE /* aCancelable */,
3095 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3096 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3097 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3098 2 /* uFirstOperationWeight */,
3099 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3100 if (SUCCEEDED(hrc))
3101 {
3102 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3103 if (SUCCEEDED(hrc))
3104 {
3105 aProgress = progress;
3106
3107 /* signal the client watcher thread */
3108 mParent->i_updateClientWatcher();
3109
3110 /* fire an event */
3111 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3112 }
3113 }
3114 }
3115 else
3116 {
3117 /* no progress object - either instant success or failure */
3118 aProgress = NULL;
3119
3120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3121
3122 if (mData->mSession.mState != SessionState_Locked)
3123 return setError(VBOX_E_INVALID_OBJECT_STATE,
3124 tr("The machine '%s' is not locked by a session"),
3125 mUserData->s.strName.c_str());
3126
3127 /* must have a VM process associated - do not kill normal API clients
3128 * with an open session */
3129 if (!Global::IsOnline(mData->mMachineState))
3130 return setError(VBOX_E_INVALID_OBJECT_STATE,
3131 tr("The machine '%s' does not have a VM process"),
3132 mUserData->s.strName.c_str());
3133
3134 /* forcibly terminate the VM process */
3135 if (mData->mSession.mPID != NIL_RTPROCESS)
3136 RTProcTerminate(mData->mSession.mPID);
3137
3138 /* signal the client watcher thread, as most likely the client has
3139 * been terminated */
3140 mParent->i_updateClientWatcher();
3141 }
3142
3143 return hrc;
3144}
3145
3146HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3147{
3148 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3149 return setError(E_INVALIDARG,
3150 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3151 aPosition, SchemaDefs::MaxBootPosition);
3152
3153 if (aDevice == DeviceType_USB)
3154 return setError(E_NOTIMPL,
3155 tr("Booting from USB device is currently not supported"));
3156
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3160 if (FAILED(hrc)) return hrc;
3161
3162 i_setModified(IsModified_MachineData);
3163 mHWData.backup();
3164 mHWData->mBootOrder[aPosition - 1] = aDevice;
3165
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3170{
3171 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3172 return setError(E_INVALIDARG,
3173 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3174 aPosition, SchemaDefs::MaxBootPosition);
3175
3176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3177
3178 *aDevice = mHWData->mBootOrder[aPosition - 1];
3179
3180 return S_OK;
3181}
3182
3183HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3184 LONG aControllerPort,
3185 LONG aDevice,
3186 DeviceType_T aType,
3187 const ComPtr<IMedium> &aMedium)
3188{
3189 IMedium *aM = aMedium;
3190 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3191 aName.c_str(), aControllerPort, aDevice, aType, aM));
3192
3193 // request the host lock first, since might be calling Host methods for getting host drives;
3194 // next, protect the media tree all the while we're in here, as well as our member variables
3195 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3196 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3197
3198 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3199 if (FAILED(hrc)) return hrc;
3200
3201 /// @todo NEWMEDIA implicit machine registration
3202 if (!mData->mRegistered)
3203 return setError(VBOX_E_INVALID_OBJECT_STATE,
3204 tr("Cannot attach storage devices to an unregistered machine"));
3205
3206 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3207
3208 /* Check for an existing controller. */
3209 ComObjPtr<StorageController> ctl;
3210 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3211 if (FAILED(hrc)) return hrc;
3212
3213 StorageControllerType_T ctrlType;
3214 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3215 if (FAILED(hrc))
3216 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3217
3218 bool fSilent = false;
3219
3220 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3221 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3222 if ( mData->mMachineState == MachineState_Paused
3223 && strReconfig == "1")
3224 fSilent = true;
3225
3226 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3227 bool fHotplug = false;
3228 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3229 fHotplug = true;
3230
3231 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3232 return setError(VBOX_E_INVALID_VM_STATE,
3233 tr("Controller '%s' does not support hot-plugging"),
3234 aName.c_str());
3235
3236 // check that the port and device are not out of range
3237 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3238 if (FAILED(hrc)) return hrc;
3239
3240 /* check if the device slot is already busy */
3241 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3242 aName,
3243 aControllerPort,
3244 aDevice);
3245 if (pAttachTemp)
3246 {
3247 Medium *pMedium = pAttachTemp->i_getMedium();
3248 if (pMedium)
3249 {
3250 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3251 return setError(VBOX_E_OBJECT_IN_USE,
3252 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3253 pMedium->i_getLocationFull().c_str(),
3254 aControllerPort,
3255 aDevice,
3256 aName.c_str());
3257 }
3258 else
3259 return setError(VBOX_E_OBJECT_IN_USE,
3260 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3261 aControllerPort, aDevice, aName.c_str());
3262 }
3263
3264 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3265 if (aMedium && medium.isNull())
3266 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3267
3268 AutoCaller mediumCaller(medium);
3269 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3270
3271 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3272
3273 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3274 if ( pAttachTemp
3275 && !medium.isNull()
3276 && ( medium->i_getType() != MediumType_Readonly
3277 || medium->i_getDeviceType() != DeviceType_DVD)
3278 )
3279 return setError(VBOX_E_OBJECT_IN_USE,
3280 tr("Medium '%s' is already attached to this virtual machine"),
3281 medium->i_getLocationFull().c_str());
3282
3283 if (!medium.isNull())
3284 {
3285 MediumType_T mtype = medium->i_getType();
3286 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3287 // For DVDs it's not written to the config file, so needs no global config
3288 // version bump. For floppies it's a new attribute "type", which is ignored
3289 // by older VirtualBox version, so needs no global config version bump either.
3290 // For hard disks this type is not accepted.
3291 if (mtype == MediumType_MultiAttach)
3292 {
3293 // This type is new with VirtualBox 4.0 and therefore requires settings
3294 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3295 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3296 // two reasons: The medium type is a property of the media registry tree, which
3297 // can reside in the global config file (for pre-4.0 media); we would therefore
3298 // possibly need to bump the global config version. We don't want to do that though
3299 // because that might make downgrading to pre-4.0 impossible.
3300 // As a result, we can only use these two new types if the medium is NOT in the
3301 // global registry:
3302 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3303 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3304 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3305 )
3306 return setError(VBOX_E_INVALID_OBJECT_STATE,
3307 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3308 "to machines that were created with VirtualBox 4.0 or later"),
3309 medium->i_getLocationFull().c_str());
3310 }
3311 }
3312
3313 bool fIndirect = false;
3314 if (!medium.isNull())
3315 fIndirect = medium->i_isReadOnly();
3316 bool associate = true;
3317
3318 do
3319 {
3320 if ( aType == DeviceType_HardDisk
3321 && mMediumAttachments.isBackedUp())
3322 {
3323 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3324
3325 /* check if the medium was attached to the VM before we started
3326 * changing attachments in which case the attachment just needs to
3327 * be restored */
3328 pAttachTemp = i_findAttachment(oldAtts, medium);
3329 if (pAttachTemp)
3330 {
3331 AssertReturn(!fIndirect, E_FAIL);
3332
3333 /* see if it's the same bus/channel/device */
3334 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3335 {
3336 /* the simplest case: restore the whole attachment
3337 * and return, nothing else to do */
3338 mMediumAttachments->push_back(pAttachTemp);
3339
3340 /* Reattach the medium to the VM. */
3341 if (fHotplug || fSilent)
3342 {
3343 mediumLock.release();
3344 treeLock.release();
3345 alock.release();
3346
3347 MediumLockList *pMediumLockList(new MediumLockList());
3348
3349 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3350 medium /* pToLockWrite */,
3351 false /* fMediumLockWriteAll */,
3352 NULL,
3353 *pMediumLockList);
3354 alock.acquire();
3355 if (FAILED(hrc))
3356 delete pMediumLockList;
3357 else
3358 {
3359 Assert(mData->mSession.mLockedMedia.IsLocked());
3360 mData->mSession.mLockedMedia.Unlock();
3361 alock.release();
3362 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3363 mData->mSession.mLockedMedia.Lock();
3364 alock.acquire();
3365 }
3366 alock.release();
3367
3368 if (SUCCEEDED(hrc))
3369 {
3370 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3371 /* Remove lock list in case of error. */
3372 if (FAILED(hrc))
3373 {
3374 mData->mSession.mLockedMedia.Unlock();
3375 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3376 mData->mSession.mLockedMedia.Lock();
3377 }
3378 }
3379 }
3380
3381 return S_OK;
3382 }
3383
3384 /* bus/channel/device differ; we need a new attachment object,
3385 * but don't try to associate it again */
3386 associate = false;
3387 break;
3388 }
3389 }
3390
3391 /* go further only if the attachment is to be indirect */
3392 if (!fIndirect)
3393 break;
3394
3395 /* perform the so called smart attachment logic for indirect
3396 * attachments. Note that smart attachment is only applicable to base
3397 * hard disks. */
3398
3399 if (medium->i_getParent().isNull())
3400 {
3401 /* first, investigate the backup copy of the current hard disk
3402 * attachments to make it possible to re-attach existing diffs to
3403 * another device slot w/o losing their contents */
3404 if (mMediumAttachments.isBackedUp())
3405 {
3406 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3407
3408 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3409 uint32_t foundLevel = 0;
3410
3411 for (MediumAttachmentList::const_iterator
3412 it = oldAtts.begin();
3413 it != oldAtts.end();
3414 ++it)
3415 {
3416 uint32_t level = 0;
3417 MediumAttachment *pAttach = *it;
3418 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3419 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3420 if (pMedium.isNull())
3421 continue;
3422
3423 if (pMedium->i_getBase(&level) == medium)
3424 {
3425 /* skip the hard disk if its currently attached (we
3426 * cannot attach the same hard disk twice) */
3427 if (i_findAttachment(*mMediumAttachments.data(),
3428 pMedium))
3429 continue;
3430
3431 /* matched device, channel and bus (i.e. attached to the
3432 * same place) will win and immediately stop the search;
3433 * otherwise the attachment that has the youngest
3434 * descendant of medium will be used
3435 */
3436 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3437 {
3438 /* the simplest case: restore the whole attachment
3439 * and return, nothing else to do */
3440 mMediumAttachments->push_back(*it);
3441
3442 /* Reattach the medium to the VM. */
3443 if (fHotplug || fSilent)
3444 {
3445 mediumLock.release();
3446 treeLock.release();
3447 alock.release();
3448
3449 MediumLockList *pMediumLockList(new MediumLockList());
3450
3451 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3452 medium /* pToLockWrite */,
3453 false /* fMediumLockWriteAll */,
3454 NULL,
3455 *pMediumLockList);
3456 alock.acquire();
3457 if (FAILED(hrc))
3458 delete pMediumLockList;
3459 else
3460 {
3461 Assert(mData->mSession.mLockedMedia.IsLocked());
3462 mData->mSession.mLockedMedia.Unlock();
3463 alock.release();
3464 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3465 mData->mSession.mLockedMedia.Lock();
3466 alock.acquire();
3467 }
3468 alock.release();
3469
3470 if (SUCCEEDED(hrc))
3471 {
3472 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3473 /* Remove lock list in case of error. */
3474 if (FAILED(hrc))
3475 {
3476 mData->mSession.mLockedMedia.Unlock();
3477 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3478 mData->mSession.mLockedMedia.Lock();
3479 }
3480 }
3481 }
3482
3483 return S_OK;
3484 }
3485 else if ( foundIt == oldAtts.end()
3486 || level > foundLevel /* prefer younger */
3487 )
3488 {
3489 foundIt = it;
3490 foundLevel = level;
3491 }
3492 }
3493 }
3494
3495 if (foundIt != oldAtts.end())
3496 {
3497 /* use the previously attached hard disk */
3498 medium = (*foundIt)->i_getMedium();
3499 mediumCaller.attach(medium);
3500 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3501 mediumLock.attach(medium);
3502 /* not implicit, doesn't require association with this VM */
3503 fIndirect = false;
3504 associate = false;
3505 /* go right to the MediumAttachment creation */
3506 break;
3507 }
3508 }
3509
3510 /* must give up the medium lock and medium tree lock as below we
3511 * go over snapshots, which needs a lock with higher lock order. */
3512 mediumLock.release();
3513 treeLock.release();
3514
3515 /* then, search through snapshots for the best diff in the given
3516 * hard disk's chain to base the new diff on */
3517
3518 ComObjPtr<Medium> base;
3519 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3520 while (snap)
3521 {
3522 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3523
3524 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3525
3526 MediumAttachment *pAttachFound = NULL;
3527 uint32_t foundLevel = 0;
3528
3529 for (MediumAttachmentList::const_iterator
3530 it = snapAtts.begin();
3531 it != snapAtts.end();
3532 ++it)
3533 {
3534 MediumAttachment *pAttach = *it;
3535 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3536 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3537 if (pMedium.isNull())
3538 continue;
3539
3540 uint32_t level = 0;
3541 if (pMedium->i_getBase(&level) == medium)
3542 {
3543 /* matched device, channel and bus (i.e. attached to the
3544 * same place) will win and immediately stop the search;
3545 * otherwise the attachment that has the youngest
3546 * descendant of medium will be used
3547 */
3548 if ( pAttach->i_getDevice() == aDevice
3549 && pAttach->i_getPort() == aControllerPort
3550 && pAttach->i_getControllerName() == aName
3551 )
3552 {
3553 pAttachFound = pAttach;
3554 break;
3555 }
3556 else if ( !pAttachFound
3557 || level > foundLevel /* prefer younger */
3558 )
3559 {
3560 pAttachFound = pAttach;
3561 foundLevel = level;
3562 }
3563 }
3564 }
3565
3566 if (pAttachFound)
3567 {
3568 base = pAttachFound->i_getMedium();
3569 break;
3570 }
3571
3572 snap = snap->i_getParent();
3573 }
3574
3575 /* re-lock medium tree and the medium, as we need it below */
3576 treeLock.acquire();
3577 mediumLock.acquire();
3578
3579 /* found a suitable diff, use it as a base */
3580 if (!base.isNull())
3581 {
3582 medium = base;
3583 mediumCaller.attach(medium);
3584 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3585 mediumLock.attach(medium);
3586 }
3587 }
3588
3589 Utf8Str strFullSnapshotFolder;
3590 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3591
3592 ComObjPtr<Medium> diff;
3593 diff.createObject();
3594 // store this diff in the same registry as the parent
3595 Guid uuidRegistryParent;
3596 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3597 {
3598 // parent image has no registry: this can happen if we're attaching a new immutable
3599 // image that has not yet been attached (medium then points to the base and we're
3600 // creating the diff image for the immutable, and the parent is not yet registered);
3601 // put the parent in the machine registry then
3602 mediumLock.release();
3603 treeLock.release();
3604 alock.release();
3605 i_addMediumToRegistry(medium);
3606 alock.acquire();
3607 treeLock.acquire();
3608 mediumLock.acquire();
3609 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3610 }
3611 hrc = diff->init(mParent,
3612 medium->i_getPreferredDiffFormat(),
3613 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3614 uuidRegistryParent,
3615 DeviceType_HardDisk);
3616 if (FAILED(hrc)) return hrc;
3617
3618 /* Apply the normal locking logic to the entire chain. */
3619 MediumLockList *pMediumLockList(new MediumLockList());
3620 mediumLock.release();
3621 treeLock.release();
3622 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3623 diff /* pToLockWrite */,
3624 false /* fMediumLockWriteAll */,
3625 medium,
3626 *pMediumLockList);
3627 treeLock.acquire();
3628 mediumLock.acquire();
3629 if (SUCCEEDED(hrc))
3630 {
3631 mediumLock.release();
3632 treeLock.release();
3633 hrc = pMediumLockList->Lock();
3634 treeLock.acquire();
3635 mediumLock.acquire();
3636 if (FAILED(hrc))
3637 setError(hrc,
3638 tr("Could not lock medium when creating diff '%s'"),
3639 diff->i_getLocationFull().c_str());
3640 else
3641 {
3642 /* will release the lock before the potentially lengthy
3643 * operation, so protect with the special state */
3644 MachineState_T oldState = mData->mMachineState;
3645 i_setMachineState(MachineState_SettingUp);
3646
3647 mediumLock.release();
3648 treeLock.release();
3649 alock.release();
3650
3651 hrc = medium->i_createDiffStorage(diff,
3652 medium->i_getPreferredDiffVariant(),
3653 pMediumLockList,
3654 NULL /* aProgress */,
3655 true /* aWait */,
3656 false /* aNotify */);
3657
3658 alock.acquire();
3659 treeLock.acquire();
3660 mediumLock.acquire();
3661
3662 i_setMachineState(oldState);
3663 }
3664 }
3665
3666 /* Unlock the media and free the associated memory. */
3667 delete pMediumLockList;
3668
3669 if (FAILED(hrc)) return hrc;
3670
3671 /* use the created diff for the actual attachment */
3672 medium = diff;
3673 mediumCaller.attach(medium);
3674 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3675 mediumLock.attach(medium);
3676 }
3677 while (0);
3678
3679 ComObjPtr<MediumAttachment> attachment;
3680 attachment.createObject();
3681 hrc = attachment->init(this,
3682 medium,
3683 aName,
3684 aControllerPort,
3685 aDevice,
3686 aType,
3687 fIndirect,
3688 false /* fPassthrough */,
3689 false /* fTempEject */,
3690 false /* fNonRotational */,
3691 false /* fDiscard */,
3692 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3693 Utf8Str::Empty);
3694 if (FAILED(hrc)) return hrc;
3695
3696 if (associate && !medium.isNull())
3697 {
3698 // as the last step, associate the medium to the VM
3699 hrc = medium->i_addBackReference(mData->mUuid);
3700 // here we can fail because of Deleting, or being in process of creating a Diff
3701 if (FAILED(hrc)) return hrc;
3702
3703 mediumLock.release();
3704 treeLock.release();
3705 alock.release();
3706 i_addMediumToRegistry(medium);
3707 alock.acquire();
3708 treeLock.acquire();
3709 mediumLock.acquire();
3710 }
3711
3712 /* success: finally remember the attachment */
3713 i_setModified(IsModified_Storage);
3714 mMediumAttachments.backup();
3715 mMediumAttachments->push_back(attachment);
3716
3717 mediumLock.release();
3718 treeLock.release();
3719 alock.release();
3720
3721 if (fHotplug || fSilent)
3722 {
3723 if (!medium.isNull())
3724 {
3725 MediumLockList *pMediumLockList(new MediumLockList());
3726
3727 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3728 medium /* pToLockWrite */,
3729 false /* fMediumLockWriteAll */,
3730 NULL,
3731 *pMediumLockList);
3732 alock.acquire();
3733 if (FAILED(hrc))
3734 delete pMediumLockList;
3735 else
3736 {
3737 Assert(mData->mSession.mLockedMedia.IsLocked());
3738 mData->mSession.mLockedMedia.Unlock();
3739 alock.release();
3740 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3741 mData->mSession.mLockedMedia.Lock();
3742 alock.acquire();
3743 }
3744 alock.release();
3745 }
3746
3747 if (SUCCEEDED(hrc))
3748 {
3749 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3750 /* Remove lock list in case of error. */
3751 if (FAILED(hrc))
3752 {
3753 mData->mSession.mLockedMedia.Unlock();
3754 mData->mSession.mLockedMedia.Remove(attachment);
3755 mData->mSession.mLockedMedia.Lock();
3756 }
3757 }
3758 }
3759
3760 /* Save modified registries, but skip this machine as it's the caller's
3761 * job to save its settings like all other settings changes. */
3762 mParent->i_unmarkRegistryModified(i_getId());
3763 mParent->i_saveModifiedRegistries();
3764
3765 if (SUCCEEDED(hrc))
3766 {
3767 if (fIndirect && medium != aM)
3768 mParent->i_onMediumConfigChanged(medium);
3769 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3770 }
3771
3772 return hrc;
3773}
3774
3775HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3776 LONG aDevice)
3777{
3778 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3779
3780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3781
3782 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3783 if (FAILED(hrc)) return hrc;
3784
3785 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3786
3787 /* Check for an existing controller. */
3788 ComObjPtr<StorageController> ctl;
3789 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3790 if (FAILED(hrc)) return hrc;
3791
3792 StorageControllerType_T ctrlType;
3793 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3794 if (FAILED(hrc))
3795 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3796
3797 bool fSilent = false;
3798 Utf8Str strReconfig;
3799
3800 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3801 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3802 if ( mData->mMachineState == MachineState_Paused
3803 && strReconfig == "1")
3804 fSilent = true;
3805
3806 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3807 bool fHotplug = false;
3808 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3809 fHotplug = true;
3810
3811 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3812 return setError(VBOX_E_INVALID_VM_STATE,
3813 tr("Controller '%s' does not support hot-plugging"),
3814 aName.c_str());
3815
3816 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3817 aName,
3818 aControllerPort,
3819 aDevice);
3820 if (!pAttach)
3821 return setError(VBOX_E_OBJECT_NOT_FOUND,
3822 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3823 aDevice, aControllerPort, aName.c_str());
3824
3825 if (fHotplug && !pAttach->i_getHotPluggable())
3826 return setError(VBOX_E_NOT_SUPPORTED,
3827 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3828 aDevice, aControllerPort, aName.c_str());
3829
3830 /*
3831 * The VM has to detach the device before we delete any implicit diffs.
3832 * If this fails we can roll back without loosing data.
3833 */
3834 if (fHotplug || fSilent)
3835 {
3836 alock.release();
3837 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3838 alock.acquire();
3839 }
3840 if (FAILED(hrc)) return hrc;
3841
3842 /* If we are here everything went well and we can delete the implicit now. */
3843 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3844
3845 alock.release();
3846
3847 /* Save modified registries, but skip this machine as it's the caller's
3848 * job to save its settings like all other settings changes. */
3849 mParent->i_unmarkRegistryModified(i_getId());
3850 mParent->i_saveModifiedRegistries();
3851
3852 if (SUCCEEDED(hrc))
3853 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3854
3855 return hrc;
3856}
3857
3858HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3859 LONG aDevice, BOOL aPassthrough)
3860{
3861 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3862 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3863
3864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3865
3866 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3867 if (FAILED(hrc)) return hrc;
3868
3869 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3870
3871 /* Check for an existing controller. */
3872 ComObjPtr<StorageController> ctl;
3873 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3874 if (FAILED(hrc)) return hrc;
3875
3876 StorageControllerType_T ctrlType;
3877 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3878 if (FAILED(hrc))
3879 return setError(E_FAIL,
3880 tr("Could not get type of controller '%s'"),
3881 aName.c_str());
3882
3883 bool fSilent = false;
3884 Utf8Str strReconfig;
3885
3886 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3887 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3888 if ( mData->mMachineState == MachineState_Paused
3889 && strReconfig == "1")
3890 fSilent = true;
3891
3892 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3893 bool fHotplug = false;
3894 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3895 fHotplug = true;
3896
3897 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3898 return setError(VBOX_E_INVALID_VM_STATE,
3899 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3900 aName.c_str());
3901
3902 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3903 aName,
3904 aControllerPort,
3905 aDevice);
3906 if (!pAttach)
3907 return setError(VBOX_E_OBJECT_NOT_FOUND,
3908 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3909 aDevice, aControllerPort, aName.c_str());
3910
3911
3912 i_setModified(IsModified_Storage);
3913 mMediumAttachments.backup();
3914
3915 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3916
3917 if (pAttach->i_getType() != DeviceType_DVD)
3918 return setError(E_INVALIDARG,
3919 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3920 aDevice, aControllerPort, aName.c_str());
3921
3922 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3923
3924 pAttach->i_updatePassthrough(!!aPassthrough);
3925
3926 attLock.release();
3927 alock.release();
3928 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3929 if (SUCCEEDED(hrc) && fValueChanged)
3930 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3931
3932 return hrc;
3933}
3934
3935HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3936 LONG aDevice, BOOL aTemporaryEject)
3937{
3938
3939 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3940 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3941
3942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3943
3944 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3945 if (FAILED(hrc)) return hrc;
3946
3947 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3948 aName,
3949 aControllerPort,
3950 aDevice);
3951 if (!pAttach)
3952 return setError(VBOX_E_OBJECT_NOT_FOUND,
3953 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3954 aDevice, aControllerPort, aName.c_str());
3955
3956
3957 i_setModified(IsModified_Storage);
3958 mMediumAttachments.backup();
3959
3960 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3961
3962 if (pAttach->i_getType() != DeviceType_DVD)
3963 return setError(E_INVALIDARG,
3964 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3965 aDevice, aControllerPort, aName.c_str());
3966 pAttach->i_updateTempEject(!!aTemporaryEject);
3967
3968 return S_OK;
3969}
3970
3971HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3972 LONG aDevice, BOOL aNonRotational)
3973{
3974
3975 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3976 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3977
3978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3979
3980 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3981 if (FAILED(hrc)) return hrc;
3982
3983 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3984
3985 if (Global::IsOnlineOrTransient(mData->mMachineState))
3986 return setError(VBOX_E_INVALID_VM_STATE,
3987 tr("Invalid machine state: %s"),
3988 Global::stringifyMachineState(mData->mMachineState));
3989
3990 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3991 aName,
3992 aControllerPort,
3993 aDevice);
3994 if (!pAttach)
3995 return setError(VBOX_E_OBJECT_NOT_FOUND,
3996 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3997 aDevice, aControllerPort, aName.c_str());
3998
3999
4000 i_setModified(IsModified_Storage);
4001 mMediumAttachments.backup();
4002
4003 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4004
4005 if (pAttach->i_getType() != DeviceType_HardDisk)
4006 return setError(E_INVALIDARG,
4007 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"),
4008 aDevice, aControllerPort, aName.c_str());
4009 pAttach->i_updateNonRotational(!!aNonRotational);
4010
4011 return S_OK;
4012}
4013
4014HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4015 LONG aDevice, BOOL aDiscard)
4016{
4017
4018 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4019 aName.c_str(), aControllerPort, aDevice, aDiscard));
4020
4021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4022
4023 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4024 if (FAILED(hrc)) return hrc;
4025
4026 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4027
4028 if (Global::IsOnlineOrTransient(mData->mMachineState))
4029 return setError(VBOX_E_INVALID_VM_STATE,
4030 tr("Invalid machine state: %s"),
4031 Global::stringifyMachineState(mData->mMachineState));
4032
4033 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4034 aName,
4035 aControllerPort,
4036 aDevice);
4037 if (!pAttach)
4038 return setError(VBOX_E_OBJECT_NOT_FOUND,
4039 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4040 aDevice, aControllerPort, aName.c_str());
4041
4042
4043 i_setModified(IsModified_Storage);
4044 mMediumAttachments.backup();
4045
4046 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4047
4048 if (pAttach->i_getType() != DeviceType_HardDisk)
4049 return setError(E_INVALIDARG,
4050 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"),
4051 aDevice, aControllerPort, aName.c_str());
4052 pAttach->i_updateDiscard(!!aDiscard);
4053
4054 return S_OK;
4055}
4056
4057HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4058 LONG aDevice, BOOL aHotPluggable)
4059{
4060 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4061 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4062
4063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4064
4065 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4066 if (FAILED(hrc)) return hrc;
4067
4068 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4069
4070 if (Global::IsOnlineOrTransient(mData->mMachineState))
4071 return setError(VBOX_E_INVALID_VM_STATE,
4072 tr("Invalid machine state: %s"),
4073 Global::stringifyMachineState(mData->mMachineState));
4074
4075 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4076 aName,
4077 aControllerPort,
4078 aDevice);
4079 if (!pAttach)
4080 return setError(VBOX_E_OBJECT_NOT_FOUND,
4081 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4082 aDevice, aControllerPort, aName.c_str());
4083
4084 /* Check for an existing controller. */
4085 ComObjPtr<StorageController> ctl;
4086 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4087 if (FAILED(hrc)) return hrc;
4088
4089 StorageControllerType_T ctrlType;
4090 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4091 if (FAILED(hrc))
4092 return setError(E_FAIL,
4093 tr("Could not get type of controller '%s'"),
4094 aName.c_str());
4095
4096 if (!i_isControllerHotplugCapable(ctrlType))
4097 return setError(VBOX_E_NOT_SUPPORTED,
4098 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4099 aName.c_str());
4100
4101 /* silently ignore attempts to modify the hot-plug status of USB devices */
4102 if (ctrlType == StorageControllerType_USB)
4103 return S_OK;
4104
4105 i_setModified(IsModified_Storage);
4106 mMediumAttachments.backup();
4107
4108 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4109
4110 if (pAttach->i_getType() == DeviceType_Floppy)
4111 return setError(E_INVALIDARG,
4112 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"),
4113 aDevice, aControllerPort, aName.c_str());
4114 pAttach->i_updateHotPluggable(!!aHotPluggable);
4115
4116 return S_OK;
4117}
4118
4119HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4120 LONG aDevice)
4121{
4122 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4123 aName.c_str(), aControllerPort, aDevice));
4124
4125 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4126}
4127
4128HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4129 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4130{
4131 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4132 aName.c_str(), aControllerPort, aDevice));
4133
4134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4135
4136 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4137 if (FAILED(hrc)) return hrc;
4138
4139 if (Global::IsOnlineOrTransient(mData->mMachineState))
4140 return setError(VBOX_E_INVALID_VM_STATE,
4141 tr("Invalid machine state: %s"),
4142 Global::stringifyMachineState(mData->mMachineState));
4143
4144 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4145 aName,
4146 aControllerPort,
4147 aDevice);
4148 if (!pAttach)
4149 return setError(VBOX_E_OBJECT_NOT_FOUND,
4150 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4151 aDevice, aControllerPort, aName.c_str());
4152
4153
4154 i_setModified(IsModified_Storage);
4155 mMediumAttachments.backup();
4156
4157 IBandwidthGroup *iB = aBandwidthGroup;
4158 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4159 if (aBandwidthGroup && group.isNull())
4160 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4161
4162 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4163
4164 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4165 if (strBandwidthGroupOld.isNotEmpty())
4166 {
4167 /* Get the bandwidth group object and release it - this must not fail. */
4168 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4169 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4170 Assert(SUCCEEDED(hrc));
4171
4172 pBandwidthGroupOld->i_release();
4173 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4174 }
4175
4176 if (!group.isNull())
4177 {
4178 group->i_reference();
4179 pAttach->i_updateBandwidthGroup(group->i_getName());
4180 }
4181
4182 return S_OK;
4183}
4184
4185HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4186 LONG aControllerPort,
4187 LONG aDevice,
4188 DeviceType_T aType)
4189{
4190 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4191 aName.c_str(), aControllerPort, aDevice, aType));
4192
4193 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4194}
4195
4196
4197HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4198 LONG aControllerPort,
4199 LONG aDevice,
4200 BOOL aForce)
4201{
4202 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4203 aName.c_str(), aControllerPort, aForce));
4204
4205 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4206}
4207
4208HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4209 LONG aControllerPort,
4210 LONG aDevice,
4211 const ComPtr<IMedium> &aMedium,
4212 BOOL aForce)
4213{
4214 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4215 aName.c_str(), aControllerPort, aDevice, aForce));
4216
4217 // request the host lock first, since might be calling Host methods for getting host drives;
4218 // next, protect the media tree all the while we're in here, as well as our member variables
4219 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4220 this->lockHandle(),
4221 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4222
4223 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4224 if (FAILED(hrc)) return hrc;
4225
4226 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4227 aName,
4228 aControllerPort,
4229 aDevice);
4230 if (pAttach.isNull())
4231 return setError(VBOX_E_OBJECT_NOT_FOUND,
4232 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4233 aDevice, aControllerPort, aName.c_str());
4234
4235 /* Remember previously mounted medium. The medium before taking the
4236 * backup is not necessarily the same thing. */
4237 ComObjPtr<Medium> oldmedium;
4238 oldmedium = pAttach->i_getMedium();
4239
4240 IMedium *iM = aMedium;
4241 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4242 if (aMedium && pMedium.isNull())
4243 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4244
4245 /* Check if potential medium is already mounted */
4246 if (pMedium == oldmedium)
4247 return S_OK;
4248
4249 AutoCaller mediumCaller(pMedium);
4250 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4251
4252 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4253 if (pMedium)
4254 {
4255 DeviceType_T mediumType = pAttach->i_getType();
4256 switch (mediumType)
4257 {
4258 case DeviceType_DVD:
4259 case DeviceType_Floppy:
4260 break;
4261
4262 default:
4263 return setError(VBOX_E_INVALID_OBJECT_STATE,
4264 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4265 aControllerPort,
4266 aDevice,
4267 aName.c_str());
4268 }
4269 }
4270
4271 i_setModified(IsModified_Storage);
4272 mMediumAttachments.backup();
4273
4274 {
4275 // The backup operation makes the pAttach reference point to the
4276 // old settings. Re-get the correct reference.
4277 pAttach = i_findAttachment(*mMediumAttachments.data(),
4278 aName,
4279 aControllerPort,
4280 aDevice);
4281 if (!oldmedium.isNull())
4282 oldmedium->i_removeBackReference(mData->mUuid);
4283 if (!pMedium.isNull())
4284 {
4285 pMedium->i_addBackReference(mData->mUuid);
4286
4287 mediumLock.release();
4288 multiLock.release();
4289 i_addMediumToRegistry(pMedium);
4290 multiLock.acquire();
4291 mediumLock.acquire();
4292 }
4293
4294 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4295 pAttach->i_updateMedium(pMedium);
4296 }
4297
4298 i_setModified(IsModified_Storage);
4299
4300 mediumLock.release();
4301 multiLock.release();
4302 hrc = i_onMediumChange(pAttach, aForce);
4303 multiLock.acquire();
4304 mediumLock.acquire();
4305
4306 /* On error roll back this change only. */
4307 if (FAILED(hrc))
4308 {
4309 if (!pMedium.isNull())
4310 pMedium->i_removeBackReference(mData->mUuid);
4311 pAttach = i_findAttachment(*mMediumAttachments.data(),
4312 aName,
4313 aControllerPort,
4314 aDevice);
4315 /* If the attachment is gone in the meantime, bail out. */
4316 if (pAttach.isNull())
4317 return hrc;
4318 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4319 if (!oldmedium.isNull())
4320 oldmedium->i_addBackReference(mData->mUuid);
4321 pAttach->i_updateMedium(oldmedium);
4322 }
4323
4324 mediumLock.release();
4325 multiLock.release();
4326
4327 /* Save modified registries, but skip this machine as it's the caller's
4328 * job to save its settings like all other settings changes. */
4329 mParent->i_unmarkRegistryModified(i_getId());
4330 mParent->i_saveModifiedRegistries();
4331
4332 return hrc;
4333}
4334HRESULT Machine::getMedium(const com::Utf8Str &aName,
4335 LONG aControllerPort,
4336 LONG aDevice,
4337 ComPtr<IMedium> &aMedium)
4338{
4339 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4340 aName.c_str(), aControllerPort, aDevice));
4341
4342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4343
4344 aMedium = NULL;
4345
4346 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4347 aName,
4348 aControllerPort,
4349 aDevice);
4350 if (pAttach.isNull())
4351 return setError(VBOX_E_OBJECT_NOT_FOUND,
4352 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4353 aDevice, aControllerPort, aName.c_str());
4354
4355 aMedium = pAttach->i_getMedium();
4356
4357 return S_OK;
4358}
4359
4360HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4361{
4362 if (aSlot < RT_ELEMENTS(mSerialPorts))
4363 {
4364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4365 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4366 return S_OK;
4367 }
4368 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4369}
4370
4371HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4372{
4373 if (aSlot < RT_ELEMENTS(mParallelPorts))
4374 {
4375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4376 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4377 return S_OK;
4378 }
4379 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4380}
4381
4382
4383HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4384{
4385 /* Do not assert if slot is out of range, just return the advertised
4386 status. testdriver/vbox.py triggers this in logVmInfo. */
4387 if (aSlot >= mNetworkAdapters.size())
4388 return setError(E_INVALIDARG,
4389 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4390 aSlot, mNetworkAdapters.size());
4391
4392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4393
4394 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4395
4396 return S_OK;
4397}
4398
4399HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4400{
4401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4402
4403 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4404 size_t i = 0;
4405 for (settings::StringsMap::const_iterator
4406 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4407 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4408 ++it, ++i)
4409 aKeys[i] = it->first;
4410
4411 return S_OK;
4412}
4413
4414 /**
4415 * @note Locks this object for reading.
4416 */
4417HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4418 com::Utf8Str &aValue)
4419{
4420 /* start with nothing found */
4421 aValue = "";
4422
4423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4424
4425 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4426 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4427 // found:
4428 aValue = it->second; // source is a Utf8Str
4429
4430 /* return the result to caller (may be empty) */
4431 return S_OK;
4432}
4433
4434 /**
4435 * @note Locks mParent for writing + this object for writing.
4436 */
4437HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4438{
4439 /* Because control characters in aKey have caused problems in the settings
4440 * they are rejected unless the key should be deleted. */
4441 if (!aValue.isEmpty())
4442 {
4443 for (size_t i = 0; i < aKey.length(); ++i)
4444 {
4445 char ch = aKey[i];
4446 if (RTLocCIsCntrl(ch))
4447 return E_INVALIDARG;
4448 }
4449 }
4450
4451 Utf8Str strOldValue; // empty
4452
4453 // locking note: we only hold the read lock briefly to look up the old value,
4454 // then release it and call the onExtraCanChange callbacks. There is a small
4455 // chance of a race insofar as the callback might be called twice if two callers
4456 // change the same key at the same time, but that's a much better solution
4457 // than the deadlock we had here before. The actual changing of the extradata
4458 // is then performed under the write lock and race-free.
4459
4460 // look up the old value first; if nothing has changed then we need not do anything
4461 {
4462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4463
4464 // For snapshots don't even think about allowing changes, extradata
4465 // is global for a machine, so there is nothing snapshot specific.
4466 if (i_isSnapshotMachine())
4467 return setError(VBOX_E_INVALID_VM_STATE,
4468 tr("Cannot set extradata for a snapshot"));
4469
4470 // check if the right IMachine instance is used
4471 if (mData->mRegistered && !i_isSessionMachine())
4472 return setError(VBOX_E_INVALID_VM_STATE,
4473 tr("Cannot set extradata for an immutable machine"));
4474
4475 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4476 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4477 strOldValue = it->second;
4478 }
4479
4480 bool fChanged;
4481 if ((fChanged = (strOldValue != aValue)))
4482 {
4483 // ask for permission from all listeners outside the locks;
4484 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4485 // lock to copy the list of callbacks to invoke
4486 Bstr bstrError;
4487 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4488 {
4489 const char *sep = bstrError.isEmpty() ? "" : ": ";
4490 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4491 return setError(E_ACCESSDENIED,
4492 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4493 aKey.c_str(),
4494 aValue.c_str(),
4495 sep,
4496 bstrError.raw());
4497 }
4498
4499 // data is changing and change not vetoed: then write it out under the lock
4500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4501
4502 if (aValue.isEmpty())
4503 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4504 else
4505 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4506 // creates a new key if needed
4507
4508 bool fNeedsGlobalSaveSettings = false;
4509 // This saving of settings is tricky: there is no "old state" for the
4510 // extradata items at all (unlike all other settings), so the old/new
4511 // settings comparison would give a wrong result!
4512 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4513
4514 if (fNeedsGlobalSaveSettings)
4515 {
4516 // save the global settings; for that we should hold only the VirtualBox lock
4517 alock.release();
4518 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4519 mParent->i_saveSettings();
4520 }
4521 }
4522
4523 // fire notification outside the lock
4524 if (fChanged)
4525 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4526
4527 return S_OK;
4528}
4529
4530HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4531{
4532 aProgress = NULL;
4533 NOREF(aSettingsFilePath);
4534 ReturnComNotImplemented();
4535}
4536
4537HRESULT Machine::saveSettings()
4538{
4539 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4540
4541 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4542 if (FAILED(hrc)) return hrc;
4543
4544 /* the settings file path may never be null */
4545 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4546
4547 /* save all VM data excluding snapshots */
4548 bool fNeedsGlobalSaveSettings = false;
4549 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4550 mlock.release();
4551
4552 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4553 {
4554 // save the global settings; for that we should hold only the VirtualBox lock
4555 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4556 hrc = mParent->i_saveSettings();
4557 }
4558
4559 return hrc;
4560}
4561
4562
4563HRESULT Machine::discardSettings()
4564{
4565 /*
4566 * We need to take the machine list lock here as well as the machine one
4567 * or we'll get into trouble should any media stuff require rolling back.
4568 *
4569 * Details:
4570 *
4571 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4572 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4573 * 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]
4574 * 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
4575 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4576 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4577 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4578 * 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
4579 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4580 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4581 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4582 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4583 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4584 * 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]
4585 * 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] (*)
4586 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4587 * 0:005> k
4588 * # Child-SP RetAddr Call Site
4589 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4590 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4591 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4592 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4593 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4594 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4595 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4596 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4597 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4598 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4599 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4600 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4601 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4602 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4603 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4604 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4605 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4606 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4607 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4608 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4609 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4610 *
4611 */
4612 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4614
4615 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4616 if (FAILED(hrc)) return hrc;
4617
4618 /*
4619 * during this rollback, the session will be notified if data has
4620 * been actually changed
4621 */
4622 i_rollback(true /* aNotify */);
4623
4624 return S_OK;
4625}
4626
4627/** @note Locks objects! */
4628HRESULT Machine::unregister(AutoCaller &autoCaller,
4629 CleanupMode_T aCleanupMode,
4630 std::vector<ComPtr<IMedium> > &aMedia)
4631{
4632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4633
4634 Guid id(i_getId());
4635
4636 if (mData->mSession.mState != SessionState_Unlocked)
4637 return setError(VBOX_E_INVALID_OBJECT_STATE,
4638 tr("Cannot unregister the machine '%s' while it is locked"),
4639 mUserData->s.strName.c_str());
4640
4641 // wait for state dependents to drop to zero
4642 i_ensureNoStateDependencies(alock);
4643
4644 if (!mData->mAccessible)
4645 {
4646 // inaccessible machines can only be unregistered; uninitialize ourselves
4647 // here because currently there may be no unregistered that are inaccessible
4648 // (this state combination is not supported). Note releasing the caller and
4649 // leaving the lock before calling uninit()
4650 alock.release();
4651 autoCaller.release();
4652
4653 uninit();
4654
4655 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4656 // calls VirtualBox::i_saveSettings()
4657
4658 return S_OK;
4659 }
4660
4661 HRESULT hrc = S_OK;
4662 mData->llFilesToDelete.clear();
4663
4664 if (!mSSData->strStateFilePath.isEmpty())
4665 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4666
4667 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4668 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4669 mData->llFilesToDelete.push_back(strNVRAMFile);
4670
4671 // This list collects the medium objects from all medium attachments
4672 // which we will detach from the machine and its snapshots, in a specific
4673 // order which allows for closing all media without getting "media in use"
4674 // errors, simply by going through the list from the front to the back:
4675 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4676 // and must be closed before the parent media from the snapshots, or closing the parents
4677 // will fail because they still have children);
4678 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4679 // the root ("first") snapshot of the machine.
4680 MediaList llMedia;
4681
4682 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4683 && mMediumAttachments->size()
4684 )
4685 {
4686 // we have media attachments: detach them all and add the Medium objects to our list
4687 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4688 }
4689
4690 if (mData->mFirstSnapshot)
4691 {
4692 // add the media from the medium attachments of the snapshots to
4693 // llMedia as well, after the "main" machine media;
4694 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4695 // snapshot machine, depth first.
4696
4697 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4698 MachineState_T oldState = mData->mMachineState;
4699 mData->mMachineState = MachineState_DeletingSnapshot;
4700
4701 // make a copy of the first snapshot reference so the refcount does not
4702 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4703 // (would hang due to the AutoCaller voodoo)
4704 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4705
4706 // GO!
4707 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4708
4709 mData->mMachineState = oldState;
4710 }
4711
4712 if (FAILED(hrc))
4713 {
4714 i_rollbackMedia();
4715 return hrc;
4716 }
4717
4718 // commit all the media changes made above
4719 i_commitMedia();
4720
4721 mData->mRegistered = false;
4722
4723 // machine lock no longer needed
4724 alock.release();
4725
4726 /* Make sure that the settings of the current VM are not saved, because
4727 * they are rather crippled at this point to meet the cleanup expectations
4728 * and there's no point destroying the VM config on disk just because. */
4729 mParent->i_unmarkRegistryModified(id);
4730
4731 // return media to caller
4732 aMedia.resize(llMedia.size());
4733 size_t i = 0;
4734 for (MediaList::const_iterator
4735 it = llMedia.begin();
4736 it != llMedia.end();
4737 ++it, ++i)
4738 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4739
4740 mParent->i_unregisterMachine(this, aCleanupMode, id);
4741 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4742
4743 return S_OK;
4744}
4745
4746/**
4747 * Task record for deleting a machine config.
4748 */
4749class Machine::DeleteConfigTask
4750 : public Machine::Task
4751{
4752public:
4753 DeleteConfigTask(Machine *m,
4754 Progress *p,
4755 const Utf8Str &t,
4756 const RTCList<ComPtr<IMedium> > &llMedia,
4757 const StringsList &llFilesToDelete)
4758 : Task(m, p, t),
4759 m_llMedia(llMedia),
4760 m_llFilesToDelete(llFilesToDelete)
4761 {}
4762
4763private:
4764 void handler()
4765 {
4766 try
4767 {
4768 m_pMachine->i_deleteConfigHandler(*this);
4769 }
4770 catch (...)
4771 {
4772 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4773 }
4774 }
4775
4776 RTCList<ComPtr<IMedium> > m_llMedia;
4777 StringsList m_llFilesToDelete;
4778
4779 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4780};
4781
4782/**
4783 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4784 * SessionMachine::taskHandler().
4785 *
4786 * @note Locks this object for writing.
4787 *
4788 * @param task
4789 */
4790void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4791{
4792 LogFlowThisFuncEnter();
4793
4794 AutoCaller autoCaller(this);
4795 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4796 if (FAILED(autoCaller.hrc()))
4797 {
4798 /* we might have been uninitialized because the session was accidentally
4799 * closed by the client, so don't assert */
4800 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4801 task.m_pProgress->i_notifyComplete(hrc);
4802 LogFlowThisFuncLeave();
4803 return;
4804 }
4805
4806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4807
4808 HRESULT hrc;
4809 try
4810 {
4811 ULONG uLogHistoryCount = 3;
4812 ComPtr<ISystemProperties> systemProperties;
4813 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4814 if (FAILED(hrc)) throw hrc;
4815
4816 if (!systemProperties.isNull())
4817 {
4818 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4819 if (FAILED(hrc)) throw hrc;
4820 }
4821
4822 MachineState_T oldState = mData->mMachineState;
4823 i_setMachineState(MachineState_SettingUp);
4824 alock.release();
4825 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4826 {
4827 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4828 {
4829 AutoCaller mac(pMedium);
4830 if (FAILED(mac.hrc())) throw mac.hrc();
4831 Utf8Str strLocation = pMedium->i_getLocationFull();
4832 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4833 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4834 if (FAILED(hrc)) throw hrc;
4835 }
4836 if (pMedium->i_isMediumFormatFile())
4837 {
4838 ComPtr<IProgress> pProgress2;
4839 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4840 if (FAILED(hrc)) throw hrc;
4841 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4842 if (FAILED(hrc)) throw hrc;
4843 }
4844
4845 /* Close the medium, deliberately without checking the return
4846 * code, and without leaving any trace in the error info, as
4847 * a failure here is a very minor issue, which shouldn't happen
4848 * as above we even managed to delete the medium. */
4849 {
4850 ErrorInfoKeeper eik;
4851 pMedium->Close();
4852 }
4853 }
4854 i_setMachineState(oldState);
4855 alock.acquire();
4856
4857 // delete the files pushed on the task list by Machine::Delete()
4858 // (this includes saved states of the machine and snapshots and
4859 // medium storage files from the IMedium list passed in, and the
4860 // machine XML file)
4861 for (StringsList::const_iterator
4862 it = task.m_llFilesToDelete.begin();
4863 it != task.m_llFilesToDelete.end();
4864 ++it)
4865 {
4866 const Utf8Str &strFile = *it;
4867 LogFunc(("Deleting file %s\n", strFile.c_str()));
4868 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4869 if (FAILED(hrc)) throw hrc;
4870 i_deleteFile(strFile);
4871 }
4872
4873 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4874 if (FAILED(hrc)) throw hrc;
4875
4876 /* delete the settings only when the file actually exists */
4877 if (mData->pMachineConfigFile->fileExists())
4878 {
4879 /* Delete any backup or uncommitted XML files. Ignore failures.
4880 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4881 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4882 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4883 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4884 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4885 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4886
4887 /* delete the Logs folder, nothing important should be left
4888 * there (we don't check for errors because the user might have
4889 * some private files there that we don't want to delete) */
4890 Utf8Str logFolder;
4891 getLogFolder(logFolder);
4892 Assert(logFolder.length());
4893 if (RTDirExists(logFolder.c_str()))
4894 {
4895 /* Delete all VBox.log[.N] files from the Logs folder
4896 * (this must be in sync with the rotation logic in
4897 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4898 * files that may have been created by the GUI. */
4899 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4900 i_deleteFile(log, true /* fIgnoreFailures */);
4901 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4902 i_deleteFile(log, true /* fIgnoreFailures */);
4903 for (ULONG i = uLogHistoryCount; i > 0; i--)
4904 {
4905 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4906 i_deleteFile(log, true /* fIgnoreFailures */);
4907 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4908 i_deleteFile(log, true /* fIgnoreFailures */);
4909 }
4910 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4911 i_deleteFile(log, true /* fIgnoreFailures */);
4912#if defined(RT_OS_WINDOWS)
4913 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4914 i_deleteFile(log, true /* fIgnoreFailures */);
4915 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4916 i_deleteFile(log, true /* fIgnoreFailures */);
4917#endif
4918
4919 RTDirRemove(logFolder.c_str());
4920 }
4921
4922 /* delete the Snapshots folder, nothing important should be left
4923 * there (we don't check for errors because the user might have
4924 * some private files there that we don't want to delete) */
4925 Utf8Str strFullSnapshotFolder;
4926 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4927 Assert(!strFullSnapshotFolder.isEmpty());
4928 if (RTDirExists(strFullSnapshotFolder.c_str()))
4929 RTDirRemove(strFullSnapshotFolder.c_str());
4930
4931 // delete the directory that contains the settings file, but only
4932 // if it matches the VM name
4933 Utf8Str settingsDir;
4934 if (i_isInOwnDir(&settingsDir))
4935 RTDirRemove(settingsDir.c_str());
4936 }
4937
4938 alock.release();
4939
4940 mParent->i_saveModifiedRegistries();
4941 }
4942 catch (HRESULT hrcXcpt)
4943 {
4944 hrc = hrcXcpt;
4945 }
4946
4947 task.m_pProgress->i_notifyComplete(hrc);
4948
4949 LogFlowThisFuncLeave();
4950}
4951
4952HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4953{
4954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4955
4956 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4957 if (FAILED(hrc)) return hrc;
4958
4959 if (mData->mRegistered)
4960 return setError(VBOX_E_INVALID_VM_STATE,
4961 tr("Cannot delete settings of a registered machine"));
4962
4963 // collect files to delete
4964 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4965 // machine config file
4966 if (mData->pMachineConfigFile->fileExists())
4967 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4968 // backup of machine config file
4969 Utf8Str strTmp(mData->m_strConfigFileFull);
4970 strTmp.append("-prev");
4971 if (RTFileExists(strTmp.c_str()))
4972 llFilesToDelete.push_back(strTmp);
4973
4974 RTCList<ComPtr<IMedium> > llMedia;
4975 for (size_t i = 0; i < aMedia.size(); ++i)
4976 {
4977 IMedium *pIMedium(aMedia[i]);
4978 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4979 if (pMedium.isNull())
4980 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4981 SafeArray<BSTR> ids;
4982 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4983 if (FAILED(hrc)) return hrc;
4984 /* At this point the medium should not have any back references
4985 * anymore. If it has it is attached to another VM and *must* not
4986 * deleted. */
4987 if (ids.size() < 1)
4988 llMedia.append(pMedium);
4989 }
4990
4991 ComObjPtr<Progress> pProgress;
4992 pProgress.createObject();
4993 hrc = pProgress->init(i_getVirtualBox(),
4994 static_cast<IMachine*>(this) /* aInitiator */,
4995 tr("Deleting files"),
4996 true /* fCancellable */,
4997 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4998 tr("Collecting file inventory"));
4999 if (FAILED(hrc))
5000 return hrc;
5001
5002 /* create and start the task on a separate thread (note that it will not
5003 * start working until we release alock) */
5004 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
5005 hrc = pTask->createThread();
5006 pTask = NULL;
5007 if (FAILED(hrc))
5008 return hrc;
5009
5010 pProgress.queryInterfaceTo(aProgress.asOutParam());
5011
5012 LogFlowFuncLeave();
5013
5014 return S_OK;
5015}
5016
5017HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5018{
5019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5020
5021 ComObjPtr<Snapshot> pSnapshot;
5022 HRESULT hrc;
5023
5024 if (aNameOrId.isEmpty())
5025 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5026 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5027 else
5028 {
5029 Guid uuid(aNameOrId);
5030 if (uuid.isValid())
5031 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5032 else
5033 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5034 }
5035 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5036
5037 return hrc;
5038}
5039
5040HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5041 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5042{
5043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5044
5045 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5046 if (FAILED(hrc)) return hrc;
5047
5048 ComObjPtr<SharedFolder> sharedFolder;
5049 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5050 if (SUCCEEDED(hrc))
5051 return setError(VBOX_E_OBJECT_IN_USE,
5052 tr("Shared folder named '%s' already exists"),
5053 aName.c_str());
5054
5055 sharedFolder.createObject();
5056 hrc = sharedFolder->init(i_getMachine(),
5057 aName,
5058 aHostPath,
5059 !!aWritable,
5060 !!aAutomount,
5061 aAutoMountPoint,
5062 true /* fFailOnError */);
5063 if (FAILED(hrc)) return hrc;
5064
5065 i_setModified(IsModified_SharedFolders);
5066 mHWData.backup();
5067 mHWData->mSharedFolders.push_back(sharedFolder);
5068
5069 /* inform the direct session if any */
5070 alock.release();
5071 i_onSharedFolderChange();
5072
5073 return S_OK;
5074}
5075
5076HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5077{
5078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5079
5080 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5081 if (FAILED(hrc)) return hrc;
5082
5083 ComObjPtr<SharedFolder> sharedFolder;
5084 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5085 if (FAILED(hrc)) return hrc;
5086
5087 i_setModified(IsModified_SharedFolders);
5088 mHWData.backup();
5089 mHWData->mSharedFolders.remove(sharedFolder);
5090
5091 /* inform the direct session if any */
5092 alock.release();
5093 i_onSharedFolderChange();
5094
5095 return S_OK;
5096}
5097
5098HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5099{
5100 /* start with No */
5101 *aCanShow = FALSE;
5102
5103 ComPtr<IInternalSessionControl> directControl;
5104 {
5105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5106
5107 if (mData->mSession.mState != SessionState_Locked)
5108 return setError(VBOX_E_INVALID_VM_STATE,
5109 tr("Machine is not locked for session (session state: %s)"),
5110 Global::stringifySessionState(mData->mSession.mState));
5111
5112 if (mData->mSession.mLockType == LockType_VM)
5113 directControl = mData->mSession.mDirectControl;
5114 }
5115
5116 /* ignore calls made after #OnSessionEnd() is called */
5117 if (!directControl)
5118 return S_OK;
5119
5120 LONG64 dummy;
5121 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5122}
5123
5124HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5125{
5126 ComPtr<IInternalSessionControl> directControl;
5127 {
5128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5129
5130 if (mData->mSession.mState != SessionState_Locked)
5131 return setError(E_FAIL,
5132 tr("Machine is not locked for session (session state: %s)"),
5133 Global::stringifySessionState(mData->mSession.mState));
5134
5135 if (mData->mSession.mLockType == LockType_VM)
5136 directControl = mData->mSession.mDirectControl;
5137 }
5138
5139 /* ignore calls made after #OnSessionEnd() is called */
5140 if (!directControl)
5141 return S_OK;
5142
5143 BOOL dummy;
5144 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5145}
5146
5147#ifdef VBOX_WITH_GUEST_PROPS
5148/**
5149 * Look up a guest property in VBoxSVC's internal structures.
5150 */
5151HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5152 com::Utf8Str &aValue,
5153 LONG64 *aTimestamp,
5154 com::Utf8Str &aFlags) const
5155{
5156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5159 if (it != mHWData->mGuestProperties.end())
5160 {
5161 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5162 aValue = it->second.strValue;
5163 *aTimestamp = it->second.mTimestamp;
5164 GuestPropWriteFlags(it->second.mFlags, szFlags);
5165 aFlags = Utf8Str(szFlags);
5166 }
5167
5168 return S_OK;
5169}
5170
5171/**
5172 * Query the VM that a guest property belongs to for the property.
5173 * @returns E_ACCESSDENIED if the VM process is not available or not
5174 * currently handling queries and the lookup should then be done in
5175 * VBoxSVC.
5176 */
5177HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5178 com::Utf8Str &aValue,
5179 LONG64 *aTimestamp,
5180 com::Utf8Str &aFlags) const
5181{
5182 HRESULT hrc = S_OK;
5183 Bstr bstrValue;
5184 Bstr bstrFlags;
5185
5186 ComPtr<IInternalSessionControl> directControl;
5187 {
5188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5189 if (mData->mSession.mLockType == LockType_VM)
5190 directControl = mData->mSession.mDirectControl;
5191 }
5192
5193 /* ignore calls made after #OnSessionEnd() is called */
5194 if (!directControl)
5195 hrc = E_ACCESSDENIED;
5196 else
5197 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5198 0 /* accessMode */,
5199 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5200
5201 aValue = bstrValue;
5202 aFlags = bstrFlags;
5203
5204 return hrc;
5205}
5206#endif // VBOX_WITH_GUEST_PROPS
5207
5208HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5209 com::Utf8Str &aValue,
5210 LONG64 *aTimestamp,
5211 com::Utf8Str &aFlags)
5212{
5213#ifndef VBOX_WITH_GUEST_PROPS
5214 ReturnComNotImplemented();
5215#else // VBOX_WITH_GUEST_PROPS
5216
5217 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5218
5219 if (hrc == E_ACCESSDENIED)
5220 /* The VM is not running or the service is not (yet) accessible */
5221 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5222 return hrc;
5223#endif // VBOX_WITH_GUEST_PROPS
5224}
5225
5226HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5227{
5228 LONG64 dummyTimestamp;
5229 com::Utf8Str dummyFlags;
5230 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5231
5232}
5233HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5234{
5235 com::Utf8Str dummyFlags;
5236 com::Utf8Str dummyValue;
5237 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5238}
5239
5240#ifdef VBOX_WITH_GUEST_PROPS
5241/**
5242 * Set a guest property in VBoxSVC's internal structures.
5243 */
5244HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5245 const com::Utf8Str &aFlags, bool fDelete)
5246{
5247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5248 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5249 if (FAILED(hrc)) return hrc;
5250
5251 try
5252 {
5253 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5254 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5255 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5256
5257 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5258 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5259
5260 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5261 if (it == mHWData->mGuestProperties.end())
5262 {
5263 if (!fDelete)
5264 {
5265 i_setModified(IsModified_MachineData);
5266 mHWData.backupEx();
5267
5268 RTTIMESPEC time;
5269 HWData::GuestProperty prop;
5270 prop.strValue = aValue;
5271 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5272 prop.mFlags = fFlags;
5273 mHWData->mGuestProperties[aName] = prop;
5274 }
5275 }
5276 else
5277 {
5278 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5279 {
5280 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5281 }
5282 else
5283 {
5284 i_setModified(IsModified_MachineData);
5285 mHWData.backupEx();
5286
5287 /* The backupEx() operation invalidates our iterator,
5288 * so get a new one. */
5289 it = mHWData->mGuestProperties.find(aName);
5290 Assert(it != mHWData->mGuestProperties.end());
5291
5292 if (!fDelete)
5293 {
5294 RTTIMESPEC time;
5295 it->second.strValue = aValue;
5296 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5297 it->second.mFlags = fFlags;
5298 }
5299 else
5300 mHWData->mGuestProperties.erase(it);
5301 }
5302 }
5303
5304 if (SUCCEEDED(hrc))
5305 {
5306 alock.release();
5307
5308 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5309 }
5310 }
5311 catch (std::bad_alloc &)
5312 {
5313 hrc = E_OUTOFMEMORY;
5314 }
5315
5316 return hrc;
5317}
5318
5319/**
5320 * Set a property on the VM that that property belongs to.
5321 * @returns E_ACCESSDENIED if the VM process is not available or not
5322 * currently handling queries and the setting should then be done in
5323 * VBoxSVC.
5324 */
5325HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5326 const com::Utf8Str &aFlags, bool fDelete)
5327{
5328 HRESULT hrc;
5329
5330 try
5331 {
5332 ComPtr<IInternalSessionControl> directControl;
5333 {
5334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5335 if (mData->mSession.mLockType == LockType_VM)
5336 directControl = mData->mSession.mDirectControl;
5337 }
5338
5339 Bstr dummy1; /* will not be changed (setter) */
5340 Bstr dummy2; /* will not be changed (setter) */
5341 LONG64 dummy64;
5342 if (!directControl)
5343 hrc = E_ACCESSDENIED;
5344 else
5345 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5346 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5347 fDelete ? 2 : 1 /* accessMode */,
5348 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5349 }
5350 catch (std::bad_alloc &)
5351 {
5352 hrc = E_OUTOFMEMORY;
5353 }
5354
5355 return hrc;
5356}
5357#endif // VBOX_WITH_GUEST_PROPS
5358
5359HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5360 const com::Utf8Str &aFlags)
5361{
5362#ifndef VBOX_WITH_GUEST_PROPS
5363 ReturnComNotImplemented();
5364#else // VBOX_WITH_GUEST_PROPS
5365
5366 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5367 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5368
5369 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5370 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5371
5372 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5373 if (hrc == E_ACCESSDENIED)
5374 /* The VM is not running or the service is not (yet) accessible */
5375 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5376 return hrc;
5377#endif // VBOX_WITH_GUEST_PROPS
5378}
5379
5380HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5381{
5382 return setGuestProperty(aProperty, aValue, "");
5383}
5384
5385HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5386{
5387#ifndef VBOX_WITH_GUEST_PROPS
5388 ReturnComNotImplemented();
5389#else // VBOX_WITH_GUEST_PROPS
5390 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5391 if (hrc == E_ACCESSDENIED)
5392 /* The VM is not running or the service is not (yet) accessible */
5393 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5394 return hrc;
5395#endif // VBOX_WITH_GUEST_PROPS
5396}
5397
5398#ifdef VBOX_WITH_GUEST_PROPS
5399/**
5400 * Enumerate the guest properties in VBoxSVC's internal structures.
5401 */
5402HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5403 std::vector<com::Utf8Str> &aNames,
5404 std::vector<com::Utf8Str> &aValues,
5405 std::vector<LONG64> &aTimestamps,
5406 std::vector<com::Utf8Str> &aFlags)
5407{
5408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5409 Utf8Str strPatterns(aPatterns);
5410
5411 /*
5412 * Look for matching patterns and build up a list.
5413 */
5414 HWData::GuestPropertyMap propMap;
5415 for (HWData::GuestPropertyMap::const_iterator
5416 it = mHWData->mGuestProperties.begin();
5417 it != mHWData->mGuestProperties.end();
5418 ++it)
5419 {
5420 if ( strPatterns.isEmpty()
5421 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5422 RTSTR_MAX,
5423 it->first.c_str(),
5424 RTSTR_MAX,
5425 NULL)
5426 )
5427 propMap.insert(*it);
5428 }
5429
5430 alock.release();
5431
5432 /*
5433 * And build up the arrays for returning the property information.
5434 */
5435 size_t cEntries = propMap.size();
5436
5437 aNames.resize(cEntries);
5438 aValues.resize(cEntries);
5439 aTimestamps.resize(cEntries);
5440 aFlags.resize(cEntries);
5441
5442 size_t i = 0;
5443 for (HWData::GuestPropertyMap::const_iterator
5444 it = propMap.begin();
5445 it != propMap.end();
5446 ++it, ++i)
5447 {
5448 aNames[i] = it->first;
5449 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5450 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5451
5452 aValues[i] = it->second.strValue;
5453 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5454 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5455
5456 aTimestamps[i] = it->second.mTimestamp;
5457
5458 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5459 GuestPropWriteFlags(it->second.mFlags, szFlags);
5460 aFlags[i] = szFlags;
5461 }
5462
5463 return S_OK;
5464}
5465
5466/**
5467 * Enumerate the properties managed by a VM.
5468 * @returns E_ACCESSDENIED if the VM process is not available or not
5469 * currently handling queries and the setting should then be done in
5470 * VBoxSVC.
5471 */
5472HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5473 std::vector<com::Utf8Str> &aNames,
5474 std::vector<com::Utf8Str> &aValues,
5475 std::vector<LONG64> &aTimestamps,
5476 std::vector<com::Utf8Str> &aFlags)
5477{
5478 HRESULT hrc;
5479 ComPtr<IInternalSessionControl> directControl;
5480 {
5481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5482 if (mData->mSession.mLockType == LockType_VM)
5483 directControl = mData->mSession.mDirectControl;
5484 }
5485
5486 com::SafeArray<BSTR> bNames;
5487 com::SafeArray<BSTR> bValues;
5488 com::SafeArray<LONG64> bTimestamps;
5489 com::SafeArray<BSTR> bFlags;
5490
5491 if (!directControl)
5492 hrc = E_ACCESSDENIED;
5493 else
5494 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5495 ComSafeArrayAsOutParam(bNames),
5496 ComSafeArrayAsOutParam(bValues),
5497 ComSafeArrayAsOutParam(bTimestamps),
5498 ComSafeArrayAsOutParam(bFlags));
5499 size_t i;
5500 aNames.resize(bNames.size());
5501 for (i = 0; i < bNames.size(); ++i)
5502 aNames[i] = Utf8Str(bNames[i]);
5503 aValues.resize(bValues.size());
5504 for (i = 0; i < bValues.size(); ++i)
5505 aValues[i] = Utf8Str(bValues[i]);
5506 aTimestamps.resize(bTimestamps.size());
5507 for (i = 0; i < bTimestamps.size(); ++i)
5508 aTimestamps[i] = bTimestamps[i];
5509 aFlags.resize(bFlags.size());
5510 for (i = 0; i < bFlags.size(); ++i)
5511 aFlags[i] = Utf8Str(bFlags[i]);
5512
5513 return hrc;
5514}
5515#endif // VBOX_WITH_GUEST_PROPS
5516HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5517 std::vector<com::Utf8Str> &aNames,
5518 std::vector<com::Utf8Str> &aValues,
5519 std::vector<LONG64> &aTimestamps,
5520 std::vector<com::Utf8Str> &aFlags)
5521{
5522#ifndef VBOX_WITH_GUEST_PROPS
5523 ReturnComNotImplemented();
5524#else // VBOX_WITH_GUEST_PROPS
5525
5526 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5527
5528 if (hrc == E_ACCESSDENIED)
5529 /* The VM is not running or the service is not (yet) accessible */
5530 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5531 return hrc;
5532#endif // VBOX_WITH_GUEST_PROPS
5533}
5534
5535HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5536 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5537{
5538 MediumAttachmentList atts;
5539
5540 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5541 if (FAILED(hrc)) return hrc;
5542
5543 aMediumAttachments.resize(atts.size());
5544 size_t i = 0;
5545 for (MediumAttachmentList::const_iterator
5546 it = atts.begin();
5547 it != atts.end();
5548 ++it, ++i)
5549 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5550
5551 return S_OK;
5552}
5553
5554HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5555 LONG aControllerPort,
5556 LONG aDevice,
5557 ComPtr<IMediumAttachment> &aAttachment)
5558{
5559 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5560 aName.c_str(), aControllerPort, aDevice));
5561
5562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5563
5564 aAttachment = NULL;
5565
5566 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5567 aName,
5568 aControllerPort,
5569 aDevice);
5570 if (pAttach.isNull())
5571 return setError(VBOX_E_OBJECT_NOT_FOUND,
5572 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5573 aDevice, aControllerPort, aName.c_str());
5574
5575 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5576
5577 return S_OK;
5578}
5579
5580
5581HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5582 StorageBus_T aConnectionType,
5583 ComPtr<IStorageController> &aController)
5584{
5585 if ( (aConnectionType <= StorageBus_Null)
5586 || (aConnectionType > StorageBus_VirtioSCSI))
5587 return setError(E_INVALIDARG,
5588 tr("Invalid connection type: %d"),
5589 aConnectionType);
5590
5591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5592
5593 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5594 if (FAILED(hrc)) return hrc;
5595
5596 /* try to find one with the name first. */
5597 ComObjPtr<StorageController> ctrl;
5598
5599 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5600 if (SUCCEEDED(hrc))
5601 return setError(VBOX_E_OBJECT_IN_USE,
5602 tr("Storage controller named '%s' already exists"),
5603 aName.c_str());
5604
5605 ctrl.createObject();
5606
5607 /* get a new instance number for the storage controller */
5608 ULONG ulInstance = 0;
5609 bool fBootable = true;
5610 for (StorageControllerList::const_iterator
5611 it = mStorageControllers->begin();
5612 it != mStorageControllers->end();
5613 ++it)
5614 {
5615 if ((*it)->i_getStorageBus() == aConnectionType)
5616 {
5617 ULONG ulCurInst = (*it)->i_getInstance();
5618
5619 if (ulCurInst >= ulInstance)
5620 ulInstance = ulCurInst + 1;
5621
5622 /* Only one controller of each type can be marked as bootable. */
5623 if ((*it)->i_getBootable())
5624 fBootable = false;
5625 }
5626 }
5627
5628 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5629 if (FAILED(hrc)) return hrc;
5630
5631 i_setModified(IsModified_Storage);
5632 mStorageControllers.backup();
5633 mStorageControllers->push_back(ctrl);
5634
5635 ctrl.queryInterfaceTo(aController.asOutParam());
5636
5637 /* inform the direct session if any */
5638 alock.release();
5639 i_onStorageControllerChange(i_getId(), aName);
5640
5641 return S_OK;
5642}
5643
5644HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5645 ComPtr<IStorageController> &aStorageController)
5646{
5647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5648
5649 ComObjPtr<StorageController> ctrl;
5650
5651 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5652 if (SUCCEEDED(hrc))
5653 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5654
5655 return hrc;
5656}
5657
5658HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5659 ULONG aInstance,
5660 ComPtr<IStorageController> &aStorageController)
5661{
5662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5663
5664 for (StorageControllerList::const_iterator
5665 it = mStorageControllers->begin();
5666 it != mStorageControllers->end();
5667 ++it)
5668 {
5669 if ( (*it)->i_getStorageBus() == aConnectionType
5670 && (*it)->i_getInstance() == aInstance)
5671 {
5672 (*it).queryInterfaceTo(aStorageController.asOutParam());
5673 return S_OK;
5674 }
5675 }
5676
5677 return setError(VBOX_E_OBJECT_NOT_FOUND,
5678 tr("Could not find a storage controller with instance number '%lu'"),
5679 aInstance);
5680}
5681
5682HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5683{
5684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5685
5686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5687 if (FAILED(hrc)) return hrc;
5688
5689 ComObjPtr<StorageController> ctrl;
5690
5691 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5692 if (SUCCEEDED(hrc))
5693 {
5694 /* Ensure that only one controller of each type is marked as bootable. */
5695 if (aBootable == TRUE)
5696 {
5697 for (StorageControllerList::const_iterator
5698 it = mStorageControllers->begin();
5699 it != mStorageControllers->end();
5700 ++it)
5701 {
5702 ComObjPtr<StorageController> aCtrl = (*it);
5703
5704 if ( (aCtrl->i_getName() != aName)
5705 && aCtrl->i_getBootable() == TRUE
5706 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5707 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5708 {
5709 aCtrl->i_setBootable(FALSE);
5710 break;
5711 }
5712 }
5713 }
5714
5715 if (SUCCEEDED(hrc))
5716 {
5717 ctrl->i_setBootable(aBootable);
5718 i_setModified(IsModified_Storage);
5719 }
5720 }
5721
5722 if (SUCCEEDED(hrc))
5723 {
5724 /* inform the direct session if any */
5725 alock.release();
5726 i_onStorageControllerChange(i_getId(), aName);
5727 }
5728
5729 return hrc;
5730}
5731
5732HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5733{
5734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5735
5736 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5737 if (FAILED(hrc)) return hrc;
5738
5739 ComObjPtr<StorageController> ctrl;
5740 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5741 if (FAILED(hrc)) return hrc;
5742
5743 MediumAttachmentList llDetachedAttachments;
5744 {
5745 /* find all attached devices to the appropriate storage controller and detach them all */
5746 // make a temporary list because detachDevice invalidates iterators into
5747 // mMediumAttachments
5748 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5749
5750 for (MediumAttachmentList::const_iterator
5751 it = llAttachments2.begin();
5752 it != llAttachments2.end();
5753 ++it)
5754 {
5755 MediumAttachment *pAttachTemp = *it;
5756
5757 AutoCaller localAutoCaller(pAttachTemp);
5758 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
5759
5760 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5761
5762 if (pAttachTemp->i_getControllerName() == aName)
5763 {
5764 llDetachedAttachments.push_back(pAttachTemp);
5765 hrc = i_detachDevice(pAttachTemp, alock, NULL);
5766 if (FAILED(hrc)) return hrc;
5767 }
5768 }
5769 }
5770
5771 /* send event about detached devices before removing parent controller */
5772 for (MediumAttachmentList::const_iterator
5773 it = llDetachedAttachments.begin();
5774 it != llDetachedAttachments.end();
5775 ++it)
5776 {
5777 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5778 }
5779
5780 /* We can remove it now. */
5781 i_setModified(IsModified_Storage);
5782 mStorageControllers.backup();
5783
5784 ctrl->i_unshare();
5785
5786 mStorageControllers->remove(ctrl);
5787
5788 /* inform the direct session if any */
5789 alock.release();
5790 i_onStorageControllerChange(i_getId(), aName);
5791
5792 return S_OK;
5793}
5794
5795HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5796 ComPtr<IUSBController> &aController)
5797{
5798 if ( (aType <= USBControllerType_Null)
5799 || (aType >= USBControllerType_Last))
5800 return setError(E_INVALIDARG,
5801 tr("Invalid USB controller type: %d"),
5802 aType);
5803
5804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5805
5806 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5807 if (FAILED(hrc)) return hrc;
5808
5809 /* try to find one with the same type first. */
5810 ComObjPtr<USBController> ctrl;
5811
5812 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5813 if (SUCCEEDED(hrc))
5814 return setError(VBOX_E_OBJECT_IN_USE,
5815 tr("USB controller named '%s' already exists"),
5816 aName.c_str());
5817
5818 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5819 ChipsetType_T enmChipsetType;
5820 hrc = mPlatform->getChipsetType(&enmChipsetType);
5821 if (FAILED(hrc))
5822 return hrc;
5823
5824 ULONG maxInstances;
5825 hrc = mPlatformProperties->GetMaxInstancesOfUSBControllerType(enmChipsetType, aType, &maxInstances);
5826 if (FAILED(hrc))
5827 return hrc;
5828
5829 ULONG cInstances = i_getUSBControllerCountByType(aType);
5830 if (cInstances >= maxInstances)
5831 return setError(E_INVALIDARG,
5832 tr("Too many USB controllers of this type"));
5833
5834 ctrl.createObject();
5835
5836 hrc = ctrl->init(this, aName, aType);
5837 if (FAILED(hrc)) return hrc;
5838
5839 i_setModified(IsModified_USB);
5840 mUSBControllers.backup();
5841 mUSBControllers->push_back(ctrl);
5842
5843 ctrl.queryInterfaceTo(aController.asOutParam());
5844
5845 /* inform the direct session if any */
5846 alock.release();
5847 i_onUSBControllerChange();
5848
5849 return S_OK;
5850}
5851
5852HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
5853{
5854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5855
5856 ComObjPtr<USBController> ctrl;
5857
5858 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5859 if (SUCCEEDED(hrc))
5860 ctrl.queryInterfaceTo(aController.asOutParam());
5861
5862 return hrc;
5863}
5864
5865HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
5866 ULONG *aControllers)
5867{
5868 if ( (aType <= USBControllerType_Null)
5869 || (aType >= USBControllerType_Last))
5870 return setError(E_INVALIDARG,
5871 tr("Invalid USB controller type: %d"),
5872 aType);
5873
5874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5875
5876 ComObjPtr<USBController> ctrl;
5877
5878 *aControllers = i_getUSBControllerCountByType(aType);
5879
5880 return S_OK;
5881}
5882
5883HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
5884{
5885
5886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5887
5888 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5889 if (FAILED(hrc)) return hrc;
5890
5891 ComObjPtr<USBController> ctrl;
5892 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5893 if (FAILED(hrc)) return hrc;
5894
5895 i_setModified(IsModified_USB);
5896 mUSBControllers.backup();
5897
5898 ctrl->i_unshare();
5899
5900 mUSBControllers->remove(ctrl);
5901
5902 /* inform the direct session if any */
5903 alock.release();
5904 i_onUSBControllerChange();
5905
5906 return S_OK;
5907}
5908
5909HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
5910 ULONG *aOriginX,
5911 ULONG *aOriginY,
5912 ULONG *aWidth,
5913 ULONG *aHeight,
5914 BOOL *aEnabled)
5915{
5916 uint32_t u32OriginX= 0;
5917 uint32_t u32OriginY= 0;
5918 uint32_t u32Width = 0;
5919 uint32_t u32Height = 0;
5920 uint16_t u16Flags = 0;
5921
5922#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5923 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5924#else
5925 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5926#endif
5927 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
5928 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5929 if (RT_FAILURE(vrc))
5930 {
5931#ifdef RT_OS_WINDOWS
5932 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5933 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5934 * So just assign fEnable to TRUE again.
5935 * The right fix would be to change GUI API wrappers to make sure that parameters
5936 * are changed only if API succeeds.
5937 */
5938 *aEnabled = TRUE;
5939#endif
5940 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5941 tr("Saved guest size is not available (%Rrc)"),
5942 vrc);
5943 }
5944
5945 *aOriginX = u32OriginX;
5946 *aOriginY = u32OriginY;
5947 *aWidth = u32Width;
5948 *aHeight = u32Height;
5949 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5950
5951 return S_OK;
5952}
5953
5954HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
5955 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
5956{
5957 if (aScreenId != 0)
5958 return E_NOTIMPL;
5959
5960 if ( aBitmapFormat != BitmapFormat_BGR0
5961 && aBitmapFormat != BitmapFormat_BGRA
5962 && aBitmapFormat != BitmapFormat_RGBA
5963 && aBitmapFormat != BitmapFormat_PNG)
5964 return setError(E_NOTIMPL,
5965 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
5966
5967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5968
5969 uint8_t *pu8Data = NULL;
5970 uint32_t cbData = 0;
5971 uint32_t u32Width = 0;
5972 uint32_t u32Height = 0;
5973
5974#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5975 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5976#else
5977 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5978#endif
5979 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
5980 &pu8Data, &cbData, &u32Width, &u32Height);
5981 if (RT_FAILURE(vrc))
5982 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5983 tr("Saved thumbnail data is not available (%Rrc)"),
5984 vrc);
5985
5986 HRESULT hrc = S_OK;
5987
5988 *aWidth = u32Width;
5989 *aHeight = u32Height;
5990
5991 if (cbData > 0)
5992 {
5993 /* Convert pixels to the format expected by the API caller. */
5994 if (aBitmapFormat == BitmapFormat_BGR0)
5995 {
5996 /* [0] B, [1] G, [2] R, [3] 0. */
5997 aData.resize(cbData);
5998 memcpy(&aData.front(), pu8Data, cbData);
5999 }
6000 else if (aBitmapFormat == BitmapFormat_BGRA)
6001 {
6002 /* [0] B, [1] G, [2] R, [3] A. */
6003 aData.resize(cbData);
6004 for (uint32_t i = 0; i < cbData; i += 4)
6005 {
6006 aData[i] = pu8Data[i];
6007 aData[i + 1] = pu8Data[i + 1];
6008 aData[i + 2] = pu8Data[i + 2];
6009 aData[i + 3] = 0xff;
6010 }
6011 }
6012 else if (aBitmapFormat == BitmapFormat_RGBA)
6013 {
6014 /* [0] R, [1] G, [2] B, [3] A. */
6015 aData.resize(cbData);
6016 for (uint32_t i = 0; i < cbData; i += 4)
6017 {
6018 aData[i] = pu8Data[i + 2];
6019 aData[i + 1] = pu8Data[i + 1];
6020 aData[i + 2] = pu8Data[i];
6021 aData[i + 3] = 0xff;
6022 }
6023 }
6024 else if (aBitmapFormat == BitmapFormat_PNG)
6025 {
6026 uint8_t *pu8PNG = NULL;
6027 uint32_t cbPNG = 0;
6028 uint32_t cxPNG = 0;
6029 uint32_t cyPNG = 0;
6030
6031 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6032
6033 if (RT_SUCCESS(vrc))
6034 {
6035 aData.resize(cbPNG);
6036 if (cbPNG)
6037 memcpy(&aData.front(), pu8PNG, cbPNG);
6038 }
6039 else
6040 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6041
6042 RTMemFree(pu8PNG);
6043 }
6044 }
6045
6046 freeSavedDisplayScreenshot(pu8Data);
6047
6048 return hrc;
6049}
6050
6051HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6052 ULONG *aWidth,
6053 ULONG *aHeight,
6054 std::vector<BitmapFormat_T> &aBitmapFormats)
6055{
6056 if (aScreenId != 0)
6057 return E_NOTIMPL;
6058
6059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6060
6061 uint8_t *pu8Data = NULL;
6062 uint32_t cbData = 0;
6063 uint32_t u32Width = 0;
6064 uint32_t u32Height = 0;
6065
6066#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6067 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6068#else
6069 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6070#endif
6071 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6072 &pu8Data, &cbData, &u32Width, &u32Height);
6073
6074 if (RT_FAILURE(vrc))
6075 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6076 tr("Saved screenshot data is not available (%Rrc)"),
6077 vrc);
6078
6079 *aWidth = u32Width;
6080 *aHeight = u32Height;
6081 aBitmapFormats.resize(1);
6082 aBitmapFormats[0] = BitmapFormat_PNG;
6083
6084 freeSavedDisplayScreenshot(pu8Data);
6085
6086 return S_OK;
6087}
6088
6089HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6090 BitmapFormat_T aBitmapFormat,
6091 ULONG *aWidth,
6092 ULONG *aHeight,
6093 std::vector<BYTE> &aData)
6094{
6095 if (aScreenId != 0)
6096 return E_NOTIMPL;
6097
6098 if (aBitmapFormat != BitmapFormat_PNG)
6099 return E_NOTIMPL;
6100
6101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6102
6103 uint8_t *pu8Data = NULL;
6104 uint32_t cbData = 0;
6105 uint32_t u32Width = 0;
6106 uint32_t u32Height = 0;
6107
6108#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6109 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6110#else
6111 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6112#endif
6113 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6114 &pu8Data, &cbData, &u32Width, &u32Height);
6115
6116 if (RT_FAILURE(vrc))
6117 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6118 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6119 vrc);
6120
6121 *aWidth = u32Width;
6122 *aHeight = u32Height;
6123
6124 aData.resize(cbData);
6125 if (cbData)
6126 memcpy(&aData.front(), pu8Data, cbData);
6127
6128 freeSavedDisplayScreenshot(pu8Data);
6129
6130 return S_OK;
6131}
6132
6133HRESULT Machine::hotPlugCPU(ULONG aCpu)
6134{
6135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 if (!mHWData->mCPUHotPlugEnabled)
6138 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6139
6140 if (aCpu >= mHWData->mCPUCount)
6141 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6142
6143 if (mHWData->mCPUAttached[aCpu])
6144 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6145
6146 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6147 if (FAILED(hrc)) return hrc;
6148
6149 alock.release();
6150 hrc = i_onCPUChange(aCpu, false);
6151 alock.acquire();
6152 if (FAILED(hrc)) return hrc;
6153
6154 i_setModified(IsModified_MachineData);
6155 mHWData.backup();
6156 mHWData->mCPUAttached[aCpu] = true;
6157
6158 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6159 if (Global::IsOnline(mData->mMachineState))
6160 i_saveSettings(NULL, alock);
6161
6162 return S_OK;
6163}
6164
6165HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6166{
6167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6168
6169 if (!mHWData->mCPUHotPlugEnabled)
6170 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6171
6172 if (aCpu >= SchemaDefs::MaxCPUCount)
6173 return setError(E_INVALIDARG,
6174 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6175 SchemaDefs::MaxCPUCount);
6176
6177 if (!mHWData->mCPUAttached[aCpu])
6178 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6179
6180 /* CPU 0 can't be detached */
6181 if (aCpu == 0)
6182 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6183
6184 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6185 if (FAILED(hrc)) return hrc;
6186
6187 alock.release();
6188 hrc = i_onCPUChange(aCpu, true);
6189 alock.acquire();
6190 if (FAILED(hrc)) return hrc;
6191
6192 i_setModified(IsModified_MachineData);
6193 mHWData.backup();
6194 mHWData->mCPUAttached[aCpu] = false;
6195
6196 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6197 if (Global::IsOnline(mData->mMachineState))
6198 i_saveSettings(NULL, alock);
6199
6200 return S_OK;
6201}
6202
6203HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6204{
6205 *aAttached = false;
6206
6207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 /* If hotplug is enabled the CPU is always enabled. */
6210 if (!mHWData->mCPUHotPlugEnabled)
6211 {
6212 if (aCpu < mHWData->mCPUCount)
6213 *aAttached = true;
6214 }
6215 else
6216 {
6217 if (aCpu < SchemaDefs::MaxCPUCount)
6218 *aAttached = mHWData->mCPUAttached[aCpu];
6219 }
6220
6221 return S_OK;
6222}
6223
6224HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6225{
6226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6227
6228 Utf8Str log = i_getLogFilename(aIdx);
6229 if (!RTFileExists(log.c_str()))
6230 log.setNull();
6231 aFilename = log;
6232
6233 return S_OK;
6234}
6235
6236HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6237{
6238 if (aSize < 0)
6239 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6240
6241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6242
6243 HRESULT hrc = S_OK;
6244 Utf8Str log = i_getLogFilename(aIdx);
6245
6246 /* do not unnecessarily hold the lock while doing something which does
6247 * not need the lock and potentially takes a long time. */
6248 alock.release();
6249
6250 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6251 * keeps the SOAP reply size under 1M for the webservice (we're using
6252 * base64 encoded strings for binary data for years now, avoiding the
6253 * expansion of each byte array element to approx. 25 bytes of XML. */
6254 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6255 aData.resize(cbData);
6256
6257 int vrc = VINF_SUCCESS;
6258 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6259
6260#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6261 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6262 {
6263 PCVBOXCRYPTOIF pCryptoIf = NULL;
6264 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6265 if (SUCCEEDED(hrc))
6266 {
6267 alock.acquire();
6268
6269 SecretKey *pKey = NULL;
6270 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6271 alock.release();
6272
6273 if (RT_SUCCESS(vrc))
6274 {
6275 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6276 if (RT_SUCCESS(vrc))
6277 {
6278 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6279 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6280 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6281 if (RT_SUCCESS(vrc))
6282 {
6283 RTVfsIoStrmRelease(hVfsIosLog);
6284 hVfsIosLog = hVfsIosLogDec;
6285 }
6286 }
6287
6288 pKey->release();
6289 }
6290
6291 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6292 }
6293 }
6294 else
6295 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6296#else
6297 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6298#endif
6299 if (RT_SUCCESS(vrc))
6300 {
6301 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6302 cbData ? &aData.front() : NULL, cbData,
6303 true /*fBlocking*/, &cbData);
6304 if (RT_SUCCESS(vrc))
6305 aData.resize(cbData);
6306 else
6307 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6308
6309 RTVfsIoStrmRelease(hVfsIosLog);
6310 }
6311 else
6312 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6313
6314 if (FAILED(hrc))
6315 aData.resize(0);
6316
6317 return hrc;
6318}
6319
6320
6321/**
6322 * Currently this method doesn't attach device to the running VM,
6323 * just makes sure it's plugged on next VM start.
6324 */
6325HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6326{
6327 // lock scope
6328 {
6329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6330
6331 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6332 if (FAILED(hrc)) return hrc;
6333
6334 ChipsetType_T aChipset = ChipsetType_PIIX3; /** @todo BUGBUG ASSUMES x86! */
6335 hrc = mPlatform->COMGETTER(ChipsetType)(&aChipset);
6336 if (FAILED(hrc)) return hrc;
6337
6338 if (aChipset != ChipsetType_ICH9) /** @todo BUGBUG ASSUMES x86! */
6339 {
6340 return setError(E_INVALIDARG,
6341 tr("Host PCI attachment only supported with ICH9 chipset"));
6342 }
6343
6344 // check if device with this host PCI address already attached
6345 for (HWData::PCIDeviceAssignmentList::const_iterator
6346 it = mHWData->mPCIDeviceAssignments.begin();
6347 it != mHWData->mPCIDeviceAssignments.end();
6348 ++it)
6349 {
6350 LONG iHostAddress = -1;
6351 ComPtr<PCIDeviceAttachment> pAttach;
6352 pAttach = *it;
6353 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6354 if (iHostAddress == aHostAddress)
6355 return setError(E_INVALIDARG,
6356 tr("Device with host PCI address already attached to this VM"));
6357 }
6358
6359 ComObjPtr<PCIDeviceAttachment> pda;
6360 char name[32];
6361
6362 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6363 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6364 pda.createObject();
6365 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6366 i_setModified(IsModified_MachineData);
6367 mHWData.backup();
6368 mHWData->mPCIDeviceAssignments.push_back(pda);
6369 }
6370
6371 return S_OK;
6372}
6373
6374/**
6375 * Currently this method doesn't detach device from the running VM,
6376 * just makes sure it's not plugged on next VM start.
6377 */
6378HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6379{
6380 ComObjPtr<PCIDeviceAttachment> pAttach;
6381 bool fRemoved = false;
6382 HRESULT hrc;
6383
6384 // lock scope
6385 {
6386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6387
6388 hrc = i_checkStateDependency(MutableStateDep);
6389 if (FAILED(hrc)) return hrc;
6390
6391 for (HWData::PCIDeviceAssignmentList::const_iterator
6392 it = mHWData->mPCIDeviceAssignments.begin();
6393 it != mHWData->mPCIDeviceAssignments.end();
6394 ++it)
6395 {
6396 LONG iHostAddress = -1;
6397 pAttach = *it;
6398 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6399 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6400 {
6401 i_setModified(IsModified_MachineData);
6402 mHWData.backup();
6403 mHWData->mPCIDeviceAssignments.remove(pAttach);
6404 fRemoved = true;
6405 break;
6406 }
6407 }
6408 }
6409
6410
6411 /* Fire event outside of the lock */
6412 if (fRemoved)
6413 {
6414 Assert(!pAttach.isNull());
6415 ComPtr<IEventSource> es;
6416 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
6417 Assert(SUCCEEDED(hrc));
6418 Bstr mid;
6419 hrc = this->COMGETTER(Id)(mid.asOutParam());
6420 Assert(SUCCEEDED(hrc));
6421 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6422 }
6423
6424 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6425 tr("No host PCI device %08x attached"),
6426 aHostAddress
6427 );
6428}
6429
6430HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6431{
6432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6433
6434 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6435 size_t i = 0;
6436 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6437 it = mHWData->mPCIDeviceAssignments.begin();
6438 it != mHWData->mPCIDeviceAssignments.end();
6439 ++it, ++i)
6440 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6441
6442 return S_OK;
6443}
6444
6445HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6446{
6447 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6448
6449 return S_OK;
6450}
6451
6452HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6453{
6454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6455
6456 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6457
6458 return S_OK;
6459}
6460
6461HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6462{
6463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6464 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6465 if (SUCCEEDED(hrc))
6466 {
6467 hrc = mHWData.backupEx();
6468 if (SUCCEEDED(hrc))
6469 {
6470 i_setModified(IsModified_MachineData);
6471 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6472 }
6473 }
6474 return hrc;
6475}
6476
6477HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6478{
6479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6480 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6481 return S_OK;
6482}
6483
6484HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6485{
6486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6487 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6488 if (SUCCEEDED(hrc))
6489 {
6490 hrc = mHWData.backupEx();
6491 if (SUCCEEDED(hrc))
6492 {
6493 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6494 if (SUCCEEDED(hrc))
6495 i_setModified(IsModified_MachineData);
6496 }
6497 }
6498 return hrc;
6499}
6500
6501HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6502{
6503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6504
6505 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6506
6507 return S_OK;
6508}
6509
6510HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6511{
6512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6513 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6514 if (SUCCEEDED(hrc))
6515 {
6516 hrc = mHWData.backupEx();
6517 if (SUCCEEDED(hrc))
6518 {
6519 i_setModified(IsModified_MachineData);
6520 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6521 }
6522 }
6523 return hrc;
6524}
6525
6526HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6527{
6528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6529
6530 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6531
6532 return S_OK;
6533}
6534
6535HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6536{
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6540 if ( SUCCEEDED(hrc)
6541 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6542 {
6543 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6544 int vrc;
6545
6546 if (aAutostartEnabled)
6547 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6548 else
6549 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6550
6551 if (RT_SUCCESS(vrc))
6552 {
6553 hrc = mHWData.backupEx();
6554 if (SUCCEEDED(hrc))
6555 {
6556 i_setModified(IsModified_MachineData);
6557 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6558 }
6559 }
6560 else if (vrc == VERR_NOT_SUPPORTED)
6561 hrc = setError(VBOX_E_NOT_SUPPORTED,
6562 tr("The VM autostart feature is not supported on this platform"));
6563 else if (vrc == VERR_PATH_NOT_FOUND)
6564 hrc = setError(E_FAIL,
6565 tr("The path to the autostart database is not set"));
6566 else
6567 hrc = setError(E_UNEXPECTED,
6568 aAutostartEnabled ?
6569 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6570 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6571 mUserData->s.strName.c_str(), vrc);
6572 }
6573 return hrc;
6574}
6575
6576HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6577{
6578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6579
6580 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6581
6582 return S_OK;
6583}
6584
6585HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6586{
6587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6588 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6589 if (SUCCEEDED(hrc))
6590 {
6591 hrc = mHWData.backupEx();
6592 if (SUCCEEDED(hrc))
6593 {
6594 i_setModified(IsModified_MachineData);
6595 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6596 }
6597 }
6598 return hrc;
6599}
6600
6601HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6602{
6603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6604
6605 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6606
6607 return S_OK;
6608}
6609
6610HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6611{
6612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6613 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6614 if ( SUCCEEDED(hrc)
6615 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6616 {
6617 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6618 int vrc;
6619
6620 if (aAutostopType != AutostopType_Disabled)
6621 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6622 else
6623 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6624
6625 if (RT_SUCCESS(vrc))
6626 {
6627 hrc = mHWData.backupEx();
6628 if (SUCCEEDED(hrc))
6629 {
6630 i_setModified(IsModified_MachineData);
6631 mHWData->mAutostart.enmAutostopType = aAutostopType;
6632 }
6633 }
6634 else if (vrc == VERR_NOT_SUPPORTED)
6635 hrc = setError(VBOX_E_NOT_SUPPORTED,
6636 tr("The VM autostop feature is not supported on this platform"));
6637 else if (vrc == VERR_PATH_NOT_FOUND)
6638 hrc = setError(E_FAIL,
6639 tr("The path to the autostart database is not set"));
6640 else
6641 hrc = setError(E_UNEXPECTED,
6642 aAutostopType != AutostopType_Disabled ?
6643 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6644 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6645 mUserData->s.strName.c_str(), vrc);
6646 }
6647 return hrc;
6648}
6649
6650HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6651{
6652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6653
6654 aDefaultFrontend = mHWData->mDefaultFrontend;
6655
6656 return S_OK;
6657}
6658
6659HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6660{
6661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6662 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6663 if (SUCCEEDED(hrc))
6664 {
6665 hrc = mHWData.backupEx();
6666 if (SUCCEEDED(hrc))
6667 {
6668 i_setModified(IsModified_MachineData);
6669 mHWData->mDefaultFrontend = aDefaultFrontend;
6670 }
6671 }
6672 return hrc;
6673}
6674
6675HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6676{
6677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6678 size_t cbIcon = mUserData->s.ovIcon.size();
6679 aIcon.resize(cbIcon);
6680 if (cbIcon)
6681 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6682 return S_OK;
6683}
6684
6685HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6686{
6687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6688 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6689 if (SUCCEEDED(hrc))
6690 {
6691 i_setModified(IsModified_MachineData);
6692 mUserData.backup();
6693 size_t cbIcon = aIcon.size();
6694 mUserData->s.ovIcon.resize(cbIcon);
6695 if (cbIcon)
6696 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6697 }
6698 return hrc;
6699}
6700
6701HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6702{
6703#ifdef VBOX_WITH_USB
6704 *aUSBProxyAvailable = true;
6705#else
6706 *aUSBProxyAvailable = false;
6707#endif
6708 return S_OK;
6709}
6710
6711HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6712{
6713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6714
6715 *aVMProcessPriority = mUserData->s.enmVMPriority;
6716
6717 return S_OK;
6718}
6719
6720HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6721{
6722 RT_NOREF(aVMProcessPriority);
6723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6724 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6725 if (SUCCEEDED(hrc))
6726 {
6727 hrc = mUserData.backupEx();
6728 if (SUCCEEDED(hrc))
6729 {
6730 i_setModified(IsModified_MachineData);
6731 mUserData->s.enmVMPriority = aVMProcessPriority;
6732 }
6733 }
6734 alock.release();
6735 if (SUCCEEDED(hrc))
6736 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6737 return hrc;
6738}
6739
6740HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6741 ComPtr<IProgress> &aProgress)
6742{
6743 ComObjPtr<Progress> pP;
6744 Progress *ppP = pP;
6745 IProgress *iP = static_cast<IProgress *>(ppP);
6746 IProgress **pProgress = &iP;
6747
6748 IMachine *pTarget = aTarget;
6749
6750 /* Convert the options. */
6751 RTCList<CloneOptions_T> optList;
6752 if (aOptions.size())
6753 for (size_t i = 0; i < aOptions.size(); ++i)
6754 optList.append(aOptions[i]);
6755
6756 if (optList.contains(CloneOptions_Link))
6757 {
6758 if (!i_isSnapshotMachine())
6759 return setError(E_INVALIDARG,
6760 tr("Linked clone can only be created from a snapshot"));
6761 if (aMode != CloneMode_MachineState)
6762 return setError(E_INVALIDARG,
6763 tr("Linked clone can only be created for a single machine state"));
6764 }
6765 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6766
6767 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6768
6769 HRESULT hrc = pWorker->start(pProgress);
6770
6771 pP = static_cast<Progress *>(*pProgress);
6772 pP.queryInterfaceTo(aProgress.asOutParam());
6773
6774 return hrc;
6775
6776}
6777
6778HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6779 const com::Utf8Str &aType,
6780 ComPtr<IProgress> &aProgress)
6781{
6782 LogFlowThisFuncEnter();
6783
6784 ComObjPtr<Progress> ptrProgress;
6785 HRESULT hrc = ptrProgress.createObject();
6786 if (SUCCEEDED(hrc))
6787 {
6788 com::Utf8Str strDefaultPath;
6789 if (aTargetPath.isEmpty())
6790 i_calculateFullPath(".", strDefaultPath);
6791
6792 /* Initialize our worker task */
6793 MachineMoveVM *pTask = NULL;
6794 try
6795 {
6796 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6797 }
6798 catch (std::bad_alloc &)
6799 {
6800 return E_OUTOFMEMORY;
6801 }
6802
6803 hrc = pTask->init();//no exceptions are thrown
6804
6805 if (SUCCEEDED(hrc))
6806 {
6807 hrc = pTask->createThread();
6808 pTask = NULL; /* Consumed by createThread(). */
6809 if (SUCCEEDED(hrc))
6810 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6811 else
6812 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6813 }
6814 else
6815 delete pTask;
6816 }
6817
6818 LogFlowThisFuncLeave();
6819 return hrc;
6820
6821}
6822
6823HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6824{
6825 NOREF(aProgress);
6826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 // This check should always fail.
6829 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6830 if (FAILED(hrc)) return hrc;
6831
6832 AssertFailedReturn(E_NOTIMPL);
6833}
6834
6835HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6836{
6837 NOREF(aSavedStateFile);
6838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6839
6840 // This check should always fail.
6841 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6842 if (FAILED(hrc)) return hrc;
6843
6844 AssertFailedReturn(E_NOTIMPL);
6845}
6846
6847HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6848{
6849 NOREF(aFRemoveFile);
6850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6851
6852 // This check should always fail.
6853 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6854 if (FAILED(hrc)) return hrc;
6855
6856 AssertFailedReturn(E_NOTIMPL);
6857}
6858
6859// public methods for internal purposes
6860/////////////////////////////////////////////////////////////////////////////
6861
6862/**
6863 * Adds the given IsModified_* flag to the dirty flags of the machine.
6864 * This must be called either during i_loadSettings or under the machine write lock.
6865 * @param fl Flag
6866 * @param fAllowStateModification If state modifications are allowed.
6867 */
6868void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6869{
6870 mData->flModifications |= fl;
6871 if (fAllowStateModification && i_isStateModificationAllowed())
6872 mData->mCurrentStateModified = true;
6873}
6874
6875/**
6876 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6877 * care of the write locking.
6878 *
6879 * @param fModification The flag to add.
6880 * @param fAllowStateModification If state modifications are allowed.
6881 */
6882void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6883{
6884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6885 i_setModified(fModification, fAllowStateModification);
6886}
6887
6888/**
6889 * Saves the registry entry of this machine to the given configuration node.
6890 *
6891 * @param data Machine registry data.
6892 *
6893 * @note locks this object for reading.
6894 */
6895HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6896{
6897 AutoLimitedCaller autoCaller(this);
6898 AssertComRCReturnRC(autoCaller.hrc());
6899
6900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6901
6902 data.uuid = mData->mUuid;
6903 data.strSettingsFile = mData->m_strConfigFile;
6904
6905 return S_OK;
6906}
6907
6908/**
6909 * Calculates the absolute path of the given path taking the directory of the
6910 * machine settings file as the current directory.
6911 *
6912 * @param strPath Path to calculate the absolute path for.
6913 * @param aResult Where to put the result (used only on success, can be the
6914 * same Utf8Str instance as passed in @a aPath).
6915 * @return IPRT result.
6916 *
6917 * @note Locks this object for reading.
6918 */
6919int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6920{
6921 AutoCaller autoCaller(this);
6922 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
6923
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925
6926 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6927
6928 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6929
6930 strSettingsDir.stripFilename();
6931 char szFolder[RTPATH_MAX];
6932 size_t cbFolder = sizeof(szFolder);
6933 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
6934 if (RT_SUCCESS(vrc))
6935 aResult = szFolder;
6936
6937 return vrc;
6938}
6939
6940/**
6941 * Copies strSource to strTarget, making it relative to the machine folder
6942 * if it is a subdirectory thereof, or simply copying it otherwise.
6943 *
6944 * @param strSource Path to evaluate and copy.
6945 * @param strTarget Buffer to receive target path.
6946 *
6947 * @note Locks this object for reading.
6948 */
6949void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6950 Utf8Str &strTarget)
6951{
6952 AutoCaller autoCaller(this);
6953 AssertComRCReturn(autoCaller.hrc(), (void)0);
6954
6955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6956
6957 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6958 // use strTarget as a temporary buffer to hold the machine settings dir
6959 strTarget = mData->m_strConfigFileFull;
6960 strTarget.stripFilename();
6961 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6962 {
6963 // is relative: then append what's left
6964 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6965 // for empty paths (only possible for subdirs) use "." to avoid
6966 // triggering default settings for not present config attributes.
6967 if (strTarget.isEmpty())
6968 strTarget = ".";
6969 }
6970 else
6971 // is not relative: then overwrite
6972 strTarget = strSource;
6973}
6974
6975/**
6976 * Returns the full path to the machine's log folder in the
6977 * \a aLogFolder argument.
6978 */
6979void Machine::i_getLogFolder(Utf8Str &aLogFolder)
6980{
6981 AutoCaller autoCaller(this);
6982 AssertComRCReturnVoid(autoCaller.hrc());
6983
6984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6985
6986 char szTmp[RTPATH_MAX];
6987 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6988 if (RT_SUCCESS(vrc))
6989 {
6990 if (szTmp[0] && !mUserData.isNull())
6991 {
6992 char szTmp2[RTPATH_MAX];
6993 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6994 if (RT_SUCCESS(vrc))
6995 aLogFolder.printf("%s%c%s",
6996 szTmp2,
6997 RTPATH_DELIMITER,
6998 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6999 }
7000 else
7001 vrc = VERR_PATH_IS_RELATIVE;
7002 }
7003
7004 if (RT_FAILURE(vrc))
7005 {
7006 // fallback if VBOX_USER_LOGHOME is not set or invalid
7007 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7008 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7009 aLogFolder.append(RTPATH_DELIMITER);
7010 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7011 }
7012}
7013
7014/**
7015 * Returns the full path to the machine's log file for an given index.
7016 */
7017Utf8Str Machine::i_getLogFilename(ULONG idx)
7018{
7019 Utf8Str logFolder;
7020 getLogFolder(logFolder);
7021 Assert(logFolder.length());
7022
7023 Utf8Str log;
7024 if (idx == 0)
7025 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7026#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7027 else if (idx == 1)
7028 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7029 else
7030 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7031#else
7032 else
7033 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7034#endif
7035 return log;
7036}
7037
7038/**
7039 * Returns the full path to the machine's hardened log file.
7040 */
7041Utf8Str Machine::i_getHardeningLogFilename(void)
7042{
7043 Utf8Str strFilename;
7044 getLogFolder(strFilename);
7045 Assert(strFilename.length());
7046 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7047 return strFilename;
7048}
7049
7050/**
7051 * Returns the default NVRAM filename based on the location of the VM config.
7052 * Note that this is a relative path.
7053 */
7054Utf8Str Machine::i_getDefaultNVRAMFilename()
7055{
7056 AutoCaller autoCaller(this);
7057 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7058
7059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7060
7061 if (i_isSnapshotMachine())
7062 return Utf8Str::Empty;
7063
7064 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7065 strNVRAMFilePath.stripPath();
7066 strNVRAMFilePath.stripSuffix();
7067 strNVRAMFilePath += ".nvram";
7068
7069 return strNVRAMFilePath;
7070}
7071
7072/**
7073 * Returns the NVRAM filename for a new snapshot. This intentionally works
7074 * similarly to the saved state file naming. Note that this is usually
7075 * a relative path, unless the snapshot folder is absolute.
7076 */
7077Utf8Str Machine::i_getSnapshotNVRAMFilename()
7078{
7079 AutoCaller autoCaller(this);
7080 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7081
7082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7083
7084 RTTIMESPEC ts;
7085 RTTimeNow(&ts);
7086 RTTIME time;
7087 RTTimeExplode(&time, &ts);
7088
7089 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7090 strNVRAMFilePath += RTPATH_DELIMITER;
7091 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7092 time.i32Year, time.u8Month, time.u8MonthDay,
7093 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7094
7095 return strNVRAMFilePath;
7096}
7097
7098/**
7099 * Returns the version of the settings file.
7100 */
7101SettingsVersion_T Machine::i_getSettingsVersion(void)
7102{
7103 AutoCaller autoCaller(this);
7104 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7105
7106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7107
7108 return mData->pMachineConfigFile->getSettingsVersion();
7109}
7110
7111/**
7112 * Composes a unique saved state filename based on the current system time. The filename is
7113 * granular to the second so this will work so long as no more than one snapshot is taken on
7114 * a machine per second.
7115 *
7116 * Before version 4.1, we used this formula for saved state files:
7117 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7118 * which no longer works because saved state files can now be shared between the saved state of the
7119 * "saved" machine and an online snapshot, and the following would cause problems:
7120 * 1) save machine
7121 * 2) create online snapshot from that machine state --> reusing saved state file
7122 * 3) save machine again --> filename would be reused, breaking the online snapshot
7123 *
7124 * So instead we now use a timestamp.
7125 *
7126 * @param strStateFilePath
7127 */
7128
7129void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7130{
7131 AutoCaller autoCaller(this);
7132 AssertComRCReturnVoid(autoCaller.hrc());
7133
7134 {
7135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7136 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7137 }
7138
7139 RTTIMESPEC ts;
7140 RTTimeNow(&ts);
7141 RTTIME time;
7142 RTTimeExplode(&time, &ts);
7143
7144 strStateFilePath += RTPATH_DELIMITER;
7145 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7146 time.i32Year, time.u8Month, time.u8MonthDay,
7147 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7148}
7149
7150/**
7151 * Returns whether at least one USB controller is present for the VM.
7152 */
7153bool Machine::i_isUSBControllerPresent()
7154{
7155 AutoCaller autoCaller(this);
7156 AssertComRCReturn(autoCaller.hrc(), false);
7157
7158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7159
7160 return (mUSBControllers->size() > 0);
7161}
7162
7163
7164/**
7165 * @note Locks this object for writing, calls the client process
7166 * (inside the lock).
7167 */
7168HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7169 const Utf8Str &strFrontend,
7170 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7171 ProgressProxy *aProgress)
7172{
7173 LogFlowThisFuncEnter();
7174
7175 AssertReturn(aControl, E_FAIL);
7176 AssertReturn(aProgress, E_FAIL);
7177 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7178
7179 AutoCaller autoCaller(this);
7180 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7181
7182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7183
7184 if (!mData->mRegistered)
7185 return setError(E_UNEXPECTED,
7186 tr("The machine '%s' is not registered"),
7187 mUserData->s.strName.c_str());
7188
7189 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7190
7191 /* The process started when launching a VM with separate UI/VM processes is always
7192 * the UI process, i.e. needs special handling as it won't claim the session. */
7193 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7194
7195 if (fSeparate)
7196 {
7197 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7198 return setError(VBOX_E_INVALID_OBJECT_STATE,
7199 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7200 mUserData->s.strName.c_str());
7201 }
7202 else
7203 {
7204 if ( mData->mSession.mState == SessionState_Locked
7205 || mData->mSession.mState == SessionState_Spawning
7206 || mData->mSession.mState == SessionState_Unlocking)
7207 return setError(VBOX_E_INVALID_OBJECT_STATE,
7208 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7209 mUserData->s.strName.c_str());
7210
7211 /* may not be busy */
7212 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7213 }
7214
7215 /* Hardening logging */
7216#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7217 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7218 {
7219 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7220 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7221 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7222 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7223 {
7224 Utf8Str strStartupLogDir = strHardeningLogFile;
7225 strStartupLogDir.stripFilename();
7226 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7227 file without stripping the file. */
7228 }
7229 strSupHardeningLogArg.append(strHardeningLogFile);
7230
7231 /* Remove legacy log filename to avoid confusion. */
7232 Utf8Str strOldStartupLogFile;
7233 getLogFolder(strOldStartupLogFile);
7234 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7235 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7236 }
7237#else
7238 Utf8Str strSupHardeningLogArg;
7239#endif
7240
7241 Utf8Str strAppOverride;
7242#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7243 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7244#endif
7245
7246 bool fUseVBoxSDS = false;
7247 Utf8Str strCanonicalName;
7248 if (false)
7249 { }
7250#ifdef VBOX_WITH_QTGUI
7251 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7252 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7253 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7254 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7255 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7256 {
7257 strCanonicalName = "GUI/Qt";
7258 fUseVBoxSDS = true;
7259 }
7260#endif
7261#ifdef VBOX_WITH_VBOXSDL
7262 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7263 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7264 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7265 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7266 {
7267 strCanonicalName = "GUI/SDL";
7268 fUseVBoxSDS = true;
7269 }
7270#endif
7271#ifdef VBOX_WITH_HEADLESS
7272 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7273 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7274 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7275 {
7276 strCanonicalName = "headless";
7277 }
7278#endif
7279 else
7280 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7281
7282 Utf8Str idStr = mData->mUuid.toString();
7283 Utf8Str const &strMachineName = mUserData->s.strName;
7284 RTPROCESS pid = NIL_RTPROCESS;
7285
7286#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7287 RT_NOREF(fUseVBoxSDS);
7288#else
7289 DWORD idCallerSession = ~(DWORD)0;
7290 if (fUseVBoxSDS)
7291 {
7292 /*
7293 * The VBoxSDS should be used for process launching the VM with
7294 * GUI only if the caller and the VBoxSDS are in different Windows
7295 * sessions and the caller in the interactive one.
7296 */
7297 fUseVBoxSDS = false;
7298
7299 /* Get windows session of the current process. The process token used
7300 due to several reasons:
7301 1. The token is absent for the current thread except someone set it
7302 for us.
7303 2. Needs to get the id of the session where the process is started.
7304 We only need to do this once, though. */
7305 static DWORD s_idCurrentSession = ~(DWORD)0;
7306 DWORD idCurrentSession = s_idCurrentSession;
7307 if (idCurrentSession == ~(DWORD)0)
7308 {
7309 HANDLE hCurrentProcessToken = NULL;
7310 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7311 {
7312 DWORD cbIgn = 0;
7313 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7314 s_idCurrentSession = idCurrentSession;
7315 else
7316 {
7317 idCurrentSession = ~(DWORD)0;
7318 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7319 }
7320 CloseHandle(hCurrentProcessToken);
7321 }
7322 else
7323 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7324 }
7325
7326 /* get the caller's session */
7327 HRESULT hrc = CoImpersonateClient();
7328 if (SUCCEEDED(hrc))
7329 {
7330 HANDLE hCallerThreadToken;
7331 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7332 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7333 &hCallerThreadToken))
7334 {
7335 SetLastError(NO_ERROR);
7336 DWORD cbIgn = 0;
7337 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7338 {
7339 /* Only need to use SDS if the session ID differs: */
7340 if (idCurrentSession != idCallerSession)
7341 {
7342 fUseVBoxSDS = false;
7343
7344 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7345 DWORD cbTokenGroups = 0;
7346 PTOKEN_GROUPS pTokenGroups = NULL;
7347 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7348 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7349 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7350 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7351 {
7352 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7353 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7354 PSID pInteractiveSid = NULL;
7355 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7356 {
7357 /* Iterate over the groups looking for the interactive SID: */
7358 fUseVBoxSDS = false;
7359 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7360 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7361 {
7362 fUseVBoxSDS = true;
7363 break;
7364 }
7365 FreeSid(pInteractiveSid);
7366 }
7367 }
7368 else
7369 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7370 RTMemTmpFree(pTokenGroups);
7371 }
7372 }
7373 else
7374 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7375 CloseHandle(hCallerThreadToken);
7376 }
7377 else
7378 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7379 CoRevertToSelf();
7380 }
7381 else
7382 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7383 }
7384 if (fUseVBoxSDS)
7385 {
7386 /* connect to VBoxSDS */
7387 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7388 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7389 if (FAILED(hrc))
7390 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7391 strMachineName.c_str());
7392
7393 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7394 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7395 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7396 service to access the files. */
7397 hrc = CoSetProxyBlanket(pVBoxSDS,
7398 RPC_C_AUTHN_DEFAULT,
7399 RPC_C_AUTHZ_DEFAULT,
7400 COLE_DEFAULT_PRINCIPAL,
7401 RPC_C_AUTHN_LEVEL_DEFAULT,
7402 RPC_C_IMP_LEVEL_IMPERSONATE,
7403 NULL,
7404 EOAC_DEFAULT);
7405 if (FAILED(hrc))
7406 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7407
7408 size_t const cEnvVars = aEnvironmentChanges.size();
7409 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7410 for (size_t i = 0; i < cEnvVars; i++)
7411 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7412
7413 ULONG uPid = 0;
7414 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7415 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7416 idCallerSession, &uPid);
7417 if (FAILED(hrc))
7418 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7419 pid = (RTPROCESS)uPid;
7420 }
7421 else
7422#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7423 {
7424 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7425 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7426 if (RT_FAILURE(vrc))
7427 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7428 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7429 }
7430
7431 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7432 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7433
7434 if (!fSeparate)
7435 {
7436 /*
7437 * Note that we don't release the lock here before calling the client,
7438 * because it doesn't need to call us back if called with a NULL argument.
7439 * Releasing the lock here is dangerous because we didn't prepare the
7440 * launch data yet, but the client we've just started may happen to be
7441 * too fast and call LockMachine() that will fail (because of PID, etc.),
7442 * so that the Machine will never get out of the Spawning session state.
7443 */
7444
7445 /* inform the session that it will be a remote one */
7446 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7447#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7448 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7449#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7450 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7451#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7452 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
7453
7454 if (FAILED(hrc))
7455 {
7456 /* restore the session state */
7457 mData->mSession.mState = SessionState_Unlocked;
7458 alock.release();
7459 mParent->i_addProcessToReap(pid);
7460 /* The failure may occur w/o any error info (from RPC), so provide one */
7461 return setError(VBOX_E_VM_ERROR,
7462 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
7463 }
7464
7465 /* attach launch data to the machine */
7466 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7467 mData->mSession.mRemoteControls.push_back(aControl);
7468 mData->mSession.mProgress = aProgress;
7469 mData->mSession.mPID = pid;
7470 mData->mSession.mState = SessionState_Spawning;
7471 Assert(strCanonicalName.isNotEmpty());
7472 mData->mSession.mName = strCanonicalName;
7473 }
7474 else
7475 {
7476 /* For separate UI process we declare the launch as completed instantly, as the
7477 * actual headless VM start may or may not come. No point in remembering anything
7478 * yet, as what matters for us is when the headless VM gets started. */
7479 aProgress->i_notifyComplete(S_OK);
7480 }
7481
7482 alock.release();
7483 mParent->i_addProcessToReap(pid);
7484
7485 LogFlowThisFuncLeave();
7486 return S_OK;
7487}
7488
7489/**
7490 * Returns @c true if the given session machine instance has an open direct
7491 * session (and optionally also for direct sessions which are closing) and
7492 * returns the session control machine instance if so.
7493 *
7494 * Note that when the method returns @c false, the arguments remain unchanged.
7495 *
7496 * @param aMachine Session machine object.
7497 * @param aControl Direct session control object (optional).
7498 * @param aRequireVM If true then only allow VM sessions.
7499 * @param aAllowClosing If true then additionally a session which is currently
7500 * being closed will also be allowed.
7501 *
7502 * @note locks this object for reading.
7503 */
7504bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7505 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7506 bool aRequireVM /*= false*/,
7507 bool aAllowClosing /*= false*/)
7508{
7509 AutoLimitedCaller autoCaller(this);
7510 AssertComRCReturn(autoCaller.hrc(), false);
7511
7512 /* just return false for inaccessible machines */
7513 if (getObjectState().getState() != ObjectState::Ready)
7514 return false;
7515
7516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7517
7518 if ( ( mData->mSession.mState == SessionState_Locked
7519 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7520 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7521 )
7522 {
7523 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7524
7525 aMachine = mData->mSession.mMachine;
7526
7527 if (aControl != NULL)
7528 *aControl = mData->mSession.mDirectControl;
7529
7530 return true;
7531 }
7532
7533 return false;
7534}
7535
7536/**
7537 * Returns @c true if the given machine has an spawning direct session.
7538 *
7539 * @note locks this object for reading.
7540 */
7541bool Machine::i_isSessionSpawning()
7542{
7543 AutoLimitedCaller autoCaller(this);
7544 AssertComRCReturn(autoCaller.hrc(), false);
7545
7546 /* just return false for inaccessible machines */
7547 if (getObjectState().getState() != ObjectState::Ready)
7548 return false;
7549
7550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7551
7552 if (mData->mSession.mState == SessionState_Spawning)
7553 return true;
7554
7555 return false;
7556}
7557
7558/**
7559 * Called from the client watcher thread to check for unexpected client process
7560 * death during Session_Spawning state (e.g. before it successfully opened a
7561 * direct session).
7562 *
7563 * On Win32 and on OS/2, this method is called only when we've got the
7564 * direct client's process termination notification, so it always returns @c
7565 * true.
7566 *
7567 * On other platforms, this method returns @c true if the client process is
7568 * terminated and @c false if it's still alive.
7569 *
7570 * @note Locks this object for writing.
7571 */
7572bool Machine::i_checkForSpawnFailure()
7573{
7574 AutoCaller autoCaller(this);
7575 if (!autoCaller.isOk())
7576 {
7577 /* nothing to do */
7578 LogFlowThisFunc(("Already uninitialized!\n"));
7579 return true;
7580 }
7581
7582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7583
7584 if (mData->mSession.mState != SessionState_Spawning)
7585 {
7586 /* nothing to do */
7587 LogFlowThisFunc(("Not spawning any more!\n"));
7588 return true;
7589 }
7590
7591 /* PID not yet initialized, skip check. */
7592 if (mData->mSession.mPID == NIL_RTPROCESS)
7593 return false;
7594
7595 HRESULT hrc = S_OK;
7596 RTPROCSTATUS status;
7597 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7598 if (vrc != VERR_PROCESS_RUNNING)
7599 {
7600 Utf8Str strExtraInfo;
7601
7602#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7603 /* If the startup logfile exists and is of non-zero length, tell the
7604 user to look there for more details to encourage them to attach it
7605 when reporting startup issues. */
7606 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7607 uint64_t cbStartupLogFile = 0;
7608 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7609 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7610 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7611#endif
7612
7613 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7614 hrc = setError(E_FAIL,
7615 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7616 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7617 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7618 hrc = setError(E_FAIL,
7619 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7620 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7621 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7622 hrc = setError(E_FAIL,
7623 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7624 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7625 else
7626 hrc = setErrorBoth(E_FAIL, vrc,
7627 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7628 i_getName().c_str(), vrc, strExtraInfo.c_str());
7629 }
7630
7631 if (FAILED(hrc))
7632 {
7633 /* Close the remote session, remove the remote control from the list
7634 * and reset session state to Closed (@note keep the code in sync with
7635 * the relevant part in LockMachine()). */
7636
7637 Assert(mData->mSession.mRemoteControls.size() == 1);
7638 if (mData->mSession.mRemoteControls.size() == 1)
7639 {
7640 ErrorInfoKeeper eik;
7641 mData->mSession.mRemoteControls.front()->Uninitialize();
7642 }
7643
7644 mData->mSession.mRemoteControls.clear();
7645 mData->mSession.mState = SessionState_Unlocked;
7646
7647 /* finalize the progress after setting the state */
7648 if (!mData->mSession.mProgress.isNull())
7649 {
7650 mData->mSession.mProgress->notifyComplete(hrc);
7651 mData->mSession.mProgress.setNull();
7652 }
7653
7654 mData->mSession.mPID = NIL_RTPROCESS;
7655
7656 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7657 return true;
7658 }
7659
7660 return false;
7661}
7662
7663/**
7664 * Checks whether the machine can be registered. If so, commits and saves
7665 * all settings.
7666 *
7667 * @note Must be called from mParent's write lock. Locks this object and
7668 * children for writing.
7669 */
7670HRESULT Machine::i_prepareRegister()
7671{
7672 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7673
7674 AutoLimitedCaller autoCaller(this);
7675 AssertComRCReturnRC(autoCaller.hrc());
7676
7677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7678
7679 /* wait for state dependents to drop to zero */
7680 i_ensureNoStateDependencies(alock);
7681
7682 if (!mData->mAccessible)
7683 return setError(VBOX_E_INVALID_OBJECT_STATE,
7684 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7685 mUserData->s.strName.c_str(),
7686 mData->mUuid.toString().c_str());
7687
7688 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7689
7690 if (mData->mRegistered)
7691 return setError(VBOX_E_INVALID_OBJECT_STATE,
7692 tr("The machine '%s' with UUID {%s} is already registered"),
7693 mUserData->s.strName.c_str(),
7694 mData->mUuid.toString().c_str());
7695
7696 HRESULT hrc = S_OK;
7697
7698 // Ensure the settings are saved. If we are going to be registered and
7699 // no config file exists yet, create it by calling i_saveSettings() too.
7700 if ( (mData->flModifications)
7701 || (!mData->pMachineConfigFile->fileExists())
7702 )
7703 {
7704 hrc = i_saveSettings(NULL, alock);
7705 // no need to check whether VirtualBox.xml needs saving too since
7706 // we can't have a machine XML file rename pending
7707 if (FAILED(hrc)) return hrc;
7708 }
7709
7710 /* more config checking goes here */
7711
7712 if (SUCCEEDED(hrc))
7713 {
7714 /* we may have had implicit modifications we want to fix on success */
7715 i_commit();
7716
7717 mData->mRegistered = true;
7718 }
7719 else
7720 {
7721 /* we may have had implicit modifications we want to cancel on failure*/
7722 i_rollback(false /* aNotify */);
7723 }
7724
7725 return hrc;
7726}
7727
7728/**
7729 * Increases the number of objects dependent on the machine state or on the
7730 * registered state. Guarantees that these two states will not change at least
7731 * until #i_releaseStateDependency() is called.
7732 *
7733 * Depending on the @a aDepType value, additional state checks may be made.
7734 * These checks will set extended error info on failure. See
7735 * #i_checkStateDependency() for more info.
7736 *
7737 * If this method returns a failure, the dependency is not added and the caller
7738 * is not allowed to rely on any particular machine state or registration state
7739 * value and may return the failed result code to the upper level.
7740 *
7741 * @param aDepType Dependency type to add.
7742 * @param aState Current machine state (NULL if not interested).
7743 * @param aRegistered Current registered state (NULL if not interested).
7744 *
7745 * @note Locks this object for writing.
7746 */
7747HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7748 MachineState_T *aState /* = NULL */,
7749 BOOL *aRegistered /* = NULL */)
7750{
7751 AutoCaller autoCaller(this);
7752 AssertComRCReturnRC(autoCaller.hrc());
7753
7754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7755
7756 HRESULT hrc = i_checkStateDependency(aDepType);
7757 if (FAILED(hrc)) return hrc;
7758
7759 {
7760 if (mData->mMachineStateChangePending != 0)
7761 {
7762 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7763 * drop to zero so don't add more. It may make sense to wait a bit
7764 * and retry before reporting an error (since the pending state
7765 * transition should be really quick) but let's just assert for
7766 * now to see if it ever happens on practice. */
7767
7768 AssertFailed();
7769
7770 return setError(E_ACCESSDENIED,
7771 tr("Machine state change is in progress. Please retry the operation later."));
7772 }
7773
7774 ++mData->mMachineStateDeps;
7775 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7776 }
7777
7778 if (aState)
7779 *aState = mData->mMachineState;
7780 if (aRegistered)
7781 *aRegistered = mData->mRegistered;
7782
7783 return S_OK;
7784}
7785
7786/**
7787 * Decreases the number of objects dependent on the machine state.
7788 * Must always complete the #i_addStateDependency() call after the state
7789 * dependency is no more necessary.
7790 */
7791void Machine::i_releaseStateDependency()
7792{
7793 AutoCaller autoCaller(this);
7794 AssertComRCReturnVoid(autoCaller.hrc());
7795
7796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 /* releaseStateDependency() w/o addStateDependency()? */
7799 AssertReturnVoid(mData->mMachineStateDeps != 0);
7800 -- mData->mMachineStateDeps;
7801
7802 if (mData->mMachineStateDeps == 0)
7803 {
7804 /* inform i_ensureNoStateDependencies() that there are no more deps */
7805 if (mData->mMachineStateChangePending != 0)
7806 {
7807 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7808 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7809 }
7810 }
7811}
7812
7813Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7814{
7815 /* start with nothing found */
7816 Utf8Str strResult("");
7817
7818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7819
7820 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7821 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7822 // found:
7823 strResult = it->second; // source is a Utf8Str
7824
7825 return strResult;
7826}
7827
7828FirmwareType_T Machine::i_getFirmwareType() const
7829{
7830 return mFirmwareSettings->i_getFirmwareType();
7831}
7832
7833// protected methods
7834/////////////////////////////////////////////////////////////////////////////
7835
7836/**
7837 * Performs machine state checks based on the @a aDepType value. If a check
7838 * fails, this method will set extended error info, otherwise it will return
7839 * S_OK. It is supposed, that on failure, the caller will immediately return
7840 * the return value of this method to the upper level.
7841 *
7842 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7843 *
7844 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7845 * current state of this machine object allows to change settings of the
7846 * machine (i.e. the machine is not registered, or registered but not running
7847 * and not saved). It is useful to call this method from Machine setters
7848 * before performing any change.
7849 *
7850 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7851 * as for MutableStateDep except that if the machine is saved, S_OK is also
7852 * returned. This is useful in setters which allow changing machine
7853 * properties when it is in the saved state.
7854 *
7855 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7856 * if the current state of this machine object allows to change runtime
7857 * changeable settings of the machine (i.e. the machine is not registered, or
7858 * registered but either running or not running and not saved). It is useful
7859 * to call this method from Machine setters before performing any changes to
7860 * runtime changeable settings.
7861 *
7862 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7863 * the same as for MutableOrRunningStateDep except that if the machine is
7864 * saved, S_OK is also returned. This is useful in setters which allow
7865 * changing runtime and saved state changeable machine properties.
7866 *
7867 * @param aDepType Dependency type to check.
7868 *
7869 * @note Non Machine based classes should use #i_addStateDependency() and
7870 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7871 * template.
7872 *
7873 * @note This method must be called from under this object's read or write
7874 * lock.
7875 */
7876HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7877{
7878 switch (aDepType)
7879 {
7880 case AnyStateDep:
7881 {
7882 break;
7883 }
7884 case MutableStateDep:
7885 {
7886 if ( mData->mRegistered
7887 && ( !i_isSessionMachine()
7888 || ( mData->mMachineState != MachineState_Aborted
7889 && mData->mMachineState != MachineState_Teleported
7890 && mData->mMachineState != MachineState_PoweredOff
7891 )
7892 )
7893 )
7894 return setError(VBOX_E_INVALID_VM_STATE,
7895 tr("The machine is not mutable (state is %s)"),
7896 Global::stringifyMachineState(mData->mMachineState));
7897 break;
7898 }
7899 case MutableOrSavedStateDep:
7900 {
7901 if ( mData->mRegistered
7902 && ( !i_isSessionMachine()
7903 || ( mData->mMachineState != MachineState_Aborted
7904 && mData->mMachineState != MachineState_Teleported
7905 && mData->mMachineState != MachineState_Saved
7906 && mData->mMachineState != MachineState_AbortedSaved
7907 && mData->mMachineState != MachineState_PoweredOff
7908 )
7909 )
7910 )
7911 return setError(VBOX_E_INVALID_VM_STATE,
7912 tr("The machine is not mutable or saved (state is %s)"),
7913 Global::stringifyMachineState(mData->mMachineState));
7914 break;
7915 }
7916 case MutableOrRunningStateDep:
7917 {
7918 if ( mData->mRegistered
7919 && ( !i_isSessionMachine()
7920 || ( mData->mMachineState != MachineState_Aborted
7921 && mData->mMachineState != MachineState_Teleported
7922 && mData->mMachineState != MachineState_PoweredOff
7923 && !Global::IsOnline(mData->mMachineState)
7924 )
7925 )
7926 )
7927 return setError(VBOX_E_INVALID_VM_STATE,
7928 tr("The machine is not mutable or running (state is %s)"),
7929 Global::stringifyMachineState(mData->mMachineState));
7930 break;
7931 }
7932 case MutableOrSavedOrRunningStateDep:
7933 {
7934 if ( mData->mRegistered
7935 && ( !i_isSessionMachine()
7936 || ( mData->mMachineState != MachineState_Aborted
7937 && mData->mMachineState != MachineState_Teleported
7938 && mData->mMachineState != MachineState_Saved
7939 && mData->mMachineState != MachineState_AbortedSaved
7940 && mData->mMachineState != MachineState_PoweredOff
7941 && !Global::IsOnline(mData->mMachineState)
7942 )
7943 )
7944 )
7945 return setError(VBOX_E_INVALID_VM_STATE,
7946 tr("The machine is not mutable, saved or running (state is %s)"),
7947 Global::stringifyMachineState(mData->mMachineState));
7948 break;
7949 }
7950 }
7951
7952 return S_OK;
7953}
7954
7955/**
7956 * Helper to initialize all associated child objects and allocate data
7957 * structures.
7958 *
7959 * This method must be called as a part of the object's initialization procedure
7960 * (usually done in the #init() method).
7961 *
7962 * @note Must be called only from #init() or from #i_registeredInit().
7963 */
7964HRESULT Machine::initDataAndChildObjects()
7965{
7966 AutoCaller autoCaller(this);
7967 AssertComRCReturnRC(autoCaller.hrc());
7968 AssertReturn( getObjectState().getState() == ObjectState::InInit
7969 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7970
7971 AssertReturn(!mData->mAccessible, E_FAIL);
7972
7973 /* allocate data structures */
7974 mSSData.allocate();
7975 mUserData.allocate();
7976 mHWData.allocate();
7977 mMediumAttachments.allocate();
7978 mStorageControllers.allocate();
7979 mUSBControllers.allocate();
7980
7981 /* create the platform + platform properties objects for this machine */
7982 HRESULT hrc = unconst(mPlatform).createObject();
7983 ComAssertComRCRetRC(hrc);
7984 hrc = mPlatform->init(this);
7985 ComAssertComRCRetRC(hrc);
7986 hrc = unconst(mPlatformProperties).createObject();
7987 ComAssertComRCRetRC(hrc);
7988 hrc = mPlatformProperties->init(mParent);
7989 ComAssertComRCRetRC(hrc);
7990
7991 /* initialize mOSTypeId */
7992 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7993
7994 /* create associated firmware settings object */
7995 unconst(mFirmwareSettings).createObject();
7996 mFirmwareSettings->init(this);
7997
7998 /* create associated recording settings object */
7999 unconst(mRecordingSettings).createObject();
8000 mRecordingSettings->init(this);
8001
8002 /* create associated trusted platform module object */
8003 unconst(mTrustedPlatformModule).createObject();
8004 mTrustedPlatformModule->init(this);
8005
8006 /* create associated NVRAM store object */
8007 unconst(mNvramStore).createObject();
8008 mNvramStore->init(this);
8009
8010 /* create the graphics adapter object (always present) */
8011 unconst(mGraphicsAdapter).createObject();
8012 mGraphicsAdapter->init(this);
8013
8014 /* create an associated VRDE object (default is disabled) */
8015 unconst(mVRDEServer).createObject();
8016 mVRDEServer->init(this);
8017
8018 /* create associated serial port objects */
8019 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8020 {
8021 unconst(mSerialPorts[slot]).createObject();
8022 mSerialPorts[slot]->init(this, slot);
8023 }
8024
8025 /* create associated parallel port objects */
8026 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8027 {
8028 unconst(mParallelPorts[slot]).createObject();
8029 mParallelPorts[slot]->init(this, slot);
8030 }
8031
8032 /* create the audio settings object */
8033 unconst(mAudioSettings).createObject();
8034 mAudioSettings->init(this);
8035
8036 /* create the USB device filters object (always present) */
8037 unconst(mUSBDeviceFilters).createObject();
8038 mUSBDeviceFilters->init(this);
8039
8040 /* create associated network adapter objects */
8041 ChipsetType_T enmChipsetType;
8042 hrc = mPlatform->getChipsetType(&enmChipsetType);
8043 ComAssertComRC(hrc);
8044
8045 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType));
8046 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8047 {
8048 unconst(mNetworkAdapters[slot]).createObject();
8049 mNetworkAdapters[slot]->init(this, slot);
8050 }
8051
8052 /* create the bandwidth control */
8053 unconst(mBandwidthControl).createObject();
8054 mBandwidthControl->init(this);
8055
8056 /* create the guest debug control object */
8057 unconst(mGuestDebugControl).createObject();
8058 mGuestDebugControl->init(this);
8059
8060 return hrc;
8061}
8062
8063/**
8064 * Helper to uninitialize all associated child objects and to free all data
8065 * structures.
8066 *
8067 * This method must be called as a part of the object's uninitialization
8068 * procedure (usually done in the #uninit() method).
8069 *
8070 * @note Must be called only from #uninit() or from #i_registeredInit().
8071 */
8072void Machine::uninitDataAndChildObjects()
8073{
8074 AutoCaller autoCaller(this);
8075 AssertComRCReturnVoid(autoCaller.hrc());
8076 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8077 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8078 || getObjectState().getState() == ObjectState::InUninit
8079 || getObjectState().getState() == ObjectState::Limited);
8080
8081 /* tell all our other child objects we've been uninitialized */
8082 if (mGuestDebugControl)
8083 {
8084 mGuestDebugControl->uninit();
8085 unconst(mGuestDebugControl).setNull();
8086 }
8087
8088 if (mBandwidthControl)
8089 {
8090 mBandwidthControl->uninit();
8091 unconst(mBandwidthControl).setNull();
8092 }
8093
8094 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8095 {
8096 if (mNetworkAdapters[slot])
8097 {
8098 mNetworkAdapters[slot]->uninit();
8099 unconst(mNetworkAdapters[slot]).setNull();
8100 }
8101 }
8102
8103 if (mUSBDeviceFilters)
8104 {
8105 mUSBDeviceFilters->uninit();
8106 unconst(mUSBDeviceFilters).setNull();
8107 }
8108
8109 if (mAudioSettings)
8110 {
8111 mAudioSettings->uninit();
8112 unconst(mAudioSettings).setNull();
8113 }
8114
8115 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8116 {
8117 if (mParallelPorts[slot])
8118 {
8119 mParallelPorts[slot]->uninit();
8120 unconst(mParallelPorts[slot]).setNull();
8121 }
8122 }
8123
8124 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8125 {
8126 if (mSerialPorts[slot])
8127 {
8128 mSerialPorts[slot]->uninit();
8129 unconst(mSerialPorts[slot]).setNull();
8130 }
8131 }
8132
8133 if (mVRDEServer)
8134 {
8135 mVRDEServer->uninit();
8136 unconst(mVRDEServer).setNull();
8137 }
8138
8139 if (mGraphicsAdapter)
8140 {
8141 mGraphicsAdapter->uninit();
8142 unconst(mGraphicsAdapter).setNull();
8143 }
8144
8145 if (mPlatform)
8146 {
8147 mPlatform->uninit();
8148 unconst(mPlatform).setNull();
8149 }
8150
8151 if (mPlatformProperties)
8152 {
8153 mPlatformProperties->uninit();
8154 unconst(mPlatformProperties).setNull();
8155 }
8156
8157 if (mFirmwareSettings)
8158 {
8159 mFirmwareSettings->uninit();
8160 unconst(mFirmwareSettings).setNull();
8161 }
8162
8163 if (mRecordingSettings)
8164 {
8165 mRecordingSettings->uninit();
8166 unconst(mRecordingSettings).setNull();
8167 }
8168
8169 if (mTrustedPlatformModule)
8170 {
8171 mTrustedPlatformModule->uninit();
8172 unconst(mTrustedPlatformModule).setNull();
8173 }
8174
8175 if (mNvramStore)
8176 {
8177 mNvramStore->uninit();
8178 unconst(mNvramStore).setNull();
8179 }
8180
8181 /* Deassociate media (only when a real Machine or a SnapshotMachine
8182 * instance is uninitialized; SessionMachine instances refer to real
8183 * Machine media). This is necessary for a clean re-initialization of
8184 * the VM after successfully re-checking the accessibility state. Note
8185 * that in case of normal Machine or SnapshotMachine uninitialization (as
8186 * a result of unregistering or deleting the snapshot), outdated media
8187 * attachments will already be uninitialized and deleted, so this
8188 * code will not affect them. */
8189 if ( !mMediumAttachments.isNull()
8190 && !i_isSessionMachine()
8191 )
8192 {
8193 for (MediumAttachmentList::const_iterator
8194 it = mMediumAttachments->begin();
8195 it != mMediumAttachments->end();
8196 ++it)
8197 {
8198 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8199 if (pMedium.isNull())
8200 continue;
8201 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8202 AssertComRC(hrc);
8203 }
8204 }
8205
8206 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8207 {
8208 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8209 if (mData->mFirstSnapshot)
8210 {
8211 // Snapshots tree is protected by machine write lock.
8212 // Otherwise we assert in Snapshot::uninit()
8213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8214 mData->mFirstSnapshot->uninit();
8215 mData->mFirstSnapshot.setNull();
8216 }
8217
8218 mData->mCurrentSnapshot.setNull();
8219 }
8220
8221 /* free data structures (the essential mData structure is not freed here
8222 * since it may be still in use) */
8223 mMediumAttachments.free();
8224 mStorageControllers.free();
8225 mUSBControllers.free();
8226 mHWData.free();
8227 mUserData.free();
8228 mSSData.free();
8229}
8230
8231/**
8232 * Returns a pointer to the Machine object for this machine that acts like a
8233 * parent for complex machine data objects such as shared folders, etc.
8234 *
8235 * For primary Machine objects and for SnapshotMachine objects, returns this
8236 * object's pointer itself. For SessionMachine objects, returns the peer
8237 * (primary) machine pointer.
8238 */
8239Machine *Machine::i_getMachine()
8240{
8241 if (i_isSessionMachine())
8242 return (Machine*)mPeer;
8243 return this;
8244}
8245
8246/**
8247 * Makes sure that there are no machine state dependents. If necessary, waits
8248 * for the number of dependents to drop to zero.
8249 *
8250 * Make sure this method is called from under this object's write lock to
8251 * guarantee that no new dependents may be added when this method returns
8252 * control to the caller.
8253 *
8254 * @note Receives a lock to this object for writing. The lock will be released
8255 * while waiting (if necessary).
8256 *
8257 * @warning To be used only in methods that change the machine state!
8258 */
8259void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8260{
8261 AssertReturnVoid(isWriteLockOnCurrentThread());
8262
8263 /* Wait for all state dependents if necessary */
8264 if (mData->mMachineStateDeps != 0)
8265 {
8266 /* lazy semaphore creation */
8267 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8268 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8269
8270 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8271 mData->mMachineStateDeps));
8272
8273 ++mData->mMachineStateChangePending;
8274
8275 /* reset the semaphore before waiting, the last dependent will signal
8276 * it */
8277 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8278
8279 alock.release();
8280
8281 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8282
8283 alock.acquire();
8284
8285 -- mData->mMachineStateChangePending;
8286 }
8287}
8288
8289/**
8290 * Changes the machine state and informs callbacks.
8291 *
8292 * This method is not intended to fail so it either returns S_OK or asserts (and
8293 * returns a failure).
8294 *
8295 * @note Locks this object for writing.
8296 */
8297HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8298{
8299 LogFlowThisFuncEnter();
8300 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8301 Assert(aMachineState != MachineState_Null);
8302
8303 AutoCaller autoCaller(this);
8304 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8305
8306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8307
8308 /* wait for state dependents to drop to zero */
8309 i_ensureNoStateDependencies(alock);
8310
8311 MachineState_T const enmOldState = mData->mMachineState;
8312 if (enmOldState != aMachineState)
8313 {
8314 mData->mMachineState = aMachineState;
8315 RTTimeNow(&mData->mLastStateChange);
8316
8317#ifdef VBOX_WITH_DTRACE_R3_MAIN
8318 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8319#endif
8320 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8321 }
8322
8323 LogFlowThisFuncLeave();
8324 return S_OK;
8325}
8326
8327/**
8328 * Searches for a shared folder with the given logical name
8329 * in the collection of shared folders.
8330 *
8331 * @param aName logical name of the shared folder
8332 * @param aSharedFolder where to return the found object
8333 * @param aSetError whether to set the error info if the folder is
8334 * not found
8335 * @return
8336 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8337 *
8338 * @note
8339 * must be called from under the object's lock!
8340 */
8341HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8342 ComObjPtr<SharedFolder> &aSharedFolder,
8343 bool aSetError /* = false */)
8344{
8345 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8346 for (HWData::SharedFolderList::const_iterator
8347 it = mHWData->mSharedFolders.begin();
8348 it != mHWData->mSharedFolders.end();
8349 ++it)
8350 {
8351 SharedFolder *pSF = *it;
8352 AutoCaller autoCaller(pSF);
8353 if (pSF->i_getName() == aName)
8354 {
8355 aSharedFolder = pSF;
8356 hrc = S_OK;
8357 break;
8358 }
8359 }
8360
8361 if (aSetError && FAILED(hrc))
8362 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8363
8364 return hrc;
8365}
8366
8367/**
8368 * Initializes all machine instance data from the given settings structures
8369 * from XML. The exception is the machine UUID which needs special handling
8370 * depending on the caller's use case, so the caller needs to set that herself.
8371 *
8372 * This gets called in several contexts during machine initialization:
8373 *
8374 * -- When machine XML exists on disk already and needs to be loaded into memory,
8375 * for example, from #i_registeredInit() to load all registered machines on
8376 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8377 * attached to the machine should be part of some media registry already.
8378 *
8379 * -- During OVF import, when a machine config has been constructed from an
8380 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8381 * ensure that the media listed as attachments in the config (which have
8382 * been imported from the OVF) receive the correct registry ID.
8383 *
8384 * -- During VM cloning.
8385 *
8386 * @param config Machine settings from XML.
8387 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8388 * for each attached medium in the config.
8389 * @return
8390 */
8391HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8392 const Guid *puuidRegistry)
8393{
8394 // copy name, description, OS type, teleporter, UTC etc.
8395 mUserData->s = config.machineUserData;
8396
8397 // look up the object by Id to check it is valid
8398 ComObjPtr<GuestOSType> pGuestOSType;
8399 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8400 if (!pGuestOSType.isNull())
8401 mUserData->s.strOsType = pGuestOSType->i_id();
8402
8403#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8404 // stateFile encryption (optional)
8405 mSSData->strStateKeyId = config.strStateKeyId;
8406 mSSData->strStateKeyStore = config.strStateKeyStore;
8407 mData->mstrLogKeyId = config.strLogKeyId;
8408 mData->mstrLogKeyStore = config.strLogKeyStore;
8409#endif
8410
8411 // stateFile (optional)
8412 if (config.strStateFile.isEmpty())
8413 mSSData->strStateFilePath.setNull();
8414 else
8415 {
8416 Utf8Str stateFilePathFull(config.strStateFile);
8417 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8418 if (RT_FAILURE(vrc))
8419 return setErrorBoth(E_FAIL, vrc,
8420 tr("Invalid saved state file path '%s' (%Rrc)"),
8421 config.strStateFile.c_str(),
8422 vrc);
8423 mSSData->strStateFilePath = stateFilePathFull;
8424 }
8425
8426 // snapshot folder needs special processing so set it again
8427 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8428 if (FAILED(hrc)) return hrc;
8429
8430 /* Copy the extra data items (config may or may not be the same as
8431 * mData->pMachineConfigFile) if necessary. When loading the XML files
8432 * from disk they are the same, but not for OVF import. */
8433 if (mData->pMachineConfigFile != &config)
8434 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8435
8436 /* currentStateModified (optional, default is true) */
8437 mData->mCurrentStateModified = config.fCurrentStateModified;
8438
8439 mData->mLastStateChange = config.timeLastStateChange;
8440
8441 /*
8442 * note: all mUserData members must be assigned prior this point because
8443 * we need to commit changes in order to let mUserData be shared by all
8444 * snapshot machine instances.
8445 */
8446 mUserData.commitCopy();
8447
8448 // machine registry, if present (must be loaded before snapshots)
8449 if (config.canHaveOwnMediaRegistry())
8450 {
8451 // determine machine folder
8452 Utf8Str strMachineFolder = i_getSettingsFileFull();
8453 strMachineFolder.stripFilename();
8454 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8455 config.mediaRegistry,
8456 strMachineFolder);
8457 if (FAILED(hrc)) return hrc;
8458 }
8459
8460 /* Snapshot node (optional) */
8461 size_t cRootSnapshots;
8462 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8463 {
8464 // there must be only one root snapshot
8465 Assert(cRootSnapshots == 1);
8466 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8467
8468 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
8469 if (FAILED(hrc)) return hrc;
8470 }
8471
8472 // hardware data
8473 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
8474 config.recordingSettings);
8475 if (FAILED(hrc)) return hrc;
8476
8477 /*
8478 * NOTE: the assignment below must be the last thing to do,
8479 * otherwise it will be not possible to change the settings
8480 * somewhere in the code above because all setters will be
8481 * blocked by i_checkStateDependency(MutableStateDep).
8482 */
8483
8484 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8485 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8486 {
8487 /* no need to use i_setMachineState() during init() */
8488 mData->mMachineState = MachineState_AbortedSaved;
8489 }
8490 else if (config.fAborted)
8491 {
8492 mSSData->strStateFilePath.setNull();
8493
8494 /* no need to use i_setMachineState() during init() */
8495 mData->mMachineState = MachineState_Aborted;
8496 }
8497 else if (!mSSData->strStateFilePath.isEmpty())
8498 {
8499 /* no need to use i_setMachineState() during init() */
8500 mData->mMachineState = MachineState_Saved;
8501 }
8502
8503 // after loading settings, we are no longer different from the XML on disk
8504 mData->flModifications = 0;
8505
8506 return S_OK;
8507}
8508
8509/**
8510 * Loads all snapshots starting from the given settings.
8511 *
8512 * @param data snapshot settings.
8513 * @param aCurSnapshotId Current snapshot ID from the settings file.
8514 */
8515HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8516 const Guid &aCurSnapshotId)
8517{
8518 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8519 AssertReturn(!i_isSessionMachine(), E_FAIL);
8520
8521 HRESULT hrc = S_OK;
8522
8523 std::list<const settings::Snapshot *> llSettingsTodo;
8524 llSettingsTodo.push_back(&data);
8525 std::list<Snapshot *> llParentsTodo;
8526 llParentsTodo.push_back(NULL);
8527
8528 while (llSettingsTodo.size() > 0)
8529 {
8530 const settings::Snapshot *current = llSettingsTodo.front();
8531 llSettingsTodo.pop_front();
8532 Snapshot *pParent = llParentsTodo.front();
8533 llParentsTodo.pop_front();
8534
8535 Utf8Str strStateFile;
8536 if (!current->strStateFile.isEmpty())
8537 {
8538 /* optional */
8539 strStateFile = current->strStateFile;
8540 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8541 if (RT_FAILURE(vrc))
8542 {
8543 setErrorBoth(E_FAIL, vrc,
8544 tr("Invalid saved state file path '%s' (%Rrc)"),
8545 strStateFile.c_str(), vrc);
8546 }
8547 }
8548
8549 /* create a snapshot machine object */
8550 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8551 pSnapshotMachine.createObject();
8552 hrc = pSnapshotMachine->initFromSettings(this,
8553 current->hardware,
8554 &current->debugging,
8555 &current->autostart,
8556 current->recordingSettings,
8557 current->uuid.ref(),
8558 strStateFile);
8559 if (FAILED(hrc)) break;
8560
8561 /* create a snapshot object */
8562 ComObjPtr<Snapshot> pSnapshot;
8563 pSnapshot.createObject();
8564 /* initialize the snapshot */
8565 hrc = pSnapshot->init(mParent, // VirtualBox object
8566 current->uuid,
8567 current->strName,
8568 current->strDescription,
8569 current->timestamp,
8570 pSnapshotMachine,
8571 pParent);
8572 if (FAILED(hrc)) break;
8573
8574 /* memorize the first snapshot if necessary */
8575 if (!mData->mFirstSnapshot)
8576 {
8577 Assert(pParent == NULL);
8578 mData->mFirstSnapshot = pSnapshot;
8579 }
8580
8581 /* memorize the current snapshot when appropriate */
8582 if ( !mData->mCurrentSnapshot
8583 && pSnapshot->i_getId() == aCurSnapshotId
8584 )
8585 mData->mCurrentSnapshot = pSnapshot;
8586
8587 /* create all children */
8588 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8589 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8590 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8591 {
8592 llSettingsTodo.push_back(&*it);
8593 llParentsTodo.push_back(pSnapshot);
8594 }
8595 }
8596
8597 return hrc;
8598}
8599
8600/**
8601 * Loads settings into mHWData.
8602 *
8603 * @param puuidRegistry Registry ID.
8604 * @param puuidSnapshot Snapshot ID
8605 * @param data Reference to the hardware settings.
8606 * @param pDbg Pointer to the debugging settings.
8607 * @param pAutostart Pointer to the autostart settings
8608 * @param recording Reference to recording settings.
8609 */
8610HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8611 const Guid *puuidSnapshot,
8612 const settings::Hardware &data,
8613 const settings::Debugging *pDbg,
8614 const settings::Autostart *pAutostart,
8615 const settings::RecordingSettings &recording)
8616{
8617 AssertReturn(!i_isSessionMachine(), E_FAIL);
8618
8619 HRESULT hrc = S_OK;
8620
8621 try
8622 {
8623 ComObjPtr<GuestOSType> pGuestOSType;
8624 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8625
8626 /* The hardware version attribute (optional). */
8627 mHWData->mHWVersion = data.strVersion;
8628 mHWData->mHardwareUUID = data.uuid;
8629
8630 mHWData->mCPUCount = data.cCPUs;
8631 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8632 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8633 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8634 mHWData->mCpuProfile = data.strCpuProfile;
8635
8636 // cpu
8637 if (mHWData->mCPUHotPlugEnabled)
8638 {
8639 for (settings::CpuList::const_iterator
8640 it = data.llCpus.begin();
8641 it != data.llCpus.end();
8642 ++it)
8643 {
8644 const settings::Cpu &cpu = *it;
8645
8646 mHWData->mCPUAttached[cpu.ulId] = true;
8647 }
8648 }
8649
8650 mHWData->mMemorySize = data.ulMemorySizeMB;
8651 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8652
8653 // boot order
8654 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8655 {
8656 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8657 if (it == data.mapBootOrder.end())
8658 mHWData->mBootOrder[i] = DeviceType_Null;
8659 else
8660 mHWData->mBootOrder[i] = it->second;
8661 }
8662
8663 mHWData->mPointingHIDType = data.pointingHIDType;
8664 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8665 mHWData->mParavirtProvider = data.paravirtProvider;
8666 mHWData->mParavirtDebug = data.strParavirtDebug;
8667 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8668
8669 /* GraphicsAdapter */
8670 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8671 if (FAILED(hrc)) return hrc;
8672
8673 /* VRDEServer */
8674 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8675 if (FAILED(hrc)) return hrc;
8676
8677 /* Platform */
8678 hrc = mPlatform->i_loadSettings(data.platformSettings);
8679 if (FAILED(hrc)) return hrc;
8680
8681 i_platformPropertiesUpdate();
8682
8683 /* Firmware */
8684 hrc = mFirmwareSettings->i_loadSettings(data.firmwareSettings);
8685 if (FAILED(hrc)) return hrc;
8686
8687 /* Recording */
8688 hrc = mRecordingSettings->i_loadSettings(recording);
8689 if (FAILED(hrc)) return hrc;
8690
8691 /* Trusted Platform Module */
8692 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8693 if (FAILED(hrc)) return hrc;
8694
8695 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
8696 if (FAILED(hrc)) return hrc;
8697
8698 // Bandwidth control (must come before network adapters)
8699 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
8700 if (FAILED(hrc)) return hrc;
8701
8702 /* USB controllers */
8703 for (settings::USBControllerList::const_iterator
8704 it = data.usbSettings.llUSBControllers.begin();
8705 it != data.usbSettings.llUSBControllers.end();
8706 ++it)
8707 {
8708 const settings::USBController &settingsCtrl = *it;
8709 ComObjPtr<USBController> newCtrl;
8710
8711 newCtrl.createObject();
8712 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8713 mUSBControllers->push_back(newCtrl);
8714 }
8715
8716 /* USB device filters */
8717 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8718 if (FAILED(hrc)) return hrc;
8719
8720 // network adapters (establish array size first and apply defaults, to
8721 // ensure reading the same settings as we saved, since the list skips
8722 // adapters having defaults)
8723 size_t const newCount = PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType);
8724 size_t const oldCount = mNetworkAdapters.size();
8725 if (newCount > oldCount)
8726 {
8727 mNetworkAdapters.resize(newCount);
8728 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8729 {
8730 unconst(mNetworkAdapters[slot]).createObject();
8731 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8732 }
8733 }
8734 else if (newCount < oldCount)
8735 mNetworkAdapters.resize(newCount);
8736 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8737 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8738 for (settings::NetworkAdaptersList::const_iterator
8739 it = data.llNetworkAdapters.begin();
8740 it != data.llNetworkAdapters.end();
8741 ++it)
8742 {
8743 const settings::NetworkAdapter &nic = *it;
8744
8745 /* slot uniqueness is guaranteed by XML Schema */
8746 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8747 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8748 if (FAILED(hrc)) return hrc;
8749 }
8750
8751 // serial ports (establish defaults first, to ensure reading the same
8752 // settings as we saved, since the list skips ports having defaults)
8753 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8754 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8755 for (settings::SerialPortsList::const_iterator
8756 it = data.llSerialPorts.begin();
8757 it != data.llSerialPorts.end();
8758 ++it)
8759 {
8760 const settings::SerialPort &s = *it;
8761
8762 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8763 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8764 if (FAILED(hrc)) return hrc;
8765 }
8766
8767 // parallel ports (establish defaults first, to ensure reading the same
8768 // settings as we saved, since the list skips ports having defaults)
8769 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8770 mParallelPorts[i]->i_applyDefaults();
8771 for (settings::ParallelPortsList::const_iterator
8772 it = data.llParallelPorts.begin();
8773 it != data.llParallelPorts.end();
8774 ++it)
8775 {
8776 const settings::ParallelPort &p = *it;
8777
8778 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8779 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8780 if (FAILED(hrc)) return hrc;
8781 }
8782
8783 /* Audio settings */
8784 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
8785 if (FAILED(hrc)) return hrc;
8786
8787 /* storage controllers */
8788 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
8789 if (FAILED(hrc)) return hrc;
8790
8791 /* Shared folders */
8792 for (settings::SharedFoldersList::const_iterator
8793 it = data.llSharedFolders.begin();
8794 it != data.llSharedFolders.end();
8795 ++it)
8796 {
8797 const settings::SharedFolder &sf = *it;
8798
8799 ComObjPtr<SharedFolder> sharedFolder;
8800 /* Check for double entries. Not allowed! */
8801 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8802 if (SUCCEEDED(hrc))
8803 return setError(VBOX_E_OBJECT_IN_USE,
8804 tr("Shared folder named '%s' already exists"),
8805 sf.strName.c_str());
8806
8807 /* Create the new shared folder. Don't break on error. This will be
8808 * reported when the machine starts. */
8809 sharedFolder.createObject();
8810 hrc = sharedFolder->init(i_getMachine(),
8811 sf.strName,
8812 sf.strHostPath,
8813 RT_BOOL(sf.fWritable),
8814 RT_BOOL(sf.fAutoMount),
8815 sf.strAutoMountPoint,
8816 false /* fFailOnError */);
8817 if (FAILED(hrc)) return hrc;
8818 mHWData->mSharedFolders.push_back(sharedFolder);
8819 }
8820
8821 // Clipboard
8822 mHWData->mClipboardMode = data.clipboardMode;
8823 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8824
8825 // drag'n'drop
8826 mHWData->mDnDMode = data.dndMode;
8827
8828 // guest settings
8829 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8830
8831 // IO settings
8832 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8833 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8834
8835 // Host PCI devices
8836 for (settings::HostPCIDeviceAttachmentList::const_iterator
8837 it = data.pciAttachments.begin();
8838 it != data.pciAttachments.end();
8839 ++it)
8840 {
8841 const settings::HostPCIDeviceAttachment &hpda = *it;
8842 ComObjPtr<PCIDeviceAttachment> pda;
8843
8844 pda.createObject();
8845 pda->i_loadSettings(this, hpda);
8846 mHWData->mPCIDeviceAssignments.push_back(pda);
8847 }
8848
8849 /*
8850 * (The following isn't really real hardware, but it lives in HWData
8851 * for reasons of convenience.)
8852 */
8853
8854#ifdef VBOX_WITH_GUEST_PROPS
8855 /* Guest properties (optional) */
8856
8857 /* Only load transient guest properties for configs which have saved
8858 * state, because there shouldn't be any for powered off VMs. The same
8859 * logic applies for snapshots, as offline snapshots shouldn't have
8860 * any such properties. They confuse the code in various places.
8861 * Note: can't rely on the machine state, as it isn't set yet. */
8862 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8863 /* apologies for the hacky unconst() usage, but this needs hacking
8864 * actually inconsistent settings into consistency, otherwise there
8865 * will be some corner cases where the inconsistency survives
8866 * surprisingly long without getting fixed, especially for snapshots
8867 * as there are no config changes. */
8868 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8869 for (settings::GuestPropertiesList::iterator
8870 it = llGuestProperties.begin();
8871 it != llGuestProperties.end();
8872 /*nothing*/)
8873 {
8874 const settings::GuestProperty &prop = *it;
8875 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8876 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8877 if ( fSkipTransientGuestProperties
8878 && ( fFlags & GUEST_PROP_F_TRANSIENT
8879 || fFlags & GUEST_PROP_F_TRANSRESET))
8880 {
8881 it = llGuestProperties.erase(it);
8882 continue;
8883 }
8884 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8885 mHWData->mGuestProperties[prop.strName] = property;
8886 ++it;
8887 }
8888#endif /* VBOX_WITH_GUEST_PROPS defined */
8889
8890 hrc = i_loadDebugging(pDbg);
8891 if (FAILED(hrc))
8892 return hrc;
8893
8894 mHWData->mAutostart = *pAutostart;
8895
8896 /* default frontend */
8897 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8898 }
8899 catch (std::bad_alloc &)
8900 {
8901 return E_OUTOFMEMORY;
8902 }
8903
8904 AssertComRC(hrc);
8905 return hrc;
8906}
8907
8908/**
8909 * Called from i_loadHardware() to load the debugging settings of the
8910 * machine.
8911 *
8912 * @param pDbg Pointer to the settings.
8913 */
8914HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8915{
8916 mHWData->mDebugging = *pDbg;
8917 /* no more processing currently required, this will probably change. */
8918
8919 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
8920 if (FAILED(hrc)) return hrc;
8921
8922 return S_OK;
8923}
8924
8925/**
8926 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8927 *
8928 * @param data storage settings.
8929 * @param puuidRegistry media registry ID to set media to or NULL;
8930 * see Machine::i_loadMachineDataFromSettings()
8931 * @param puuidSnapshot snapshot ID
8932 * @return
8933 */
8934HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8935 const Guid *puuidRegistry,
8936 const Guid *puuidSnapshot)
8937{
8938 AssertReturn(!i_isSessionMachine(), E_FAIL);
8939
8940 HRESULT hrc = S_OK;
8941
8942 for (settings::StorageControllersList::const_iterator
8943 it = data.llStorageControllers.begin();
8944 it != data.llStorageControllers.end();
8945 ++it)
8946 {
8947 const settings::StorageController &ctlData = *it;
8948
8949 ComObjPtr<StorageController> pCtl;
8950 /* Try to find one with the name first. */
8951 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8952 if (SUCCEEDED(hrc))
8953 return setError(VBOX_E_OBJECT_IN_USE,
8954 tr("Storage controller named '%s' already exists"),
8955 ctlData.strName.c_str());
8956
8957 pCtl.createObject();
8958 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
8959 if (FAILED(hrc)) return hrc;
8960
8961 mStorageControllers->push_back(pCtl);
8962
8963 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8964 if (FAILED(hrc)) return hrc;
8965
8966 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8967 if (FAILED(hrc)) return hrc;
8968
8969 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8970 if (FAILED(hrc)) return hrc;
8971
8972 /* Load the attached devices now. */
8973 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
8974 if (FAILED(hrc)) return hrc;
8975 }
8976
8977 return S_OK;
8978}
8979
8980/**
8981 * Called from i_loadStorageControllers for a controller's devices.
8982 *
8983 * @param aStorageController
8984 * @param data
8985 * @param puuidRegistry media registry ID to set media to or NULL; see
8986 * Machine::i_loadMachineDataFromSettings()
8987 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
8988 * @return
8989 */
8990HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8991 const settings::StorageController &data,
8992 const Guid *puuidRegistry,
8993 const Guid *puuidSnapshot)
8994{
8995 HRESULT hrc = S_OK;
8996
8997 /* paranoia: detect duplicate attachments */
8998 for (settings::AttachedDevicesList::const_iterator
8999 it = data.llAttachedDevices.begin();
9000 it != data.llAttachedDevices.end();
9001 ++it)
9002 {
9003 const settings::AttachedDevice &ad = *it;
9004
9005 for (settings::AttachedDevicesList::const_iterator it2 = it;
9006 it2 != data.llAttachedDevices.end();
9007 ++it2)
9008 {
9009 if (it == it2)
9010 continue;
9011
9012 const settings::AttachedDevice &ad2 = *it2;
9013
9014 if ( ad.lPort == ad2.lPort
9015 && ad.lDevice == ad2.lDevice)
9016 {
9017 return setError(E_FAIL,
9018 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9019 aStorageController->i_getName().c_str(),
9020 ad.lPort,
9021 ad.lDevice,
9022 mUserData->s.strName.c_str());
9023 }
9024 }
9025 }
9026
9027 for (settings::AttachedDevicesList::const_iterator
9028 it = data.llAttachedDevices.begin();
9029 it != data.llAttachedDevices.end();
9030 ++it)
9031 {
9032 const settings::AttachedDevice &dev = *it;
9033 ComObjPtr<Medium> medium;
9034
9035 switch (dev.deviceType)
9036 {
9037 case DeviceType_Floppy:
9038 case DeviceType_DVD:
9039 if (dev.strHostDriveSrc.isNotEmpty())
9040 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9041 false /* fRefresh */, medium);
9042 else
9043 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9044 dev.uuid,
9045 false /* fRefresh */,
9046 false /* aSetError */,
9047 medium);
9048 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9049 // This is not an error. The host drive or UUID might have vanished, so just go
9050 // ahead without this removeable medium attachment
9051 hrc = S_OK;
9052 break;
9053
9054 case DeviceType_HardDisk:
9055 {
9056 /* find a hard disk by UUID */
9057 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9058 if (FAILED(hrc))
9059 {
9060 if (i_isSnapshotMachine())
9061 {
9062 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9063 // so the user knows that the bad disk is in a snapshot somewhere
9064 com::ErrorInfo info;
9065 return setError(E_FAIL,
9066 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9067 puuidSnapshot->raw(),
9068 info.getText().raw());
9069 }
9070 return hrc;
9071 }
9072
9073 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9074
9075 if (medium->i_getType() == MediumType_Immutable)
9076 {
9077 if (i_isSnapshotMachine())
9078 return setError(E_FAIL,
9079 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9080 "of the virtual machine '%s' ('%s')"),
9081 medium->i_getLocationFull().c_str(),
9082 dev.uuid.raw(),
9083 puuidSnapshot->raw(),
9084 mUserData->s.strName.c_str(),
9085 mData->m_strConfigFileFull.c_str());
9086
9087 return setError(E_FAIL,
9088 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9089 medium->i_getLocationFull().c_str(),
9090 dev.uuid.raw(),
9091 mUserData->s.strName.c_str(),
9092 mData->m_strConfigFileFull.c_str());
9093 }
9094
9095 if (medium->i_getType() == MediumType_MultiAttach)
9096 {
9097 if (i_isSnapshotMachine())
9098 return setError(E_FAIL,
9099 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9100 "of the virtual machine '%s' ('%s')"),
9101 medium->i_getLocationFull().c_str(),
9102 dev.uuid.raw(),
9103 puuidSnapshot->raw(),
9104 mUserData->s.strName.c_str(),
9105 mData->m_strConfigFileFull.c_str());
9106
9107 return setError(E_FAIL,
9108 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9109 medium->i_getLocationFull().c_str(),
9110 dev.uuid.raw(),
9111 mUserData->s.strName.c_str(),
9112 mData->m_strConfigFileFull.c_str());
9113 }
9114
9115 if ( !i_isSnapshotMachine()
9116 && medium->i_getChildren().size() != 0
9117 )
9118 return setError(E_FAIL,
9119 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9120 "because it has %d differencing child hard disks"),
9121 medium->i_getLocationFull().c_str(),
9122 dev.uuid.raw(),
9123 mUserData->s.strName.c_str(),
9124 mData->m_strConfigFileFull.c_str(),
9125 medium->i_getChildren().size());
9126
9127 if (i_findAttachment(*mMediumAttachments.data(),
9128 medium))
9129 return setError(E_FAIL,
9130 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9131 medium->i_getLocationFull().c_str(),
9132 dev.uuid.raw(),
9133 mUserData->s.strName.c_str(),
9134 mData->m_strConfigFileFull.c_str());
9135
9136 break;
9137 }
9138
9139 default:
9140 return setError(E_FAIL,
9141 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9142 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9143 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9144 }
9145
9146 if (FAILED(hrc))
9147 break;
9148
9149 /* Bandwidth groups are loaded at this point. */
9150 ComObjPtr<BandwidthGroup> pBwGroup;
9151
9152 if (!dev.strBwGroup.isEmpty())
9153 {
9154 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9155 if (FAILED(hrc))
9156 return setError(E_FAIL,
9157 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9158 medium->i_getLocationFull().c_str(),
9159 dev.strBwGroup.c_str(),
9160 mUserData->s.strName.c_str(),
9161 mData->m_strConfigFileFull.c_str());
9162 pBwGroup->i_reference();
9163 }
9164
9165 const Utf8Str controllerName = aStorageController->i_getName();
9166 ComObjPtr<MediumAttachment> pAttachment;
9167 pAttachment.createObject();
9168 hrc = pAttachment->init(this,
9169 medium,
9170 controllerName,
9171 dev.lPort,
9172 dev.lDevice,
9173 dev.deviceType,
9174 false,
9175 dev.fPassThrough,
9176 dev.fTempEject,
9177 dev.fNonRotational,
9178 dev.fDiscard,
9179 dev.fHotPluggable,
9180 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9181 if (FAILED(hrc)) break;
9182
9183 /* associate the medium with this machine and snapshot */
9184 if (!medium.isNull())
9185 {
9186 AutoCaller medCaller(medium);
9187 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9188 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9189
9190 if (i_isSnapshotMachine())
9191 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9192 else
9193 hrc = medium->i_addBackReference(mData->mUuid);
9194 /* If the medium->addBackReference fails it sets an appropriate
9195 * error message, so no need to do any guesswork here. */
9196
9197 if (puuidRegistry)
9198 // caller wants registry ID to be set on all attached media (OVF import case)
9199 medium->i_addRegistry(*puuidRegistry);
9200 }
9201
9202 if (FAILED(hrc))
9203 break;
9204
9205 /* back up mMediumAttachments to let registeredInit() properly rollback
9206 * on failure (= limited accessibility) */
9207 i_setModified(IsModified_Storage);
9208 mMediumAttachments.backup();
9209 mMediumAttachments->push_back(pAttachment);
9210 }
9211
9212 return hrc;
9213}
9214
9215/**
9216 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9217 *
9218 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9219 * @param aSnapshot where to return the found snapshot
9220 * @param aSetError true to set extended error info on failure
9221 */
9222HRESULT Machine::i_findSnapshotById(const Guid &aId,
9223 ComObjPtr<Snapshot> &aSnapshot,
9224 bool aSetError /* = false */)
9225{
9226 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9227
9228 if (!mData->mFirstSnapshot)
9229 {
9230 if (aSetError)
9231 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9232 return E_FAIL;
9233 }
9234
9235 if (aId.isZero())
9236 aSnapshot = mData->mFirstSnapshot;
9237 else
9238 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9239
9240 if (!aSnapshot)
9241 {
9242 if (aSetError)
9243 return setError(E_FAIL,
9244 tr("Could not find a snapshot with UUID {%s}"),
9245 aId.toString().c_str());
9246 return E_FAIL;
9247 }
9248
9249 return S_OK;
9250}
9251
9252/**
9253 * Returns the snapshot with the given name or fails of no such snapshot.
9254 *
9255 * @param strName snapshot name to find
9256 * @param aSnapshot where to return the found snapshot
9257 * @param aSetError true to set extended error info on failure
9258 */
9259HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9260 ComObjPtr<Snapshot> &aSnapshot,
9261 bool aSetError /* = false */)
9262{
9263 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9264
9265 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9266
9267 if (!mData->mFirstSnapshot)
9268 {
9269 if (aSetError)
9270 return setError(VBOX_E_OBJECT_NOT_FOUND,
9271 tr("This machine does not have any snapshots"));
9272 return VBOX_E_OBJECT_NOT_FOUND;
9273 }
9274
9275 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9276
9277 if (!aSnapshot)
9278 {
9279 if (aSetError)
9280 return setError(VBOX_E_OBJECT_NOT_FOUND,
9281 tr("Could not find a snapshot named '%s'"), strName.c_str());
9282 return VBOX_E_OBJECT_NOT_FOUND;
9283 }
9284
9285 return S_OK;
9286}
9287
9288/**
9289 * Returns a storage controller object with the given name.
9290 *
9291 * @param aName storage controller name to find
9292 * @param aStorageController where to return the found storage controller
9293 * @param aSetError true to set extended error info on failure
9294 */
9295HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9296 ComObjPtr<StorageController> &aStorageController,
9297 bool aSetError /* = false */)
9298{
9299 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9300
9301 for (StorageControllerList::const_iterator
9302 it = mStorageControllers->begin();
9303 it != mStorageControllers->end();
9304 ++it)
9305 {
9306 if ((*it)->i_getName() == aName)
9307 {
9308 aStorageController = (*it);
9309 return S_OK;
9310 }
9311 }
9312
9313 if (aSetError)
9314 return setError(VBOX_E_OBJECT_NOT_FOUND,
9315 tr("Could not find a storage controller named '%s'"),
9316 aName.c_str());
9317 return VBOX_E_OBJECT_NOT_FOUND;
9318}
9319
9320/**
9321 * Returns a USB controller object with the given name.
9322 *
9323 * @param aName USB controller name to find
9324 * @param aUSBController where to return the found USB controller
9325 * @param aSetError true to set extended error info on failure
9326 */
9327HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9328 ComObjPtr<USBController> &aUSBController,
9329 bool aSetError /* = false */)
9330{
9331 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9332
9333 for (USBControllerList::const_iterator
9334 it = mUSBControllers->begin();
9335 it != mUSBControllers->end();
9336 ++it)
9337 {
9338 if ((*it)->i_getName() == aName)
9339 {
9340 aUSBController = (*it);
9341 return S_OK;
9342 }
9343 }
9344
9345 if (aSetError)
9346 return setError(VBOX_E_OBJECT_NOT_FOUND,
9347 tr("Could not find a storage controller named '%s'"),
9348 aName.c_str());
9349 return VBOX_E_OBJECT_NOT_FOUND;
9350}
9351
9352/**
9353 * Returns the number of USB controller instance of the given type.
9354 *
9355 * @param enmType USB controller type.
9356 */
9357ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9358{
9359 ULONG cCtrls = 0;
9360
9361 for (USBControllerList::const_iterator
9362 it = mUSBControllers->begin();
9363 it != mUSBControllers->end();
9364 ++it)
9365 {
9366 if ((*it)->i_getControllerType() == enmType)
9367 cCtrls++;
9368 }
9369
9370 return cCtrls;
9371}
9372
9373HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9374 MediumAttachmentList &atts)
9375{
9376 AutoCaller autoCaller(this);
9377 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
9378
9379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9380
9381 for (MediumAttachmentList::const_iterator
9382 it = mMediumAttachments->begin();
9383 it != mMediumAttachments->end();
9384 ++it)
9385 {
9386 const ComObjPtr<MediumAttachment> &pAtt = *it;
9387 // should never happen, but deal with NULL pointers in the list.
9388 AssertContinue(!pAtt.isNull());
9389
9390 // getControllerName() needs caller+read lock
9391 AutoCaller autoAttCaller(pAtt);
9392 if (FAILED(autoAttCaller.hrc()))
9393 {
9394 atts.clear();
9395 return autoAttCaller.hrc();
9396 }
9397 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9398
9399 if (pAtt->i_getControllerName() == aName)
9400 atts.push_back(pAtt);
9401 }
9402
9403 return S_OK;
9404}
9405
9406
9407/**
9408 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9409 * file if the machine name was changed and about creating a new settings file
9410 * if this is a new machine.
9411 *
9412 * @note Must be never called directly but only from #saveSettings().
9413 */
9414HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9415 bool *pfSettingsFileIsNew)
9416{
9417 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9418
9419 HRESULT hrc = S_OK;
9420
9421 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9422 /// @todo need to handle primary group change, too
9423
9424 /* attempt to rename the settings file if machine name is changed */
9425 if ( mUserData->s.fNameSync
9426 && mUserData.isBackedUp()
9427 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9428 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9429 )
9430 {
9431 bool dirRenamed = false;
9432 bool fileRenamed = false;
9433
9434 Utf8Str configFile, newConfigFile;
9435 Utf8Str configFilePrev, newConfigFilePrev;
9436 Utf8Str NVRAMFile, newNVRAMFile;
9437 Utf8Str configDir, newConfigDir;
9438
9439 do
9440 {
9441 int vrc = VINF_SUCCESS;
9442
9443 Utf8Str name = mUserData.backedUpData()->s.strName;
9444 Utf8Str newName = mUserData->s.strName;
9445 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9446 if (group == "/")
9447 group.setNull();
9448 Utf8Str newGroup = mUserData->s.llGroups.front();
9449 if (newGroup == "/")
9450 newGroup.setNull();
9451
9452 configFile = mData->m_strConfigFileFull;
9453
9454 /* first, rename the directory if it matches the group and machine name */
9455 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9456 /** @todo hack, make somehow use of ComposeMachineFilename */
9457 if (mUserData->s.fDirectoryIncludesUUID)
9458 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9459 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9460 /** @todo hack, make somehow use of ComposeMachineFilename */
9461 if (mUserData->s.fDirectoryIncludesUUID)
9462 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9463 configDir = configFile;
9464 configDir.stripFilename();
9465 newConfigDir = configDir;
9466 if ( configDir.length() >= groupPlusName.length()
9467 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9468 groupPlusName.c_str()))
9469 {
9470 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9471 Utf8Str newConfigBaseDir(newConfigDir);
9472 newConfigDir.append(newGroupPlusName);
9473 /* consistency: use \ if appropriate on the platform */
9474 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9475 /* new dir and old dir cannot be equal here because of 'if'
9476 * above and because name != newName */
9477 Assert(configDir != newConfigDir);
9478 if (!fSettingsFileIsNew)
9479 {
9480 /* perform real rename only if the machine is not new */
9481 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9482 if ( vrc == VERR_FILE_NOT_FOUND
9483 || vrc == VERR_PATH_NOT_FOUND)
9484 {
9485 /* create the parent directory, then retry renaming */
9486 Utf8Str parent(newConfigDir);
9487 parent.stripFilename();
9488 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9489 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9490 }
9491 if (RT_FAILURE(vrc))
9492 {
9493 hrc = setErrorBoth(E_FAIL, vrc,
9494 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9495 configDir.c_str(),
9496 newConfigDir.c_str(),
9497 vrc);
9498 break;
9499 }
9500 /* delete subdirectories which are no longer needed */
9501 Utf8Str dir(configDir);
9502 dir.stripFilename();
9503 while (dir != newConfigBaseDir && dir != ".")
9504 {
9505 vrc = RTDirRemove(dir.c_str());
9506 if (RT_FAILURE(vrc))
9507 break;
9508 dir.stripFilename();
9509 }
9510 dirRenamed = true;
9511 }
9512 }
9513
9514 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9515
9516 /* then try to rename the settings file itself */
9517 if (newConfigFile != configFile)
9518 {
9519 /* get the path to old settings file in renamed directory */
9520 Assert(mData->m_strConfigFileFull == configFile);
9521 configFile.printf("%s%c%s",
9522 newConfigDir.c_str(),
9523 RTPATH_DELIMITER,
9524 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9525 if (!fSettingsFileIsNew)
9526 {
9527 /* perform real rename only if the machine is not new */
9528 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9529 if (RT_FAILURE(vrc))
9530 {
9531 hrc = setErrorBoth(E_FAIL, vrc,
9532 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9533 configFile.c_str(),
9534 newConfigFile.c_str(),
9535 vrc);
9536 break;
9537 }
9538 fileRenamed = true;
9539 configFilePrev = configFile;
9540 configFilePrev += "-prev";
9541 newConfigFilePrev = newConfigFile;
9542 newConfigFilePrev += "-prev";
9543 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9544 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9545 if (NVRAMFile.isNotEmpty())
9546 {
9547 // in the NVRAM file path, replace the old directory with the new directory
9548 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9549 {
9550 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9551 NVRAMFile = newConfigDir + strNVRAMFile;
9552 }
9553 newNVRAMFile = newConfigFile;
9554 newNVRAMFile.stripSuffix();
9555 newNVRAMFile += ".nvram";
9556 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9557 }
9558 }
9559 }
9560
9561 // update m_strConfigFileFull amd mConfigFile
9562 mData->m_strConfigFileFull = newConfigFile;
9563 // compute the relative path too
9564 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9565
9566 // store the old and new so that VirtualBox::i_saveSettings() can update
9567 // the media registry
9568 if ( mData->mRegistered
9569 && (configDir != newConfigDir || configFile != newConfigFile))
9570 {
9571 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9572
9573 if (pfNeedsGlobalSaveSettings)
9574 *pfNeedsGlobalSaveSettings = true;
9575 }
9576
9577 // in the saved state file path, replace the old directory with the new directory
9578 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9579 {
9580 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9581 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9582 }
9583 if (newNVRAMFile.isNotEmpty())
9584 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9585
9586 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9587 if (mData->mFirstSnapshot)
9588 {
9589 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9590 newConfigDir.c_str());
9591 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9592 newConfigDir.c_str());
9593 }
9594 }
9595 while (0);
9596
9597 if (FAILED(hrc))
9598 {
9599 /* silently try to rename everything back */
9600 if (fileRenamed)
9601 {
9602 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9603 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9604 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9605 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9606 }
9607 if (dirRenamed)
9608 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9609 }
9610
9611 if (FAILED(hrc)) return hrc;
9612 }
9613
9614 if (fSettingsFileIsNew)
9615 {
9616 /* create a virgin config file */
9617 int vrc = VINF_SUCCESS;
9618
9619 /* ensure the settings directory exists */
9620 Utf8Str path(mData->m_strConfigFileFull);
9621 path.stripFilename();
9622 if (!RTDirExists(path.c_str()))
9623 {
9624 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9625 if (RT_FAILURE(vrc))
9626 {
9627 return setErrorBoth(E_FAIL, vrc,
9628 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9629 path.c_str(),
9630 vrc);
9631 }
9632 }
9633
9634 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9635 path = mData->m_strConfigFileFull;
9636 RTFILE f = NIL_RTFILE;
9637 vrc = RTFileOpen(&f, path.c_str(),
9638 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9639 if (RT_FAILURE(vrc))
9640 return setErrorBoth(E_FAIL, vrc,
9641 tr("Could not create the settings file '%s' (%Rrc)"),
9642 path.c_str(),
9643 vrc);
9644 RTFileClose(f);
9645 }
9646 if (pfSettingsFileIsNew)
9647 *pfSettingsFileIsNew = fSettingsFileIsNew;
9648
9649 return hrc;
9650}
9651
9652/**
9653 * Saves and commits machine data, user data and hardware data.
9654 *
9655 * Note that on failure, the data remains uncommitted.
9656 *
9657 * @a aFlags may combine the following flags:
9658 *
9659 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9660 * Used when saving settings after an operation that makes them 100%
9661 * correspond to the settings from the current snapshot.
9662 * - SaveS_Force: settings will be saved without doing a deep compare of the
9663 * settings structures. This is used when this is called because snapshots
9664 * have changed to avoid the overhead of the deep compare.
9665 *
9666 * @note Must be called from under this object's write lock. Locks children for
9667 * writing.
9668 *
9669 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9670 * initialized to false and that will be set to true by this function if
9671 * the caller must invoke VirtualBox::i_saveSettings() because the global
9672 * settings have changed. This will happen if a machine rename has been
9673 * saved and the global machine and media registries will therefore need
9674 * updating.
9675 * @param alock Reference to the lock for this machine object.
9676 * @param aFlags Flags.
9677 */
9678HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9679 AutoWriteLock &alock,
9680 int aFlags /*= 0*/)
9681{
9682 LogFlowThisFuncEnter();
9683
9684 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9685
9686 /* make sure child objects are unable to modify the settings while we are
9687 * saving them */
9688 i_ensureNoStateDependencies(alock);
9689
9690 AssertReturn(!i_isSnapshotMachine(),
9691 E_FAIL);
9692
9693 if (!mData->mAccessible)
9694 return setError(VBOX_E_INVALID_VM_STATE,
9695 tr("The machine is not accessible, so cannot save settings"));
9696
9697 HRESULT hrc = S_OK;
9698 PCVBOXCRYPTOIF pCryptoIf = NULL;
9699 const char *pszPassword = NULL;
9700 SecretKey *pKey = NULL;
9701
9702#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9703 if (mData->mstrKeyId.isNotEmpty())
9704 {
9705 /* VM is going to be encrypted. */
9706 alock.release(); /** @todo Revise the locking. */
9707 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
9708 alock.acquire();
9709 if (FAILED(hrc)) return hrc; /* Error is set. */
9710
9711 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
9712 if (RT_SUCCESS(vrc))
9713 pszPassword = (const char *)pKey->getKeyBuffer();
9714 else
9715 {
9716 mParent->i_releaseCryptoIf(pCryptoIf);
9717 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
9718 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
9719 mData->mstrKeyId.c_str(), vrc);
9720 }
9721 }
9722#else
9723 RT_NOREF(pKey);
9724#endif
9725
9726 bool fNeedsWrite = false;
9727 bool fSettingsFileIsNew = false;
9728
9729 /* First, prepare to save settings. It will care about renaming the
9730 * settings directory and file if the machine name was changed and about
9731 * creating a new settings file if this is a new machine. */
9732 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
9733 if (FAILED(hrc))
9734 {
9735#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9736 if (pCryptoIf)
9737 {
9738 alock.release(); /** @todo Revise the locking. */
9739 mParent->i_releaseCryptoIf(pCryptoIf);
9740 alock.acquire();
9741 }
9742 if (pKey)
9743 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9744#endif
9745 return hrc;
9746 }
9747
9748 // keep a pointer to the current settings structures
9749 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9750 settings::MachineConfigFile *pNewConfig = NULL;
9751
9752 try
9753 {
9754 // make a fresh one to have everyone write stuff into
9755 pNewConfig = new settings::MachineConfigFile(NULL);
9756 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9757#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9758 pNewConfig->strKeyId = mData->mstrKeyId;
9759 pNewConfig->strKeyStore = mData->mstrKeyStore;
9760#endif
9761
9762 // now go and copy all the settings data from COM to the settings structures
9763 // (this calls i_saveSettings() on all the COM objects in the machine)
9764 i_copyMachineDataToSettings(*pNewConfig);
9765
9766 if (aFlags & SaveS_ResetCurStateModified)
9767 {
9768 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9769 mData->mCurrentStateModified = FALSE;
9770 fNeedsWrite = true; // always, no need to compare
9771 }
9772 else if (aFlags & SaveS_Force)
9773 {
9774 fNeedsWrite = true; // always, no need to compare
9775 }
9776 else
9777 {
9778 if (!mData->mCurrentStateModified)
9779 {
9780 // do a deep compare of the settings that we just saved with the settings
9781 // previously stored in the config file; this invokes MachineConfigFile::operator==
9782 // which does a deep compare of all the settings, which is expensive but less expensive
9783 // than writing out XML in vain
9784 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9785
9786 // could still be modified if any settings changed
9787 mData->mCurrentStateModified = fAnySettingsChanged;
9788
9789 fNeedsWrite = fAnySettingsChanged;
9790 }
9791 else
9792 fNeedsWrite = true;
9793 }
9794
9795 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9796
9797 if (fNeedsWrite)
9798 {
9799 // now spit it all out!
9800 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
9801 if (aFlags & SaveS_RemoveBackup)
9802 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
9803 }
9804
9805 mData->pMachineConfigFile = pNewConfig;
9806 delete pOldConfig;
9807 i_commit();
9808
9809 // after saving settings, we are no longer different from the XML on disk
9810 mData->flModifications = 0;
9811 }
9812 catch (HRESULT err)
9813 {
9814 // we assume that error info is set by the thrower
9815 hrc = err;
9816
9817 // delete any newly created settings file
9818 if (fSettingsFileIsNew)
9819 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
9820
9821 // restore old config
9822 delete pNewConfig;
9823 mData->pMachineConfigFile = pOldConfig;
9824 }
9825 catch (...)
9826 {
9827 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9828 }
9829
9830#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9831 if (pCryptoIf)
9832 {
9833 alock.release(); /** @todo Revise the locking. */
9834 mParent->i_releaseCryptoIf(pCryptoIf);
9835 alock.acquire();
9836 }
9837 if (pKey)
9838 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9839#endif
9840
9841 if (fNeedsWrite)
9842 {
9843 /* Fire the data change event, even on failure (since we've already
9844 * committed all data). This is done only for SessionMachines because
9845 * mutable Machine instances are always not registered (i.e. private
9846 * to the client process that creates them) and thus don't need to
9847 * inform callbacks. */
9848 if (i_isSessionMachine())
9849 mParent->i_onMachineDataChanged(mData->mUuid);
9850 }
9851
9852 LogFlowThisFunc(("hrc=%08X\n", hrc));
9853 LogFlowThisFuncLeave();
9854 return hrc;
9855}
9856
9857/**
9858 * Implementation for saving the machine settings into the given
9859 * settings::MachineConfigFile instance. This copies machine extradata
9860 * from the previous machine config file in the instance data, if any.
9861 *
9862 * This gets called from two locations:
9863 *
9864 * -- Machine::i_saveSettings(), during the regular XML writing;
9865 *
9866 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9867 * exported to OVF and we write the VirtualBox proprietary XML
9868 * into a <vbox:Machine> tag.
9869 *
9870 * This routine fills all the fields in there, including snapshots, *except*
9871 * for the following:
9872 *
9873 * -- fCurrentStateModified. There is some special logic associated with that.
9874 *
9875 * The caller can then call MachineConfigFile::write() or do something else
9876 * with it.
9877 *
9878 * Caller must hold the machine lock!
9879 *
9880 * This throws XML errors and HRESULT, so the caller must have a catch block!
9881 */
9882void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9883{
9884 // deep copy extradata, being extra careful with self assignment (the STL
9885 // map assignment on Mac OS X clang based Xcode isn't checking)
9886 if (&config != mData->pMachineConfigFile)
9887 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9888
9889 config.uuid = mData->mUuid;
9890
9891 // copy name, description, OS type, teleport, UTC etc.
9892 config.machineUserData = mUserData->s;
9893
9894#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9895 config.strStateKeyId = mSSData->strStateKeyId;
9896 config.strStateKeyStore = mSSData->strStateKeyStore;
9897 config.strLogKeyId = mData->mstrLogKeyId;
9898 config.strLogKeyStore = mData->mstrLogKeyStore;
9899#endif
9900
9901 if ( mData->mMachineState == MachineState_Saved
9902 || mData->mMachineState == MachineState_AbortedSaved
9903 || mData->mMachineState == MachineState_Restoring
9904 // when doing certain snapshot operations we may or may not have
9905 // a saved state in the current state, so keep everything as is
9906 || ( ( mData->mMachineState == MachineState_Snapshotting
9907 || mData->mMachineState == MachineState_DeletingSnapshot
9908 || mData->mMachineState == MachineState_RestoringSnapshot)
9909 && (!mSSData->strStateFilePath.isEmpty())
9910 )
9911 )
9912 {
9913 Assert(!mSSData->strStateFilePath.isEmpty());
9914 /* try to make the file name relative to the settings file dir */
9915 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9916 }
9917 else
9918 {
9919 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9920 config.strStateFile.setNull();
9921 }
9922
9923 if (mData->mCurrentSnapshot)
9924 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9925 else
9926 config.uuidCurrentSnapshot.clear();
9927
9928 config.timeLastStateChange = mData->mLastStateChange;
9929 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
9930 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9931
9932 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
9933 if (FAILED(hrc)) throw hrc;
9934
9935 // save machine's media registry if this is VirtualBox 4.0 or later
9936 if (config.canHaveOwnMediaRegistry())
9937 {
9938 // determine machine folder
9939 Utf8Str strMachineFolder = i_getSettingsFileFull();
9940 strMachineFolder.stripFilename();
9941 mParent->i_saveMediaRegistry(config.mediaRegistry,
9942 i_getId(), // only media with registry ID == machine UUID
9943 strMachineFolder);
9944 // this throws HRESULT
9945 }
9946
9947 // save snapshots
9948 hrc = i_saveAllSnapshots(config);
9949 if (FAILED(hrc)) throw hrc;
9950}
9951
9952/**
9953 * Saves all snapshots of the machine into the given machine config file. Called
9954 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9955 * @param config
9956 * @return
9957 */
9958HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9959{
9960 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9961
9962 HRESULT hrc = S_OK;
9963
9964 try
9965 {
9966 config.llFirstSnapshot.clear();
9967
9968 if (mData->mFirstSnapshot)
9969 {
9970 // the settings use a list for "the first snapshot"
9971 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9972
9973 // get reference to the snapshot on the list and work on that
9974 // element straight in the list to avoid excessive copying later
9975 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9976 if (FAILED(hrc)) throw hrc;
9977 }
9978
9979// if (mType == IsSessionMachine)
9980// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9981
9982 }
9983 catch (HRESULT err)
9984 {
9985 /* we assume that error info is set by the thrower */
9986 hrc = err;
9987 }
9988 catch (...)
9989 {
9990 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9991 }
9992
9993 return hrc;
9994}
9995
9996/**
9997 * Saves the VM hardware configuration. It is assumed that the
9998 * given node is empty.
9999 *
10000 * @param data Reference to the settings object for the hardware config.
10001 * @param pDbg Pointer to the settings object for the debugging config
10002 * which happens to live in mHWData.
10003 * @param pAutostart Pointer to the settings object for the autostart config
10004 * which happens to live in mHWData.
10005 * @param recording Reference to reecording settings.
10006 */
10007HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10008 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10009{
10010 HRESULT hrc = S_OK;
10011
10012 try
10013 {
10014 /* The hardware version attribute (optional).
10015 Automatically upgrade from 1 to current default hardware version
10016 when there is no saved state. (ugly!) */
10017 if ( mHWData->mHWVersion == "1"
10018 && mSSData->strStateFilePath.isEmpty()
10019 )
10020 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10021
10022 data.strVersion = mHWData->mHWVersion;
10023 data.uuid = mHWData->mHardwareUUID;
10024
10025 // CPU
10026 data.cCPUs = mHWData->mCPUCount;
10027 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10028 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10029 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10030 data.strCpuProfile = mHWData->mCpuProfile;
10031
10032 data.llCpus.clear();
10033 if (data.fCpuHotPlug)
10034 {
10035 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10036 {
10037 if (mHWData->mCPUAttached[idx])
10038 {
10039 settings::Cpu cpu;
10040 cpu.ulId = idx;
10041 data.llCpus.push_back(cpu);
10042 }
10043 }
10044 }
10045
10046 // memory
10047 data.ulMemorySizeMB = mHWData->mMemorySize;
10048 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10049
10050 // HID
10051 data.pointingHIDType = mHWData->mPointingHIDType;
10052 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10053
10054 // paravirt
10055 data.paravirtProvider = mHWData->mParavirtProvider;
10056 data.strParavirtDebug = mHWData->mParavirtDebug;
10057
10058 // emulated USB card reader
10059 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10060
10061 // boot order
10062 data.mapBootOrder.clear();
10063 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10064 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10065
10066 /* VRDEServer settings (optional) */
10067 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10068 if (FAILED(hrc)) throw hrc;
10069
10070 /* Platform (required) */
10071 hrc = mPlatform->i_saveSettings(data.platformSettings);
10072 if (FAILED(hrc)) return hrc;
10073
10074 /* Firmware settings (required) */
10075 hrc = mFirmwareSettings->i_saveSettings(data.firmwareSettings);
10076 if (FAILED(hrc)) throw hrc;
10077
10078 /* Recording settings. */
10079 hrc = mRecordingSettings->i_saveSettings(recording);
10080 if (FAILED(hrc)) throw hrc;
10081
10082 /* Trusted Platform Module settings (required) */
10083 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10084 if (FAILED(hrc)) throw hrc;
10085
10086 /* NVRAM settings (required) */
10087 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10088 if (FAILED(hrc)) throw hrc;
10089
10090 /* GraphicsAdapter settings (required) */
10091 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10092 if (FAILED(hrc)) throw hrc;
10093
10094 /* USB Controller (required) */
10095 data.usbSettings.llUSBControllers.clear();
10096 for (USBControllerList::const_iterator
10097 it = mUSBControllers->begin();
10098 it != mUSBControllers->end();
10099 ++it)
10100 {
10101 ComObjPtr<USBController> ctrl = *it;
10102 settings::USBController settingsCtrl;
10103
10104 settingsCtrl.strName = ctrl->i_getName();
10105 settingsCtrl.enmType = ctrl->i_getControllerType();
10106
10107 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10108 }
10109
10110 /* USB device filters (required) */
10111 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10112 if (FAILED(hrc)) throw hrc;
10113
10114 /* Network adapters (required) */
10115 size_t const uMaxNICs =
10116 RT_MIN(PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType), mNetworkAdapters.size());
10117 data.llNetworkAdapters.clear();
10118 /* Write out only the nominal number of network adapters for this
10119 * chipset type. Since Machine::commit() hasn't been called there
10120 * may be extra NIC settings in the vector. */
10121 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10122 {
10123 settings::NetworkAdapter nic;
10124 nic.ulSlot = (uint32_t)slot;
10125 /* paranoia check... must not be NULL, but must not crash either. */
10126 if (mNetworkAdapters[slot])
10127 {
10128 if (mNetworkAdapters[slot]->i_hasDefaults())
10129 continue;
10130
10131 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10132 if (FAILED(hrc)) throw hrc;
10133
10134 data.llNetworkAdapters.push_back(nic);
10135 }
10136 }
10137
10138 /* Serial ports */
10139 data.llSerialPorts.clear();
10140 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10141 {
10142 if (mSerialPorts[slot]->i_hasDefaults())
10143 continue;
10144
10145 settings::SerialPort s;
10146 s.ulSlot = slot;
10147 hrc = mSerialPorts[slot]->i_saveSettings(s);
10148 if (FAILED(hrc)) return hrc;
10149
10150 data.llSerialPorts.push_back(s);
10151 }
10152
10153 /* Parallel ports */
10154 data.llParallelPorts.clear();
10155 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10156 {
10157 if (mParallelPorts[slot]->i_hasDefaults())
10158 continue;
10159
10160 settings::ParallelPort p;
10161 p.ulSlot = slot;
10162 hrc = mParallelPorts[slot]->i_saveSettings(p);
10163 if (FAILED(hrc)) return hrc;
10164
10165 data.llParallelPorts.push_back(p);
10166 }
10167
10168 /* Audio settings */
10169 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10170 if (FAILED(hrc)) return hrc;
10171
10172 hrc = i_saveStorageControllers(data.storage);
10173 if (FAILED(hrc)) return hrc;
10174
10175 /* Shared folders */
10176 data.llSharedFolders.clear();
10177 for (HWData::SharedFolderList::const_iterator
10178 it = mHWData->mSharedFolders.begin();
10179 it != mHWData->mSharedFolders.end();
10180 ++it)
10181 {
10182 SharedFolder *pSF = *it;
10183 AutoCaller sfCaller(pSF);
10184 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10185 settings::SharedFolder sf;
10186 sf.strName = pSF->i_getName();
10187 sf.strHostPath = pSF->i_getHostPath();
10188 sf.fWritable = !!pSF->i_isWritable();
10189 sf.fAutoMount = !!pSF->i_isAutoMounted();
10190 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10191
10192 data.llSharedFolders.push_back(sf);
10193 }
10194
10195 // clipboard
10196 data.clipboardMode = mHWData->mClipboardMode;
10197 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10198
10199 // drag'n'drop
10200 data.dndMode = mHWData->mDnDMode;
10201
10202 /* Guest */
10203 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10204
10205 // IO settings
10206 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10207 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10208
10209 /* BandwidthControl (required) */
10210 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10211 if (FAILED(hrc)) throw hrc;
10212
10213 /* Host PCI devices */
10214 data.pciAttachments.clear();
10215 for (HWData::PCIDeviceAssignmentList::const_iterator
10216 it = mHWData->mPCIDeviceAssignments.begin();
10217 it != mHWData->mPCIDeviceAssignments.end();
10218 ++it)
10219 {
10220 ComObjPtr<PCIDeviceAttachment> pda = *it;
10221 settings::HostPCIDeviceAttachment hpda;
10222
10223 hrc = pda->i_saveSettings(hpda);
10224 if (FAILED(hrc)) throw hrc;
10225
10226 data.pciAttachments.push_back(hpda);
10227 }
10228
10229 // guest properties
10230 data.llGuestProperties.clear();
10231#ifdef VBOX_WITH_GUEST_PROPS
10232 for (HWData::GuestPropertyMap::const_iterator
10233 it = mHWData->mGuestProperties.begin();
10234 it != mHWData->mGuestProperties.end();
10235 ++it)
10236 {
10237 HWData::GuestProperty property = it->second;
10238
10239 /* Remove transient guest properties at shutdown unless we
10240 * are saving state. Note that restoring snapshot intentionally
10241 * keeps them, they will be removed if appropriate once the final
10242 * machine state is set (as crashes etc. need to work). */
10243 if ( ( mData->mMachineState == MachineState_PoweredOff
10244 || mData->mMachineState == MachineState_Aborted
10245 || mData->mMachineState == MachineState_Teleported)
10246 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10247 continue;
10248 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10249 prop.strName = it->first;
10250 prop.strValue = property.strValue;
10251 prop.timestamp = (uint64_t)property.mTimestamp;
10252 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10253 GuestPropWriteFlags(property.mFlags, szFlags);
10254 prop.strFlags = szFlags;
10255
10256 data.llGuestProperties.push_back(prop);
10257 }
10258
10259 /* I presume this doesn't require a backup(). */
10260 mData->mGuestPropertiesModified = FALSE;
10261#endif /* VBOX_WITH_GUEST_PROPS defined */
10262
10263 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10264 if (FAILED(hrc)) throw hrc;
10265
10266 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10267 *pAutostart = mHWData->mAutostart;
10268
10269 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10270 }
10271 catch (std::bad_alloc &)
10272 {
10273 return E_OUTOFMEMORY;
10274 }
10275
10276 AssertComRC(hrc);
10277 return hrc;
10278}
10279
10280/**
10281 * Saves the storage controller configuration.
10282 *
10283 * @param data storage settings.
10284 */
10285HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10286{
10287 data.llStorageControllers.clear();
10288
10289 for (StorageControllerList::const_iterator
10290 it = mStorageControllers->begin();
10291 it != mStorageControllers->end();
10292 ++it)
10293 {
10294 ComObjPtr<StorageController> pCtl = *it;
10295
10296 settings::StorageController ctl;
10297 ctl.strName = pCtl->i_getName();
10298 ctl.controllerType = pCtl->i_getControllerType();
10299 ctl.storageBus = pCtl->i_getStorageBus();
10300 ctl.ulInstance = pCtl->i_getInstance();
10301 ctl.fBootable = pCtl->i_getBootable();
10302
10303 /* Save the port count. */
10304 ULONG portCount;
10305 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10306 ComAssertComRCRet(hrc, hrc);
10307 ctl.ulPortCount = portCount;
10308
10309 /* Save fUseHostIOCache */
10310 BOOL fUseHostIOCache;
10311 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10312 ComAssertComRCRet(hrc, hrc);
10313 ctl.fUseHostIOCache = !!fUseHostIOCache;
10314
10315 /* save the devices now. */
10316 hrc = i_saveStorageDevices(pCtl, ctl);
10317 ComAssertComRCRet(hrc, hrc);
10318
10319 data.llStorageControllers.push_back(ctl);
10320 }
10321
10322 return S_OK;
10323}
10324
10325/**
10326 * Saves the hard disk configuration.
10327 */
10328HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10329 settings::StorageController &data)
10330{
10331 MediumAttachmentList atts;
10332
10333 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10334 if (FAILED(hrc)) return hrc;
10335
10336 data.llAttachedDevices.clear();
10337 for (MediumAttachmentList::const_iterator
10338 it = atts.begin();
10339 it != atts.end();
10340 ++it)
10341 {
10342 settings::AttachedDevice dev;
10343 IMediumAttachment *iA = *it;
10344 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10345 Medium *pMedium = pAttach->i_getMedium();
10346
10347 dev.deviceType = pAttach->i_getType();
10348 dev.lPort = pAttach->i_getPort();
10349 dev.lDevice = pAttach->i_getDevice();
10350 dev.fPassThrough = pAttach->i_getPassthrough();
10351 dev.fHotPluggable = pAttach->i_getHotPluggable();
10352 if (pMedium)
10353 {
10354 if (pMedium->i_isHostDrive())
10355 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10356 else
10357 dev.uuid = pMedium->i_getId();
10358 dev.fTempEject = pAttach->i_getTempEject();
10359 dev.fNonRotational = pAttach->i_getNonRotational();
10360 dev.fDiscard = pAttach->i_getDiscard();
10361 }
10362
10363 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10364
10365 data.llAttachedDevices.push_back(dev);
10366 }
10367
10368 return S_OK;
10369}
10370
10371/**
10372 * Saves machine state settings as defined by aFlags
10373 * (SaveSTS_* values).
10374 *
10375 * @param aFlags Combination of SaveSTS_* flags.
10376 *
10377 * @note Locks objects for writing.
10378 */
10379HRESULT Machine::i_saveStateSettings(int aFlags)
10380{
10381 if (aFlags == 0)
10382 return S_OK;
10383
10384 AutoCaller autoCaller(this);
10385 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10386
10387 /* This object's write lock is also necessary to serialize file access
10388 * (prevent concurrent reads and writes) */
10389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10390
10391 HRESULT hrc = S_OK;
10392
10393 Assert(mData->pMachineConfigFile);
10394
10395 try
10396 {
10397 if (aFlags & SaveSTS_CurStateModified)
10398 mData->pMachineConfigFile->fCurrentStateModified = true;
10399
10400 if (aFlags & SaveSTS_StateFilePath)
10401 {
10402 if (!mSSData->strStateFilePath.isEmpty())
10403 /* try to make the file name relative to the settings file dir */
10404 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10405 else
10406 mData->pMachineConfigFile->strStateFile.setNull();
10407 }
10408
10409 if (aFlags & SaveSTS_StateTimeStamp)
10410 {
10411 Assert( mData->mMachineState != MachineState_Aborted
10412 || mSSData->strStateFilePath.isEmpty());
10413
10414 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10415
10416 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10417 || mData->mMachineState == MachineState_AbortedSaved);
10418/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10419 }
10420
10421 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10422 }
10423 catch (...)
10424 {
10425 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10426 }
10427
10428 return hrc;
10429}
10430
10431/**
10432 * Ensures that the given medium is added to a media registry. If this machine
10433 * was created with 4.0 or later, then the machine registry is used. Otherwise
10434 * the global VirtualBox media registry is used.
10435 *
10436 * Caller must NOT hold machine lock, media tree or any medium locks!
10437 *
10438 * @param pMedium
10439 */
10440void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10441{
10442 /* Paranoia checks: do not hold machine or media tree locks. */
10443 AssertReturnVoid(!isWriteLockOnCurrentThread());
10444 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10445
10446 ComObjPtr<Medium> pBase;
10447 {
10448 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10449 pBase = pMedium->i_getBase();
10450 }
10451
10452 /* Paranoia checks: do not hold medium locks. */
10453 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10454 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10455
10456 // decide which medium registry to use now that the medium is attached:
10457 Guid uuid;
10458 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10459 if (fCanHaveOwnMediaRegistry)
10460 // machine XML is VirtualBox 4.0 or higher:
10461 uuid = i_getId(); // machine UUID
10462 else
10463 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10464
10465 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10466 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10467 if (pMedium->i_addRegistry(uuid))
10468 mParent->i_markRegistryModified(uuid);
10469
10470 /* For more complex hard disk structures it can happen that the base
10471 * medium isn't yet associated with any medium registry. Do that now. */
10472 if (pMedium != pBase)
10473 {
10474 /* Tree lock needed by Medium::addRegistryAll. */
10475 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10476 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10477 {
10478 treeLock.release();
10479 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10480 treeLock.acquire();
10481 }
10482 if (pBase->i_addRegistryAll(uuid))
10483 {
10484 treeLock.release();
10485 mParent->i_markRegistryModified(uuid);
10486 }
10487 }
10488}
10489
10490/**
10491 * Physically deletes a file belonging to a machine.
10492 *
10493 * @returns HRESULT
10494 * @retval VBOX_E_FILE_ERROR on failure.
10495 * @param strFile File to delete.
10496 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
10497 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
10498 * @param strWhat File hint which will be used when setting an error. Optional.
10499 * @param prc Where to return IPRT's status code on failure.
10500 * Optional and can be NULL.
10501 */
10502HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
10503 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
10504{
10505 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
10506
10507 HRESULT hrc = S_OK;
10508
10509 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
10510
10511 int vrc = RTFileDelete(strFile.c_str());
10512 if (RT_FAILURE(vrc))
10513 {
10514 if ( !fIgnoreFailures
10515 /* Don't (externally) bitch about stuff which doesn't exist. */
10516 && ( vrc != VERR_FILE_NOT_FOUND
10517 && vrc != VERR_PATH_NOT_FOUND
10518 )
10519 )
10520 {
10521 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
10522
10523 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
10524 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
10525 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
10526 }
10527 }
10528
10529 if (prc)
10530 *prc = vrc;
10531 return hrc;
10532}
10533
10534/**
10535 * Creates differencing hard disks for all normal hard disks attached to this
10536 * machine and a new set of attachments to refer to created disks.
10537 *
10538 * Used when taking a snapshot or when deleting the current state. Gets called
10539 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10540 *
10541 * This method assumes that mMediumAttachments contains the original hard disk
10542 * attachments it needs to create diffs for. On success, these attachments will
10543 * be replaced with the created diffs.
10544 *
10545 * Attachments with non-normal hard disks are left as is.
10546 *
10547 * If @a aOnline is @c false then the original hard disks that require implicit
10548 * diffs will be locked for reading. Otherwise it is assumed that they are
10549 * already locked for writing (when the VM was started). Note that in the latter
10550 * case it is responsibility of the caller to lock the newly created diffs for
10551 * writing if this method succeeds.
10552 *
10553 * @param aProgress Progress object to run (must contain at least as
10554 * many operations left as the number of hard disks
10555 * attached).
10556 * @param aWeight Weight of this operation.
10557 * @param aOnline Whether the VM was online prior to this operation.
10558 *
10559 * @note The progress object is not marked as completed, neither on success nor
10560 * on failure. This is a responsibility of the caller.
10561 *
10562 * @note Locks this object and the media tree for writing.
10563 */
10564HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10565 ULONG aWeight,
10566 bool aOnline)
10567{
10568 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10569
10570 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10571 AssertReturn(!!pProgressControl, E_INVALIDARG);
10572
10573 AutoCaller autoCaller(this);
10574 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10575
10576 AutoMultiWriteLock2 alock(this->lockHandle(),
10577 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10578
10579 /* must be in a protective state because we release the lock below */
10580 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10581 || mData->mMachineState == MachineState_OnlineSnapshotting
10582 || mData->mMachineState == MachineState_LiveSnapshotting
10583 || mData->mMachineState == MachineState_RestoringSnapshot
10584 || mData->mMachineState == MachineState_DeletingSnapshot
10585 , E_FAIL);
10586
10587 HRESULT hrc = S_OK;
10588
10589 // use appropriate locked media map (online or offline)
10590 MediumLockListMap lockedMediaOffline;
10591 MediumLockListMap *lockedMediaMap;
10592 if (aOnline)
10593 lockedMediaMap = &mData->mSession.mLockedMedia;
10594 else
10595 lockedMediaMap = &lockedMediaOffline;
10596
10597 try
10598 {
10599 if (!aOnline)
10600 {
10601 /* lock all attached hard disks early to detect "in use"
10602 * situations before creating actual diffs */
10603 for (MediumAttachmentList::const_iterator
10604 it = mMediumAttachments->begin();
10605 it != mMediumAttachments->end();
10606 ++it)
10607 {
10608 MediumAttachment *pAtt = *it;
10609 if (pAtt->i_getType() == DeviceType_HardDisk)
10610 {
10611 Medium *pMedium = pAtt->i_getMedium();
10612 Assert(pMedium);
10613
10614 MediumLockList *pMediumLockList(new MediumLockList());
10615 alock.release();
10616 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10617 NULL /* pToLockWrite */,
10618 false /* fMediumLockWriteAll */,
10619 NULL,
10620 *pMediumLockList);
10621 alock.acquire();
10622 if (FAILED(hrc))
10623 {
10624 delete pMediumLockList;
10625 throw hrc;
10626 }
10627 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10628 if (FAILED(hrc))
10629 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
10630 }
10631 }
10632
10633 /* Now lock all media. If this fails, nothing is locked. */
10634 alock.release();
10635 hrc = lockedMediaMap->Lock();
10636 alock.acquire();
10637 if (FAILED(hrc))
10638 throw setError(hrc, tr("Locking of attached media failed"));
10639 }
10640
10641 /* remember the current list (note that we don't use backup() since
10642 * mMediumAttachments may be already backed up) */
10643 MediumAttachmentList atts = *mMediumAttachments.data();
10644
10645 /* start from scratch */
10646 mMediumAttachments->clear();
10647
10648 /* go through remembered attachments and create diffs for normal hard
10649 * disks and attach them */
10650 for (MediumAttachmentList::const_iterator
10651 it = atts.begin();
10652 it != atts.end();
10653 ++it)
10654 {
10655 MediumAttachment *pAtt = *it;
10656
10657 DeviceType_T devType = pAtt->i_getType();
10658 Medium *pMedium = pAtt->i_getMedium();
10659
10660 if ( devType != DeviceType_HardDisk
10661 || pMedium == NULL
10662 || pMedium->i_getType() != MediumType_Normal)
10663 {
10664 /* copy the attachment as is */
10665
10666 /** @todo the progress object created in SessionMachine::TakeSnaphot
10667 * only expects operations for hard disks. Later other
10668 * device types need to show up in the progress as well. */
10669 if (devType == DeviceType_HardDisk)
10670 {
10671 if (pMedium == NULL)
10672 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10673 aWeight); // weight
10674 else
10675 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10676 pMedium->i_getBase()->i_getName().c_str()).raw(),
10677 aWeight); // weight
10678 }
10679
10680 mMediumAttachments->push_back(pAtt);
10681 continue;
10682 }
10683
10684 /* need a diff */
10685 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10686 pMedium->i_getBase()->i_getName().c_str()).raw(),
10687 aWeight); // weight
10688
10689 Utf8Str strFullSnapshotFolder;
10690 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10691
10692 ComObjPtr<Medium> diff;
10693 diff.createObject();
10694 // store the diff in the same registry as the parent
10695 // (this cannot fail here because we can't create implicit diffs for
10696 // unregistered images)
10697 Guid uuidRegistryParent;
10698 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10699 Assert(fInRegistry); NOREF(fInRegistry);
10700 hrc = diff->init(mParent,
10701 pMedium->i_getPreferredDiffFormat(),
10702 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10703 uuidRegistryParent,
10704 DeviceType_HardDisk);
10705 if (FAILED(hrc)) throw hrc;
10706
10707 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10708 * the push_back? Looks like we're going to release medium with the
10709 * wrong kind of lock (general issue with if we fail anywhere at all)
10710 * and an orphaned VDI in the snapshots folder. */
10711
10712 /* update the appropriate lock list */
10713 MediumLockList *pMediumLockList;
10714 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
10715 AssertComRCThrowRC(hrc);
10716 if (aOnline)
10717 {
10718 alock.release();
10719 /* The currently attached medium will be read-only, change
10720 * the lock type to read. */
10721 hrc = pMediumLockList->Update(pMedium, false);
10722 alock.acquire();
10723 AssertComRCThrowRC(hrc);
10724 }
10725
10726 /* release the locks before the potentially lengthy operation */
10727 alock.release();
10728 hrc = pMedium->i_createDiffStorage(diff,
10729 pMedium->i_getPreferredDiffVariant(),
10730 pMediumLockList,
10731 NULL /* aProgress */,
10732 true /* aWait */,
10733 false /* aNotify */);
10734 alock.acquire();
10735 if (FAILED(hrc)) throw hrc;
10736
10737 /* actual lock list update is done in Machine::i_commitMedia */
10738
10739 hrc = diff->i_addBackReference(mData->mUuid);
10740 AssertComRCThrowRC(hrc);
10741
10742 /* add a new attachment */
10743 ComObjPtr<MediumAttachment> attachment;
10744 attachment.createObject();
10745 hrc = attachment->init(this,
10746 diff,
10747 pAtt->i_getControllerName(),
10748 pAtt->i_getPort(),
10749 pAtt->i_getDevice(),
10750 DeviceType_HardDisk,
10751 true /* aImplicit */,
10752 false /* aPassthrough */,
10753 false /* aTempEject */,
10754 pAtt->i_getNonRotational(),
10755 pAtt->i_getDiscard(),
10756 pAtt->i_getHotPluggable(),
10757 pAtt->i_getBandwidthGroup());
10758 if (FAILED(hrc)) throw hrc;
10759
10760 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10761 AssertComRCThrowRC(hrc);
10762 mMediumAttachments->push_back(attachment);
10763 }
10764 }
10765 catch (HRESULT hrcXcpt)
10766 {
10767 hrc = hrcXcpt;
10768 }
10769
10770 /* unlock all hard disks we locked when there is no VM */
10771 if (!aOnline)
10772 {
10773 ErrorInfoKeeper eik;
10774
10775 HRESULT hrc2 = lockedMediaMap->Clear();
10776 AssertComRC(hrc2);
10777 }
10778
10779 return hrc;
10780}
10781
10782/**
10783 * Deletes implicit differencing hard disks created either by
10784 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10785 * mMediumAttachments.
10786 *
10787 * Note that to delete hard disks created by #attachDevice() this method is
10788 * called from #i_rollbackMedia() when the changes are rolled back.
10789 *
10790 * @note Locks this object and the media tree for writing.
10791 */
10792HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10793{
10794 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10795
10796 AutoCaller autoCaller(this);
10797 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10798
10799 AutoMultiWriteLock2 alock(this->lockHandle(),
10800 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10801
10802 /* We absolutely must have backed up state. */
10803 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10804
10805 /* Check if there are any implicitly created diff images. */
10806 bool fImplicitDiffs = false;
10807 for (MediumAttachmentList::const_iterator
10808 it = mMediumAttachments->begin();
10809 it != mMediumAttachments->end();
10810 ++it)
10811 {
10812 const ComObjPtr<MediumAttachment> &pAtt = *it;
10813 if (pAtt->i_isImplicit())
10814 {
10815 fImplicitDiffs = true;
10816 break;
10817 }
10818 }
10819 /* If there is nothing to do, leave early. This saves lots of image locking
10820 * effort. It also avoids a MachineStateChanged event without real reason.
10821 * This is important e.g. when loading a VM config, because there should be
10822 * no events. Otherwise API clients can become thoroughly confused for
10823 * inaccessible VMs (the code for loading VM configs uses this method for
10824 * cleanup if the config makes no sense), as they take such events as an
10825 * indication that the VM is alive, and they would force the VM config to
10826 * be reread, leading to an endless loop. */
10827 if (!fImplicitDiffs)
10828 return S_OK;
10829
10830 HRESULT hrc = S_OK;
10831 MachineState_T oldState = mData->mMachineState;
10832
10833 /* will release the lock before the potentially lengthy operation,
10834 * so protect with the special state (unless already protected) */
10835 if ( oldState != MachineState_Snapshotting
10836 && oldState != MachineState_OnlineSnapshotting
10837 && oldState != MachineState_LiveSnapshotting
10838 && oldState != MachineState_RestoringSnapshot
10839 && oldState != MachineState_DeletingSnapshot
10840 && oldState != MachineState_DeletingSnapshotOnline
10841 && oldState != MachineState_DeletingSnapshotPaused
10842 )
10843 i_setMachineState(MachineState_SettingUp);
10844
10845 // use appropriate locked media map (online or offline)
10846 MediumLockListMap lockedMediaOffline;
10847 MediumLockListMap *lockedMediaMap;
10848 if (aOnline)
10849 lockedMediaMap = &mData->mSession.mLockedMedia;
10850 else
10851 lockedMediaMap = &lockedMediaOffline;
10852
10853 try
10854 {
10855 if (!aOnline)
10856 {
10857 /* lock all attached hard disks early to detect "in use"
10858 * situations before deleting actual diffs */
10859 for (MediumAttachmentList::const_iterator
10860 it = mMediumAttachments->begin();
10861 it != mMediumAttachments->end();
10862 ++it)
10863 {
10864 MediumAttachment *pAtt = *it;
10865 if (pAtt->i_getType() == DeviceType_HardDisk)
10866 {
10867 Medium *pMedium = pAtt->i_getMedium();
10868 Assert(pMedium);
10869
10870 MediumLockList *pMediumLockList(new MediumLockList());
10871 alock.release();
10872 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10873 NULL /* pToLockWrite */,
10874 false /* fMediumLockWriteAll */,
10875 NULL,
10876 *pMediumLockList);
10877 alock.acquire();
10878
10879 if (FAILED(hrc))
10880 {
10881 delete pMediumLockList;
10882 throw hrc;
10883 }
10884
10885 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10886 if (FAILED(hrc))
10887 throw hrc;
10888 }
10889 }
10890
10891 if (FAILED(hrc))
10892 throw hrc;
10893 } // end of offline
10894
10895 /* Lock lists are now up to date and include implicitly created media */
10896
10897 /* Go through remembered attachments and delete all implicitly created
10898 * diffs and fix up the attachment information */
10899 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10900 MediumAttachmentList implicitAtts;
10901 for (MediumAttachmentList::const_iterator
10902 it = mMediumAttachments->begin();
10903 it != mMediumAttachments->end();
10904 ++it)
10905 {
10906 ComObjPtr<MediumAttachment> pAtt = *it;
10907 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10908 if (pMedium.isNull())
10909 continue;
10910
10911 // Implicit attachments go on the list for deletion and back references are removed.
10912 if (pAtt->i_isImplicit())
10913 {
10914 /* Deassociate and mark for deletion */
10915 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10916 hrc = pMedium->i_removeBackReference(mData->mUuid);
10917 if (FAILED(hrc))
10918 throw hrc;
10919 implicitAtts.push_back(pAtt);
10920 continue;
10921 }
10922
10923 /* Was this medium attached before? */
10924 if (!i_findAttachment(oldAtts, pMedium))
10925 {
10926 /* no: de-associate */
10927 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10928 hrc = pMedium->i_removeBackReference(mData->mUuid);
10929 if (FAILED(hrc))
10930 throw hrc;
10931 continue;
10932 }
10933 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10934 }
10935
10936 /* If there are implicit attachments to delete, throw away the lock
10937 * map contents (which will unlock all media) since the medium
10938 * attachments will be rolled back. Below we need to completely
10939 * recreate the lock map anyway since it is infinitely complex to
10940 * do this incrementally (would need reconstructing each attachment
10941 * change, which would be extremely hairy). */
10942 if (implicitAtts.size() != 0)
10943 {
10944 ErrorInfoKeeper eik;
10945
10946 HRESULT hrc2 = lockedMediaMap->Clear();
10947 AssertComRC(hrc2);
10948 }
10949
10950 /* rollback hard disk changes */
10951 mMediumAttachments.rollback();
10952
10953 MultiResult mrc(S_OK);
10954
10955 // Delete unused implicit diffs.
10956 if (implicitAtts.size() != 0)
10957 {
10958 alock.release();
10959
10960 for (MediumAttachmentList::const_iterator
10961 it = implicitAtts.begin();
10962 it != implicitAtts.end();
10963 ++it)
10964 {
10965 // Remove medium associated with this attachment.
10966 ComObjPtr<MediumAttachment> pAtt = *it;
10967 Assert(pAtt);
10968 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10969 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10970 Assert(pMedium);
10971
10972 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10973 // continue on delete failure, just collect error messages
10974 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
10975 pMedium->i_getLocationFull().c_str() ));
10976 mrc = hrc;
10977 }
10978 // Clear the list of deleted implicit attachments now, while not
10979 // holding the lock, as it will ultimately trigger Medium::uninit()
10980 // calls which assume that the media tree lock isn't held.
10981 implicitAtts.clear();
10982
10983 alock.acquire();
10984
10985 /* if there is a VM recreate media lock map as mentioned above,
10986 * otherwise it is a waste of time and we leave things unlocked */
10987 if (aOnline)
10988 {
10989 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10990 /* must never be NULL, but better safe than sorry */
10991 if (!pMachine.isNull())
10992 {
10993 alock.release();
10994 hrc = mData->mSession.mMachine->i_lockMedia();
10995 alock.acquire();
10996 if (FAILED(hrc))
10997 throw hrc;
10998 }
10999 }
11000 }
11001 }
11002 catch (HRESULT hrcXcpt)
11003 {
11004 hrc = hrcXcpt;
11005 }
11006
11007 if (mData->mMachineState == MachineState_SettingUp)
11008 i_setMachineState(oldState);
11009
11010 /* unlock all hard disks we locked when there is no VM */
11011 if (!aOnline)
11012 {
11013 ErrorInfoKeeper eik;
11014
11015 HRESULT hrc2 = lockedMediaMap->Clear();
11016 AssertComRC(hrc2);
11017 }
11018
11019 return hrc;
11020}
11021
11022
11023/**
11024 * Looks through the given list of media attachments for one with the given parameters
11025 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11026 * can be searched as well if needed.
11027 *
11028 * @param ll
11029 * @param aControllerName
11030 * @param aControllerPort
11031 * @param aDevice
11032 * @return
11033 */
11034MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11035 const Utf8Str &aControllerName,
11036 LONG aControllerPort,
11037 LONG aDevice)
11038{
11039 for (MediumAttachmentList::const_iterator
11040 it = ll.begin();
11041 it != ll.end();
11042 ++it)
11043 {
11044 MediumAttachment *pAttach = *it;
11045 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11046 return pAttach;
11047 }
11048
11049 return NULL;
11050}
11051
11052/**
11053 * Looks through the given list of media attachments for one with the given parameters
11054 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11055 * can be searched as well if needed.
11056 *
11057 * @param ll
11058 * @param pMedium
11059 * @return
11060 */
11061MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11062 ComObjPtr<Medium> pMedium)
11063{
11064 for (MediumAttachmentList::const_iterator
11065 it = ll.begin();
11066 it != ll.end();
11067 ++it)
11068 {
11069 MediumAttachment *pAttach = *it;
11070 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11071 if (pMediumThis == pMedium)
11072 return pAttach;
11073 }
11074
11075 return NULL;
11076}
11077
11078/**
11079 * Looks through the given list of media attachments for one with the given parameters
11080 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11081 * can be searched as well if needed.
11082 *
11083 * @param ll
11084 * @param id
11085 * @return
11086 */
11087MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11088 Guid &id)
11089{
11090 for (MediumAttachmentList::const_iterator
11091 it = ll.begin();
11092 it != ll.end();
11093 ++it)
11094 {
11095 MediumAttachment *pAttach = *it;
11096 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11097 if (pMediumThis->i_getId() == id)
11098 return pAttach;
11099 }
11100
11101 return NULL;
11102}
11103
11104/**
11105 * Main implementation for Machine::DetachDevice. This also gets called
11106 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11107 *
11108 * @param pAttach Medium attachment to detach.
11109 * @param writeLock Machine write lock which the caller must have locked once.
11110 * This may be released temporarily in here.
11111 * @param pSnapshot If NULL, then the detachment is for the current machine.
11112 * Otherwise this is for a SnapshotMachine, and this must be
11113 * its snapshot.
11114 * @return
11115 */
11116HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11117 AutoWriteLock &writeLock,
11118 Snapshot *pSnapshot)
11119{
11120 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11121 DeviceType_T mediumType = pAttach->i_getType();
11122
11123 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11124
11125 if (pAttach->i_isImplicit())
11126 {
11127 /* attempt to implicitly delete the implicitly created diff */
11128
11129 /// @todo move the implicit flag from MediumAttachment to Medium
11130 /// and forbid any hard disk operation when it is implicit. Or maybe
11131 /// a special media state for it to make it even more simple.
11132
11133 Assert(mMediumAttachments.isBackedUp());
11134
11135 /* will release the lock before the potentially lengthy operation, so
11136 * protect with the special state */
11137 MachineState_T oldState = mData->mMachineState;
11138 i_setMachineState(MachineState_SettingUp);
11139
11140 writeLock.release();
11141
11142 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11143
11144 writeLock.acquire();
11145
11146 i_setMachineState(oldState);
11147
11148 if (FAILED(hrc)) return hrc;
11149 }
11150
11151 i_setModified(IsModified_Storage);
11152 mMediumAttachments.backup();
11153 mMediumAttachments->remove(pAttach);
11154
11155 if (!oldmedium.isNull())
11156 {
11157 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11158 if (pSnapshot)
11159 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11160 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11161 else if (mediumType != DeviceType_HardDisk)
11162 oldmedium->i_removeBackReference(mData->mUuid);
11163 }
11164
11165 return S_OK;
11166}
11167
11168/**
11169 * Goes thru all media of the given list and
11170 *
11171 * 1) calls i_detachDevice() on each of them for this machine and
11172 * 2) adds all Medium objects found in the process to the given list,
11173 * depending on cleanupMode.
11174 *
11175 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11176 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11177 * media to the list.
11178 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11179 * also removable media if they are located in the VM folder and referenced
11180 * only by this VM (media prepared by unattended installer).
11181 *
11182 * This gets called from Machine::Unregister, both for the actual Machine and
11183 * the SnapshotMachine objects that might be found in the snapshots.
11184 *
11185 * Requires caller and locking. The machine lock must be passed in because it
11186 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11187 *
11188 * @param writeLock Machine lock from top-level caller; this gets passed to
11189 * i_detachDevice.
11190 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11191 * object if called for a SnapshotMachine.
11192 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11193 * added to llMedia; if Full, then all media get added;
11194 * otherwise no media get added.
11195 * @param llMedia Caller's list to receive Medium objects which got detached so
11196 * caller can close() them, depending on cleanupMode.
11197 * @return
11198 */
11199HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11200 Snapshot *pSnapshot,
11201 CleanupMode_T cleanupMode,
11202 MediaList &llMedia)
11203{
11204 Assert(isWriteLockOnCurrentThread());
11205
11206 HRESULT hrc;
11207
11208 // make a temporary list because i_detachDevice invalidates iterators into
11209 // mMediumAttachments
11210 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11211
11212 for (MediumAttachmentList::iterator
11213 it = llAttachments2.begin();
11214 it != llAttachments2.end();
11215 ++it)
11216 {
11217 ComObjPtr<MediumAttachment> &pAttach = *it;
11218 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11219
11220 if (!pMedium.isNull())
11221 {
11222 AutoCaller mac(pMedium);
11223 if (FAILED(mac.hrc())) return mac.hrc();
11224 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11225 DeviceType_T devType = pMedium->i_getDeviceType();
11226 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11227 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11228 strMediumLocation.stripFilename();
11229 Utf8Str strMachineFolder = i_getSettingsFileFull();
11230 strMachineFolder.stripFilename();
11231 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11232 && devType == DeviceType_HardDisk)
11233 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11234 && ( devType == DeviceType_HardDisk
11235 || ( cBackRefs <= 1
11236 && strMediumLocation == strMachineFolder
11237 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11238 || (cleanupMode == CleanupMode_Full)
11239 )
11240 {
11241 llMedia.push_back(pMedium);
11242 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11243 /* Not allowed to keep this lock as below we need the parent
11244 * medium lock, and the lock order is parent to child. */
11245 lock.release();
11246 /*
11247 * Search for media which are not attached to any machine, but
11248 * in the chain to an attached disk. Media are only consided
11249 * if they are:
11250 * - have only one child
11251 * - no references to any machines
11252 * - are of normal medium type
11253 */
11254 while (!pParent.isNull())
11255 {
11256 AutoCaller mac1(pParent);
11257 if (FAILED(mac1.hrc())) return mac1.hrc();
11258 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11259 if (pParent->i_getChildren().size() == 1)
11260 {
11261 if ( pParent->i_getMachineBackRefCount() == 0
11262 && pParent->i_getType() == MediumType_Normal
11263 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11264 llMedia.push_back(pParent);
11265 }
11266 else
11267 break;
11268 pParent = pParent->i_getParent();
11269 }
11270 }
11271 }
11272
11273 // real machine: then we need to use the proper method
11274 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11275
11276 if (FAILED(hrc))
11277 return hrc;
11278 }
11279
11280 return S_OK;
11281}
11282
11283/**
11284 * Perform deferred hard disk detachments.
11285 *
11286 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11287 * changed (not backed up).
11288 *
11289 * If @a aOnline is @c true then this method will also unlock the old hard
11290 * disks for which the new implicit diffs were created and will lock these new
11291 * diffs for writing.
11292 *
11293 * @param aOnline Whether the VM was online prior to this operation.
11294 *
11295 * @note Locks this object for writing!
11296 */
11297void Machine::i_commitMedia(bool aOnline /*= false*/)
11298{
11299 AutoCaller autoCaller(this);
11300 AssertComRCReturnVoid(autoCaller.hrc());
11301
11302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11303
11304 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11305
11306 HRESULT hrc = S_OK;
11307
11308 /* no attach/detach operations -- nothing to do */
11309 if (!mMediumAttachments.isBackedUp())
11310 return;
11311
11312 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11313 bool fMediaNeedsLocking = false;
11314
11315 /* enumerate new attachments */
11316 for (MediumAttachmentList::const_iterator
11317 it = mMediumAttachments->begin();
11318 it != mMediumAttachments->end();
11319 ++it)
11320 {
11321 MediumAttachment *pAttach = *it;
11322
11323 pAttach->i_commit();
11324
11325 Medium *pMedium = pAttach->i_getMedium();
11326 bool fImplicit = pAttach->i_isImplicit();
11327
11328 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11329 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11330 fImplicit));
11331
11332 /** @todo convert all this Machine-based voodoo to MediumAttachment
11333 * based commit logic. */
11334 if (fImplicit)
11335 {
11336 /* convert implicit attachment to normal */
11337 pAttach->i_setImplicit(false);
11338
11339 if ( aOnline
11340 && pMedium
11341 && pAttach->i_getType() == DeviceType_HardDisk
11342 )
11343 {
11344 /* update the appropriate lock list */
11345 MediumLockList *pMediumLockList;
11346 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11347 AssertComRC(hrc);
11348 if (pMediumLockList)
11349 {
11350 /* unlock if there's a need to change the locking */
11351 if (!fMediaNeedsLocking)
11352 {
11353 Assert(mData->mSession.mLockedMedia.IsLocked());
11354 hrc = mData->mSession.mLockedMedia.Unlock();
11355 AssertComRC(hrc);
11356 fMediaNeedsLocking = true;
11357 }
11358 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
11359 AssertComRC(hrc);
11360 hrc = pMediumLockList->Append(pMedium, true);
11361 AssertComRC(hrc);
11362 }
11363 }
11364
11365 continue;
11366 }
11367
11368 if (pMedium)
11369 {
11370 /* was this medium attached before? */
11371 for (MediumAttachmentList::iterator
11372 oldIt = oldAtts.begin();
11373 oldIt != oldAtts.end();
11374 ++oldIt)
11375 {
11376 MediumAttachment *pOldAttach = *oldIt;
11377 if (pOldAttach->i_getMedium() == pMedium)
11378 {
11379 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11380
11381 /* yes: remove from old to avoid de-association */
11382 oldAtts.erase(oldIt);
11383 break;
11384 }
11385 }
11386 }
11387 }
11388
11389 /* enumerate remaining old attachments and de-associate from the
11390 * current machine state */
11391 for (MediumAttachmentList::const_iterator
11392 it = oldAtts.begin();
11393 it != oldAtts.end();
11394 ++it)
11395 {
11396 MediumAttachment *pAttach = *it;
11397 Medium *pMedium = pAttach->i_getMedium();
11398
11399 /* Detach only hard disks, since DVD/floppy media is detached
11400 * instantly in MountMedium. */
11401 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11402 {
11403 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11404
11405 /* now de-associate from the current machine state */
11406 hrc = pMedium->i_removeBackReference(mData->mUuid);
11407 AssertComRC(hrc);
11408
11409 if (aOnline)
11410 {
11411 /* unlock since medium is not used anymore */
11412 MediumLockList *pMediumLockList;
11413 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11414 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
11415 {
11416 /* this happens for online snapshots, there the attachment
11417 * is changing, but only to a diff image created under
11418 * the old one, so there is no separate lock list */
11419 Assert(!pMediumLockList);
11420 }
11421 else
11422 {
11423 AssertComRC(hrc);
11424 if (pMediumLockList)
11425 {
11426 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
11427 AssertComRC(hrc);
11428 }
11429 }
11430 }
11431 }
11432 }
11433
11434 /* take media locks again so that the locking state is consistent */
11435 if (fMediaNeedsLocking)
11436 {
11437 Assert(aOnline);
11438 hrc = mData->mSession.mLockedMedia.Lock();
11439 AssertComRC(hrc);
11440 }
11441
11442 /* commit the hard disk changes */
11443 mMediumAttachments.commit();
11444
11445 if (i_isSessionMachine())
11446 {
11447 /*
11448 * Update the parent machine to point to the new owner.
11449 * This is necessary because the stored parent will point to the
11450 * session machine otherwise and cause crashes or errors later
11451 * when the session machine gets invalid.
11452 */
11453 /** @todo Change the MediumAttachment class to behave like any other
11454 * class in this regard by creating peer MediumAttachment
11455 * objects for session machines and share the data with the peer
11456 * machine.
11457 */
11458 for (MediumAttachmentList::const_iterator
11459 it = mMediumAttachments->begin();
11460 it != mMediumAttachments->end();
11461 ++it)
11462 (*it)->i_updateParentMachine(mPeer);
11463
11464 /* attach new data to the primary machine and reshare it */
11465 mPeer->mMediumAttachments.attach(mMediumAttachments);
11466 }
11467
11468 return;
11469}
11470
11471/**
11472 * Perform deferred deletion of implicitly created diffs.
11473 *
11474 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11475 * changed (not backed up).
11476 *
11477 * @note Locks this object for writing!
11478 */
11479void Machine::i_rollbackMedia()
11480{
11481 AutoCaller autoCaller(this);
11482 AssertComRCReturnVoid(autoCaller.hrc());
11483
11484 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11485 LogFlowThisFunc(("Entering rollbackMedia\n"));
11486
11487 HRESULT hrc = S_OK;
11488
11489 /* no attach/detach operations -- nothing to do */
11490 if (!mMediumAttachments.isBackedUp())
11491 return;
11492
11493 /* enumerate new attachments */
11494 for (MediumAttachmentList::const_iterator
11495 it = mMediumAttachments->begin();
11496 it != mMediumAttachments->end();
11497 ++it)
11498 {
11499 MediumAttachment *pAttach = *it;
11500 /* Fix up the backrefs for DVD/floppy media. */
11501 if (pAttach->i_getType() != DeviceType_HardDisk)
11502 {
11503 Medium *pMedium = pAttach->i_getMedium();
11504 if (pMedium)
11505 {
11506 hrc = pMedium->i_removeBackReference(mData->mUuid);
11507 AssertComRC(hrc);
11508 }
11509 }
11510
11511 (*it)->i_rollback();
11512
11513 pAttach = *it;
11514 /* Fix up the backrefs for DVD/floppy media. */
11515 if (pAttach->i_getType() != DeviceType_HardDisk)
11516 {
11517 Medium *pMedium = pAttach->i_getMedium();
11518 if (pMedium)
11519 {
11520 hrc = pMedium->i_addBackReference(mData->mUuid);
11521 AssertComRC(hrc);
11522 }
11523 }
11524 }
11525
11526 /** @todo convert all this Machine-based voodoo to MediumAttachment
11527 * based rollback logic. */
11528 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11529
11530 return;
11531}
11532
11533/**
11534 * Returns true if the settings file is located in the directory named exactly
11535 * as the machine; this means, among other things, that the machine directory
11536 * should be auto-renamed.
11537 *
11538 * @param aSettingsDir if not NULL, the full machine settings file directory
11539 * name will be assigned there.
11540 *
11541 * @note Doesn't lock anything.
11542 * @note Not thread safe (must be called from this object's lock).
11543 */
11544bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11545{
11546 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11547 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11548 if (aSettingsDir)
11549 *aSettingsDir = strMachineDirName;
11550 strMachineDirName.stripPath(); // vmname
11551 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11552 strConfigFileOnly.stripPath() // vmname.vbox
11553 .stripSuffix(); // vmname
11554 /** @todo hack, make somehow use of ComposeMachineFilename */
11555 if (mUserData->s.fDirectoryIncludesUUID)
11556 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11557
11558 AssertReturn(!strMachineDirName.isEmpty(), false);
11559 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11560
11561 return strMachineDirName == strConfigFileOnly;
11562}
11563
11564/**
11565 * Discards all changes to machine settings.
11566 *
11567 * @param aNotify Whether to notify the direct session about changes or not.
11568 *
11569 * @note Locks objects for writing!
11570 */
11571void Machine::i_rollback(bool aNotify)
11572{
11573 AutoCaller autoCaller(this);
11574 AssertComRCReturn(autoCaller.hrc(), (void)0);
11575
11576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11577
11578 if (!mStorageControllers.isNull())
11579 {
11580 if (mStorageControllers.isBackedUp())
11581 {
11582 /* unitialize all new devices (absent in the backed up list). */
11583 StorageControllerList *backedList = mStorageControllers.backedUpData();
11584 for (StorageControllerList::const_iterator
11585 it = mStorageControllers->begin();
11586 it != mStorageControllers->end();
11587 ++it)
11588 {
11589 if ( std::find(backedList->begin(), backedList->end(), *it)
11590 == backedList->end()
11591 )
11592 {
11593 (*it)->uninit();
11594 }
11595 }
11596
11597 /* restore the list */
11598 mStorageControllers.rollback();
11599 }
11600
11601 /* rollback any changes to devices after restoring the list */
11602 if (mData->flModifications & IsModified_Storage)
11603 {
11604 for (StorageControllerList::const_iterator
11605 it = mStorageControllers->begin();
11606 it != mStorageControllers->end();
11607 ++it)
11608 {
11609 (*it)->i_rollback();
11610 }
11611 }
11612 }
11613
11614 if (!mUSBControllers.isNull())
11615 {
11616 if (mUSBControllers.isBackedUp())
11617 {
11618 /* unitialize all new devices (absent in the backed up list). */
11619 USBControllerList *backedList = mUSBControllers.backedUpData();
11620 for (USBControllerList::const_iterator
11621 it = mUSBControllers->begin();
11622 it != mUSBControllers->end();
11623 ++it)
11624 {
11625 if ( std::find(backedList->begin(), backedList->end(), *it)
11626 == backedList->end()
11627 )
11628 {
11629 (*it)->uninit();
11630 }
11631 }
11632
11633 /* restore the list */
11634 mUSBControllers.rollback();
11635 }
11636
11637 /* rollback any changes to devices after restoring the list */
11638 if (mData->flModifications & IsModified_USB)
11639 {
11640 for (USBControllerList::const_iterator
11641 it = mUSBControllers->begin();
11642 it != mUSBControllers->end();
11643 ++it)
11644 {
11645 (*it)->i_rollback();
11646 }
11647 }
11648 }
11649
11650 mUserData.rollback();
11651
11652 mHWData.rollback();
11653
11654 if (mData->flModifications & IsModified_Storage)
11655 i_rollbackMedia();
11656
11657 if (mPlatform)
11658 {
11659 mPlatform->i_rollback();
11660 i_platformPropertiesUpdate();
11661 }
11662
11663 if (mFirmwareSettings)
11664 mFirmwareSettings->i_rollback();
11665
11666 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11667 mRecordingSettings->i_rollback();
11668
11669 if (mTrustedPlatformModule)
11670 mTrustedPlatformModule->i_rollback();
11671
11672 if (mNvramStore)
11673 mNvramStore->i_rollback();
11674
11675 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11676 mGraphicsAdapter->i_rollback();
11677
11678 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11679 mVRDEServer->i_rollback();
11680
11681 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
11682 mAudioSettings->i_rollback();
11683
11684 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11685 mUSBDeviceFilters->i_rollback();
11686
11687 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11688 mBandwidthControl->i_rollback();
11689
11690 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
11691 mGuestDebugControl->i_rollback();
11692
11693 if (mPlatform && (mData->flModifications & IsModified_Platform))
11694 {
11695 ChipsetType_T enmChipset;
11696 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11697 ComAssertComRC(hrc);
11698
11699 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipset));
11700 }
11701
11702 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11703 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11704 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11705
11706 if (mData->flModifications & IsModified_NetworkAdapters)
11707 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11708 if ( mNetworkAdapters[slot]
11709 && mNetworkAdapters[slot]->i_isModified())
11710 {
11711 mNetworkAdapters[slot]->i_rollback();
11712 networkAdapters[slot] = mNetworkAdapters[slot];
11713 }
11714
11715 if (mData->flModifications & IsModified_SerialPorts)
11716 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11717 if ( mSerialPorts[slot]
11718 && mSerialPorts[slot]->i_isModified())
11719 {
11720 mSerialPorts[slot]->i_rollback();
11721 serialPorts[slot] = mSerialPorts[slot];
11722 }
11723
11724 if (mData->flModifications & IsModified_ParallelPorts)
11725 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11726 if ( mParallelPorts[slot]
11727 && mParallelPorts[slot]->i_isModified())
11728 {
11729 mParallelPorts[slot]->i_rollback();
11730 parallelPorts[slot] = mParallelPorts[slot];
11731 }
11732
11733 if (aNotify)
11734 {
11735 /* inform the direct session about changes */
11736
11737 ComObjPtr<Machine> that = this;
11738 uint32_t flModifications = mData->flModifications;
11739 alock.release();
11740
11741 if (flModifications & IsModified_SharedFolders)
11742 that->i_onSharedFolderChange();
11743
11744 if (flModifications & IsModified_VRDEServer)
11745 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11746 if (flModifications & IsModified_USB)
11747 that->i_onUSBControllerChange();
11748
11749 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11750 if (networkAdapters[slot])
11751 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11752 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11753 if (serialPorts[slot])
11754 that->i_onSerialPortChange(serialPorts[slot]);
11755 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11756 if (parallelPorts[slot])
11757 that->i_onParallelPortChange(parallelPorts[slot]);
11758
11759 if (flModifications & IsModified_Storage)
11760 {
11761 for (StorageControllerList::const_iterator
11762 it = mStorageControllers->begin();
11763 it != mStorageControllers->end();
11764 ++it)
11765 {
11766 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11767 }
11768 }
11769
11770 if (flModifications & IsModified_GuestDebugControl)
11771 that->i_onGuestDebugControlChange(mGuestDebugControl);
11772
11773#if 0
11774 if (flModifications & IsModified_BandwidthControl)
11775 that->onBandwidthControlChange();
11776#endif
11777 }
11778}
11779
11780/**
11781 * Commits all the changes to machine settings.
11782 *
11783 * Note that this operation is supposed to never fail.
11784 *
11785 * @note Locks this object and children for writing.
11786 */
11787void Machine::i_commit()
11788{
11789 AutoCaller autoCaller(this);
11790 AssertComRCReturnVoid(autoCaller.hrc());
11791
11792 AutoCaller peerCaller(mPeer);
11793 AssertComRCReturnVoid(peerCaller.hrc());
11794
11795 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11796
11797 /*
11798 * use safe commit to ensure Snapshot machines (that share mUserData)
11799 * will still refer to a valid memory location
11800 */
11801 mUserData.commitCopy();
11802
11803 mHWData.commit();
11804
11805 if (mMediumAttachments.isBackedUp())
11806 i_commitMedia(Global::IsOnline(mData->mMachineState));
11807
11808 mPlatform->i_commit();
11809 mFirmwareSettings->i_commit();
11810 mRecordingSettings->i_commit();
11811 mTrustedPlatformModule->i_commit();
11812 mNvramStore->i_commit();
11813 mGraphicsAdapter->i_commit();
11814 mVRDEServer->i_commit();
11815 mAudioSettings->i_commit();
11816 mUSBDeviceFilters->i_commit();
11817 mBandwidthControl->i_commit();
11818 mGuestDebugControl->i_commit();
11819
11820 /* Since mNetworkAdapters is a list which might have been changed (resized)
11821 * without using the Backupable<> template we need to handle the copying
11822 * of the list entries manually, including the creation of peers for the
11823 * new objects. */
11824 ChipsetType_T enmChipset;
11825 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11826 ComAssertComRC(hrc);
11827
11828 bool commitNetworkAdapters = false;
11829 size_t const newSize = PlatformProperties::s_getMaxNetworkAdapters(enmChipset);
11830 if (mPeer)
11831 {
11832 size_t const oldSize = mNetworkAdapters.size();
11833 size_t const oldPeerSize = mPeer->mNetworkAdapters.size();
11834
11835 /* commit everything, even the ones which will go away */
11836 for (size_t slot = 0; slot < oldSize; slot++)
11837 mNetworkAdapters[slot]->i_commit();
11838 /* copy over the new entries, creating a peer and uninit the original */
11839 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, oldPeerSize));
11840 /* make sure to have enough room for iterating over the (newly added) slots down below */
11841 if (newSize > oldSize)
11842 {
11843 mNetworkAdapters.resize(newSize);
11844
11845 com::Utf8Str osTypeId;
11846 ComObjPtr<GuestOSType> osType = NULL;
11847 hrc = getOSTypeId(osTypeId);
11848 if (SUCCEEDED(hrc))
11849 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
11850
11851 for (size_t slot = oldSize; slot < newSize; slot++)
11852 {
11853 mNetworkAdapters[slot].createObject();
11854 mNetworkAdapters[slot]->init(this, (ULONG)slot);
11855 mNetworkAdapters[slot]->i_applyDefaults(SUCCEEDED(hrc) ? osType : NULL);
11856 }
11857 }
11858 for (size_t slot = 0; slot < newSize; slot++)
11859 {
11860 /* look if this adapter has a peer device */
11861 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11862 if (!peer)
11863 {
11864 /* no peer means the adapter is a newly created one;
11865 * create a peer owning data this data share it with */
11866 peer.createObject();
11867 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11868 }
11869 mPeer->mNetworkAdapters[slot] = peer;
11870 }
11871 /* uninit any no longer needed network adapters */
11872 for (size_t slot = newSize; slot < oldSize; ++slot)
11873 mNetworkAdapters[slot]->uninit();
11874 for (size_t slot = newSize; slot < oldPeerSize; ++slot)
11875 {
11876 if (mPeer->mNetworkAdapters[slot])
11877 mPeer->mNetworkAdapters[slot]->uninit();
11878 }
11879 /* Keep the original network adapter count until this point, so that
11880 * discarding a chipset type change will not lose settings. */
11881 mNetworkAdapters.resize(newSize);
11882 mPeer->mNetworkAdapters.resize(newSize);
11883 }
11884 else
11885 {
11886 /* we have no peer (our parent is the newly created machine);
11887 * just commit changes to the network adapters */
11888 commitNetworkAdapters = true;
11889 }
11890 if (commitNetworkAdapters)
11891 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11892 mNetworkAdapters[slot]->i_commit();
11893
11894 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11895 mSerialPorts[slot]->i_commit();
11896 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11897 mParallelPorts[slot]->i_commit();
11898
11899 bool commitStorageControllers = false;
11900
11901 if (mStorageControllers.isBackedUp())
11902 {
11903 mStorageControllers.commit();
11904
11905 if (mPeer)
11906 {
11907 /* Commit all changes to new controllers (this will reshare data with
11908 * peers for those who have peers) */
11909 StorageControllerList *newList = new StorageControllerList();
11910 for (StorageControllerList::const_iterator
11911 it = mStorageControllers->begin();
11912 it != mStorageControllers->end();
11913 ++it)
11914 {
11915 (*it)->i_commit();
11916
11917 /* look if this controller has a peer device */
11918 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11919 if (!peer)
11920 {
11921 /* no peer means the device is a newly created one;
11922 * create a peer owning data this device share it with */
11923 peer.createObject();
11924 peer->init(mPeer, *it, true /* aReshare */);
11925 }
11926 else
11927 {
11928 /* remove peer from the old list */
11929 mPeer->mStorageControllers->remove(peer);
11930 }
11931 /* and add it to the new list */
11932 newList->push_back(peer);
11933 }
11934
11935 /* uninit old peer's controllers that are left */
11936 for (StorageControllerList::const_iterator
11937 it = mPeer->mStorageControllers->begin();
11938 it != mPeer->mStorageControllers->end();
11939 ++it)
11940 {
11941 (*it)->uninit();
11942 }
11943
11944 /* attach new list of controllers to our peer */
11945 mPeer->mStorageControllers.attach(newList);
11946 }
11947 else
11948 {
11949 /* we have no peer (our parent is the newly created machine);
11950 * just commit changes to devices */
11951 commitStorageControllers = true;
11952 }
11953 }
11954 else
11955 {
11956 /* the list of controllers itself is not changed,
11957 * just commit changes to controllers themselves */
11958 commitStorageControllers = true;
11959 }
11960
11961 if (commitStorageControllers)
11962 {
11963 for (StorageControllerList::const_iterator
11964 it = mStorageControllers->begin();
11965 it != mStorageControllers->end();
11966 ++it)
11967 {
11968 (*it)->i_commit();
11969 }
11970 }
11971
11972 bool commitUSBControllers = false;
11973
11974 if (mUSBControllers.isBackedUp())
11975 {
11976 mUSBControllers.commit();
11977
11978 if (mPeer)
11979 {
11980 /* Commit all changes to new controllers (this will reshare data with
11981 * peers for those who have peers) */
11982 USBControllerList *newList = new USBControllerList();
11983 for (USBControllerList::const_iterator
11984 it = mUSBControllers->begin();
11985 it != mUSBControllers->end();
11986 ++it)
11987 {
11988 (*it)->i_commit();
11989
11990 /* look if this controller has a peer device */
11991 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11992 if (!peer)
11993 {
11994 /* no peer means the device is a newly created one;
11995 * create a peer owning data this device share it with */
11996 peer.createObject();
11997 peer->init(mPeer, *it, true /* aReshare */);
11998 }
11999 else
12000 {
12001 /* remove peer from the old list */
12002 mPeer->mUSBControllers->remove(peer);
12003 }
12004 /* and add it to the new list */
12005 newList->push_back(peer);
12006 }
12007
12008 /* uninit old peer's controllers that are left */
12009 for (USBControllerList::const_iterator
12010 it = mPeer->mUSBControllers->begin();
12011 it != mPeer->mUSBControllers->end();
12012 ++it)
12013 {
12014 (*it)->uninit();
12015 }
12016
12017 /* attach new list of controllers to our peer */
12018 mPeer->mUSBControllers.attach(newList);
12019 }
12020 else
12021 {
12022 /* we have no peer (our parent is the newly created machine);
12023 * just commit changes to devices */
12024 commitUSBControllers = true;
12025 }
12026 }
12027 else
12028 {
12029 /* the list of controllers itself is not changed,
12030 * just commit changes to controllers themselves */
12031 commitUSBControllers = true;
12032 }
12033
12034 if (commitUSBControllers)
12035 {
12036 for (USBControllerList::const_iterator
12037 it = mUSBControllers->begin();
12038 it != mUSBControllers->end();
12039 ++it)
12040 {
12041 (*it)->i_commit();
12042 }
12043 }
12044
12045 if (i_isSessionMachine())
12046 {
12047 /* attach new data to the primary machine and reshare it */
12048 mPeer->mUserData.attach(mUserData);
12049 mPeer->mHWData.attach(mHWData);
12050 /* mmMediumAttachments is reshared by fixupMedia */
12051 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12052 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12053 }
12054}
12055
12056/**
12057 * Copies all the hardware data from the given machine.
12058 *
12059 * Currently, only called when the VM is being restored from a snapshot. In
12060 * particular, this implies that the VM is not running during this method's
12061 * call.
12062 *
12063 * @note This method must be called from under this object's lock.
12064 *
12065 * @note This method doesn't call #i_commit(), so all data remains backed up and
12066 * unsaved.
12067 */
12068void Machine::i_copyFrom(Machine *aThat)
12069{
12070 AssertReturnVoid(!i_isSnapshotMachine());
12071 AssertReturnVoid(aThat->i_isSnapshotMachine());
12072
12073 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12074
12075 mHWData.assignCopy(aThat->mHWData);
12076
12077 // create copies of all shared folders (mHWData after attaching a copy
12078 // contains just references to original objects)
12079 for (HWData::SharedFolderList::iterator
12080 it = mHWData->mSharedFolders.begin();
12081 it != mHWData->mSharedFolders.end();
12082 ++it)
12083 {
12084 ComObjPtr<SharedFolder> folder;
12085 folder.createObject();
12086 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12087 AssertComRC(hrc);
12088 *it = folder;
12089 }
12090
12091 mPlatform->i_copyFrom(aThat->mPlatform);
12092 i_platformPropertiesUpdate();
12093 mFirmwareSettings->i_copyFrom(aThat->mFirmwareSettings);
12094 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12095 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12096 mNvramStore->i_copyFrom(aThat->mNvramStore);
12097 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12098 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12099 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12100 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12101 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12102 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12103
12104 /* create private copies of all controllers */
12105 mStorageControllers.backup();
12106 mStorageControllers->clear();
12107 for (StorageControllerList::const_iterator
12108 it = aThat->mStorageControllers->begin();
12109 it != aThat->mStorageControllers->end();
12110 ++it)
12111 {
12112 ComObjPtr<StorageController> ctrl;
12113 ctrl.createObject();
12114 ctrl->initCopy(this, *it);
12115 mStorageControllers->push_back(ctrl);
12116 }
12117
12118 /* create private copies of all USB controllers */
12119 mUSBControllers.backup();
12120 mUSBControllers->clear();
12121 for (USBControllerList::const_iterator
12122 it = aThat->mUSBControllers->begin();
12123 it != aThat->mUSBControllers->end();
12124 ++it)
12125 {
12126 ComObjPtr<USBController> ctrl;
12127 ctrl.createObject();
12128 ctrl->initCopy(this, *it);
12129 mUSBControllers->push_back(ctrl);
12130 }
12131
12132 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12133 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12134 {
12135 if (mNetworkAdapters[slot].isNotNull())
12136 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12137 else
12138 {
12139 unconst(mNetworkAdapters[slot]).createObject();
12140 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12141 }
12142 }
12143 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12144 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12145 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12146 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12147}
12148
12149/**
12150 * Returns whether the given storage controller is hotplug capable.
12151 *
12152 * @returns true if the controller supports hotplugging
12153 * false otherwise.
12154 * @param enmCtrlType The controller type to check for.
12155 */
12156bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12157{
12158 BOOL aHotplugCapable = FALSE;
12159 HRESULT hrc = mPlatformProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12160 AssertComRC(hrc);
12161
12162 return RT_BOOL(aHotplugCapable);
12163}
12164
12165#ifdef VBOX_WITH_RESOURCE_USAGE_API
12166
12167void Machine::i_getDiskList(MediaList &list)
12168{
12169 for (MediumAttachmentList::const_iterator
12170 it = mMediumAttachments->begin();
12171 it != mMediumAttachments->end();
12172 ++it)
12173 {
12174 MediumAttachment *pAttach = *it;
12175 /* just in case */
12176 AssertContinue(pAttach);
12177
12178 AutoCaller localAutoCallerA(pAttach);
12179 if (FAILED(localAutoCallerA.hrc())) continue;
12180
12181 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12182
12183 if (pAttach->i_getType() == DeviceType_HardDisk)
12184 list.push_back(pAttach->i_getMedium());
12185 }
12186}
12187
12188void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12189{
12190 AssertReturnVoid(isWriteLockOnCurrentThread());
12191 AssertPtrReturnVoid(aCollector);
12192
12193 pm::CollectorHAL *hal = aCollector->getHAL();
12194 /* Create sub metrics */
12195 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12196 "Percentage of processor time spent in user mode by the VM process.");
12197 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12198 "Percentage of processor time spent in kernel mode by the VM process.");
12199 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12200 "Size of resident portion of VM process in memory.");
12201 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12202 "Actual size of all VM disks combined.");
12203 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12204 "Network receive rate.");
12205 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12206 "Network transmit rate.");
12207 /* Create and register base metrics */
12208 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12209 cpuLoadUser, cpuLoadKernel);
12210 aCollector->registerBaseMetric(cpuLoad);
12211 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12212 ramUsageUsed);
12213 aCollector->registerBaseMetric(ramUsage);
12214 MediaList disks;
12215 i_getDiskList(disks);
12216 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12217 diskUsageUsed);
12218 aCollector->registerBaseMetric(diskUsage);
12219
12220 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12221 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12222 new pm::AggregateAvg()));
12223 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12224 new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12226 new pm::AggregateMax()));
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12228 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12229 new pm::AggregateAvg()));
12230 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12231 new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12233 new pm::AggregateMax()));
12234
12235 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12236 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12237 new pm::AggregateAvg()));
12238 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12239 new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12241 new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12244 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12245 new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12247 new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12249 new pm::AggregateMax()));
12250
12251
12252 /* Guest metrics collector */
12253 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12254 aCollector->registerGuest(mCollectorGuest);
12255 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12256
12257 /* Create sub metrics */
12258 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12259 "Percentage of processor time spent in user mode as seen by the guest.");
12260 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12261 "Percentage of processor time spent in kernel mode as seen by the guest.");
12262 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12263 "Percentage of processor time spent idling as seen by the guest.");
12264
12265 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12266 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12267 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12268 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12269 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12270 pm::SubMetric *guestMemCache = new pm::SubMetric(
12271 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12272
12273 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12274 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12275
12276 /* Create and register base metrics */
12277 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12278 machineNetRx, machineNetTx);
12279 aCollector->registerBaseMetric(machineNetRate);
12280
12281 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12282 guestLoadUser, guestLoadKernel, guestLoadIdle);
12283 aCollector->registerBaseMetric(guestCpuLoad);
12284
12285 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12286 guestMemTotal, guestMemFree,
12287 guestMemBalloon, guestMemShared,
12288 guestMemCache, guestPagedTotal);
12289 aCollector->registerBaseMetric(guestCpuMem);
12290
12291 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12292 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12293 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12294 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12295
12296 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12297 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12298 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12299 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12300
12301 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12302 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12304 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12305
12306 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12307 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12309 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12310
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12314 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12315
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12320
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12325
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12330
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12334 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12335
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12339 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12340
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12344 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12345}
12346
12347void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12348{
12349 AssertReturnVoid(isWriteLockOnCurrentThread());
12350
12351 if (aCollector)
12352 {
12353 aCollector->unregisterMetricsFor(aMachine);
12354 aCollector->unregisterBaseMetricsFor(aMachine);
12355 }
12356}
12357
12358#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12359
12360/**
12361 * Updates the machine's platform properties based on the current platform architecture.
12362 *
12363 * @note Called internally when committing, rolling back or loading settings.
12364 */
12365void Machine::i_platformPropertiesUpdate()
12366{
12367 if (mPlatform)
12368 {
12369 /* Update architecture for platform properties. */
12370 PlatformArchitecture_T platformArchitecture;
12371 HRESULT hrc = mPlatform->getArchitecture(&platformArchitecture);
12372 ComAssertComRC(hrc);
12373 hrc = mPlatformProperties->i_setArchitecture(platformArchitecture);
12374 ComAssertComRC(hrc);
12375 }
12376}
12377
12378
12379////////////////////////////////////////////////////////////////////////////////
12380
12381DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12382
12383HRESULT SessionMachine::FinalConstruct()
12384{
12385 LogFlowThisFunc(("\n"));
12386
12387 mClientToken = NULL;
12388
12389 return BaseFinalConstruct();
12390}
12391
12392void SessionMachine::FinalRelease()
12393{
12394 LogFlowThisFunc(("\n"));
12395
12396 Assert(!mClientToken);
12397 /* paranoia, should not hang around any more */
12398 if (mClientToken)
12399 {
12400 delete mClientToken;
12401 mClientToken = NULL;
12402 }
12403
12404 uninit(Uninit::Unexpected);
12405
12406 BaseFinalRelease();
12407}
12408
12409/**
12410 * @note Must be called only by Machine::LockMachine() from its own write lock.
12411 */
12412HRESULT SessionMachine::init(Machine *aMachine)
12413{
12414 LogFlowThisFuncEnter();
12415 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12416
12417 AssertReturn(aMachine, E_INVALIDARG);
12418
12419 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12420
12421 /* Enclose the state transition NotReady->InInit->Ready */
12422 AutoInitSpan autoInitSpan(this);
12423 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12424
12425 HRESULT hrc = S_OK;
12426
12427 RT_ZERO(mAuthLibCtx);
12428
12429 /* create the machine client token */
12430 try
12431 {
12432 mClientToken = new ClientToken(aMachine, this);
12433 if (!mClientToken->isReady())
12434 {
12435 delete mClientToken;
12436 mClientToken = NULL;
12437 hrc = E_FAIL;
12438 }
12439 }
12440 catch (std::bad_alloc &)
12441 {
12442 hrc = E_OUTOFMEMORY;
12443 }
12444 if (FAILED(hrc))
12445 return hrc;
12446
12447 /* memorize the peer Machine */
12448 unconst(mPeer) = aMachine;
12449 /* share the parent pointer */
12450 unconst(mParent) = aMachine->mParent;
12451
12452 /* take the pointers to data to share */
12453 mData.share(aMachine->mData);
12454 mSSData.share(aMachine->mSSData);
12455
12456 mUserData.share(aMachine->mUserData);
12457 mHWData.share(aMachine->mHWData);
12458 mMediumAttachments.share(aMachine->mMediumAttachments);
12459
12460 mStorageControllers.allocate();
12461 for (StorageControllerList::const_iterator
12462 it = aMachine->mStorageControllers->begin();
12463 it != aMachine->mStorageControllers->end();
12464 ++it)
12465 {
12466 ComObjPtr<StorageController> ctl;
12467 ctl.createObject();
12468 ctl->init(this, *it);
12469 mStorageControllers->push_back(ctl);
12470 }
12471
12472 mUSBControllers.allocate();
12473 for (USBControllerList::const_iterator
12474 it = aMachine->mUSBControllers->begin();
12475 it != aMachine->mUSBControllers->end();
12476 ++it)
12477 {
12478 ComObjPtr<USBController> ctl;
12479 ctl.createObject();
12480 ctl->init(this, *it);
12481 mUSBControllers->push_back(ctl);
12482 }
12483
12484 unconst(mPlatformProperties).createObject();
12485 mPlatformProperties->init(mParent);
12486 unconst(mPlatform).createObject();
12487 mPlatform->init(this, aMachine->mPlatform);
12488
12489 i_platformPropertiesUpdate();
12490
12491 unconst(mFirmwareSettings).createObject();
12492 mFirmwareSettings->init(this, aMachine->mFirmwareSettings);
12493
12494 unconst(mRecordingSettings).createObject();
12495 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12496
12497 unconst(mTrustedPlatformModule).createObject();
12498 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12499
12500 unconst(mNvramStore).createObject();
12501 mNvramStore->init(this, aMachine->mNvramStore);
12502
12503 /* create another GraphicsAdapter object that will be mutable */
12504 unconst(mGraphicsAdapter).createObject();
12505 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12506 /* create another VRDEServer object that will be mutable */
12507 unconst(mVRDEServer).createObject();
12508 mVRDEServer->init(this, aMachine->mVRDEServer);
12509 /* create another audio settings object that will be mutable */
12510 unconst(mAudioSettings).createObject();
12511 mAudioSettings->init(this, aMachine->mAudioSettings);
12512 /* create a list of serial ports that will be mutable */
12513 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12514 {
12515 unconst(mSerialPorts[slot]).createObject();
12516 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12517 }
12518 /* create a list of parallel ports that will be mutable */
12519 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12520 {
12521 unconst(mParallelPorts[slot]).createObject();
12522 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12523 }
12524
12525 /* create another USB device filters object that will be mutable */
12526 unconst(mUSBDeviceFilters).createObject();
12527 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12528
12529 /* create a list of network adapters that will be mutable */
12530 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12531 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12532 {
12533 unconst(mNetworkAdapters[slot]).createObject();
12534 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12535 }
12536
12537 /* create another bandwidth control object that will be mutable */
12538 unconst(mBandwidthControl).createObject();
12539 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12540
12541 unconst(mGuestDebugControl).createObject();
12542 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
12543
12544 /* default is to delete saved state on Saved -> PoweredOff transition */
12545 mRemoveSavedState = true;
12546
12547 /* Confirm a successful initialization when it's the case */
12548 autoInitSpan.setSucceeded();
12549
12550 miNATNetworksStarted = 0;
12551
12552 LogFlowThisFuncLeave();
12553 return hrc;
12554}
12555
12556/**
12557 * Uninitializes this session object. If the reason is other than
12558 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12559 * or the client watcher code.
12560 *
12561 * @param aReason uninitialization reason
12562 *
12563 * @note Locks mParent + this object for writing.
12564 */
12565void SessionMachine::uninit(Uninit::Reason aReason)
12566{
12567 LogFlowThisFuncEnter();
12568 LogFlowThisFunc(("reason=%d\n", aReason));
12569
12570 /*
12571 * Strongly reference ourselves to prevent this object deletion after
12572 * mData->mSession.mMachine.setNull() below (which can release the last
12573 * reference and call the destructor). Important: this must be done before
12574 * accessing any members (and before AutoUninitSpan that does it as well).
12575 * This self reference will be released as the very last step on return.
12576 */
12577 ComObjPtr<SessionMachine> selfRef;
12578 if (aReason != Uninit::Unexpected)
12579 selfRef = this;
12580
12581 /* Enclose the state transition Ready->InUninit->NotReady */
12582 AutoUninitSpan autoUninitSpan(this);
12583 if (autoUninitSpan.uninitDone())
12584 {
12585 LogFlowThisFunc(("Already uninitialized\n"));
12586 LogFlowThisFuncLeave();
12587 return;
12588 }
12589
12590 if (autoUninitSpan.initFailed())
12591 {
12592 /* We've been called by init() because it's failed. It's not really
12593 * necessary (nor it's safe) to perform the regular uninit sequence
12594 * below, the following is enough.
12595 */
12596 LogFlowThisFunc(("Initialization failed.\n"));
12597 /* destroy the machine client token */
12598 if (mClientToken)
12599 {
12600 delete mClientToken;
12601 mClientToken = NULL;
12602 }
12603 uninitDataAndChildObjects();
12604 mData.free();
12605 unconst(mParent) = NULL;
12606 unconst(mPeer) = NULL;
12607 LogFlowThisFuncLeave();
12608 return;
12609 }
12610
12611 MachineState_T lastState;
12612 {
12613 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12614 lastState = mData->mMachineState;
12615 }
12616 NOREF(lastState);
12617
12618#ifdef VBOX_WITH_USB
12619 // release all captured USB devices, but do this before requesting the locks below
12620 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12621 {
12622 /* Console::captureUSBDevices() is called in the VM process only after
12623 * setting the machine state to Starting or Restoring.
12624 * Console::detachAllUSBDevices() will be called upon successful
12625 * termination. So, we need to release USB devices only if there was
12626 * an abnormal termination of a running VM.
12627 *
12628 * This is identical to SessionMachine::DetachAllUSBDevices except
12629 * for the aAbnormal argument. */
12630 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12631 AssertComRC(hrc);
12632 NOREF(hrc);
12633
12634 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12635 if (service)
12636 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12637 }
12638#endif /* VBOX_WITH_USB */
12639
12640 // we need to lock this object in uninit() because the lock is shared
12641 // with mPeer (as well as data we modify below). mParent lock is needed
12642 // by several calls to it.
12643 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12644
12645#ifdef VBOX_WITH_RESOURCE_USAGE_API
12646 /*
12647 * It is safe to call Machine::i_unregisterMetrics() here because
12648 * PerformanceCollector::samplerCallback no longer accesses guest methods
12649 * holding the lock.
12650 */
12651 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12652 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12653 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12654 if (mCollectorGuest)
12655 {
12656 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12657 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12658 mCollectorGuest = NULL;
12659 }
12660#endif
12661
12662 if (aReason == Uninit::Abnormal)
12663 {
12664 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12665
12666 /*
12667 * Move the VM to the 'Aborted' machine state unless we are restoring a
12668 * VM that was in the 'Saved' machine state. In that case, if the VM
12669 * fails before reaching either the 'Restoring' machine state or the
12670 * 'Running' machine state then we set the machine state to
12671 * 'AbortedSaved' in order to preserve the saved state file so that the
12672 * VM can be restored in the future.
12673 */
12674 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12675 i_setMachineState(MachineState_AbortedSaved);
12676 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12677 i_setMachineState(MachineState_Aborted);
12678 }
12679
12680 // any machine settings modified?
12681 if (mData->flModifications)
12682 {
12683 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12684 i_rollback(false /* aNotify */);
12685 }
12686
12687 mData->mSession.mPID = NIL_RTPROCESS;
12688
12689 if (aReason == Uninit::Unexpected)
12690 {
12691 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12692 * client watcher thread to update the set of machines that have open
12693 * sessions. */
12694 mParent->i_updateClientWatcher();
12695 }
12696
12697 /* uninitialize all remote controls */
12698 if (mData->mSession.mRemoteControls.size())
12699 {
12700 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12701 mData->mSession.mRemoteControls.size()));
12702
12703 /* Always restart a the beginning, since the iterator is invalidated
12704 * by using erase(). */
12705 for (Data::Session::RemoteControlList::iterator
12706 it = mData->mSession.mRemoteControls.begin();
12707 it != mData->mSession.mRemoteControls.end();
12708 it = mData->mSession.mRemoteControls.begin())
12709 {
12710 ComPtr<IInternalSessionControl> pControl = *it;
12711 mData->mSession.mRemoteControls.erase(it);
12712 multilock.release();
12713 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12714 HRESULT hrc = pControl->Uninitialize();
12715 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
12716 if (FAILED(hrc))
12717 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12718 multilock.acquire();
12719 }
12720 mData->mSession.mRemoteControls.clear();
12721 }
12722
12723 /* Remove all references to the NAT network service. The service will stop
12724 * if all references (also from other VMs) are removed. */
12725 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12726 {
12727 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12728 {
12729 BOOL enabled;
12730 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12731 if ( FAILED(hrc)
12732 || !enabled)
12733 continue;
12734
12735 NetworkAttachmentType_T type;
12736 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12737 if ( SUCCEEDED(hrc)
12738 && type == NetworkAttachmentType_NATNetwork)
12739 {
12740 Bstr name;
12741 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12742 if (SUCCEEDED(hrc))
12743 {
12744 multilock.release();
12745 Utf8Str strName(name);
12746 LogRel(("VM '%s' stops using NAT network '%s'\n",
12747 mUserData->s.strName.c_str(), strName.c_str()));
12748 mParent->i_natNetworkRefDec(strName);
12749 multilock.acquire();
12750 }
12751 }
12752 }
12753 }
12754
12755 /*
12756 * An expected uninitialization can come only from #i_checkForDeath().
12757 * Otherwise it means that something's gone really wrong (for example,
12758 * the Session implementation has released the VirtualBox reference
12759 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12760 * etc). However, it's also possible, that the client releases the IPC
12761 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12762 * but the VirtualBox release event comes first to the server process.
12763 * This case is practically possible, so we should not assert on an
12764 * unexpected uninit, just log a warning.
12765 */
12766
12767 if (aReason == Uninit::Unexpected)
12768 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12769
12770 if (aReason != Uninit::Normal)
12771 {
12772 mData->mSession.mDirectControl.setNull();
12773 }
12774 else
12775 {
12776 /* this must be null here (see #OnSessionEnd()) */
12777 Assert(mData->mSession.mDirectControl.isNull());
12778 Assert(mData->mSession.mState == SessionState_Unlocking);
12779 Assert(!mData->mSession.mProgress.isNull());
12780 }
12781 if (mData->mSession.mProgress)
12782 {
12783 if (aReason == Uninit::Normal)
12784 mData->mSession.mProgress->i_notifyComplete(S_OK);
12785 else
12786 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12787 COM_IIDOF(ISession),
12788 getComponentName(),
12789 tr("The VM session was aborted"));
12790 mData->mSession.mProgress.setNull();
12791 }
12792
12793 if (mConsoleTaskData.mProgress)
12794 {
12795 Assert(aReason == Uninit::Abnormal);
12796 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12797 COM_IIDOF(ISession),
12798 getComponentName(),
12799 tr("The VM session was aborted"));
12800 mConsoleTaskData.mProgress.setNull();
12801 }
12802
12803 /* remove the association between the peer machine and this session machine */
12804 Assert( (SessionMachine*)mData->mSession.mMachine == this
12805 || aReason == Uninit::Unexpected);
12806
12807 /* reset the rest of session data */
12808 mData->mSession.mLockType = LockType_Null;
12809 mData->mSession.mMachine.setNull();
12810 mData->mSession.mState = SessionState_Unlocked;
12811 mData->mSession.mName.setNull();
12812
12813 /* destroy the machine client token before leaving the exclusive lock */
12814 if (mClientToken)
12815 {
12816 delete mClientToken;
12817 mClientToken = NULL;
12818 }
12819
12820 /* fire an event */
12821 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12822
12823 uninitDataAndChildObjects();
12824
12825 /* free the essential data structure last */
12826 mData.free();
12827
12828 /* release the exclusive lock before setting the below two to NULL */
12829 multilock.release();
12830
12831 unconst(mParent) = NULL;
12832 unconst(mPeer) = NULL;
12833
12834 AuthLibUnload(&mAuthLibCtx);
12835
12836 LogFlowThisFuncLeave();
12837}
12838
12839// util::Lockable interface
12840////////////////////////////////////////////////////////////////////////////////
12841
12842/**
12843 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12844 * with the primary Machine instance (mPeer).
12845 */
12846RWLockHandle *SessionMachine::lockHandle() const
12847{
12848 AssertReturn(mPeer != NULL, NULL);
12849 return mPeer->lockHandle();
12850}
12851
12852// IInternalMachineControl methods
12853////////////////////////////////////////////////////////////////////////////////
12854
12855/**
12856 * Passes collected guest statistics to performance collector object
12857 */
12858HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12859 ULONG aCpuKernel, ULONG aCpuIdle,
12860 ULONG aMemTotal, ULONG aMemFree,
12861 ULONG aMemBalloon, ULONG aMemShared,
12862 ULONG aMemCache, ULONG aPageTotal,
12863 ULONG aAllocVMM, ULONG aFreeVMM,
12864 ULONG aBalloonedVMM, ULONG aSharedVMM,
12865 ULONG aVmNetRx, ULONG aVmNetTx)
12866{
12867#ifdef VBOX_WITH_RESOURCE_USAGE_API
12868 if (mCollectorGuest)
12869 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12870 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12871 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12872 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12873
12874 return S_OK;
12875#else
12876 NOREF(aValidStats);
12877 NOREF(aCpuUser);
12878 NOREF(aCpuKernel);
12879 NOREF(aCpuIdle);
12880 NOREF(aMemTotal);
12881 NOREF(aMemFree);
12882 NOREF(aMemBalloon);
12883 NOREF(aMemShared);
12884 NOREF(aMemCache);
12885 NOREF(aPageTotal);
12886 NOREF(aAllocVMM);
12887 NOREF(aFreeVMM);
12888 NOREF(aBalloonedVMM);
12889 NOREF(aSharedVMM);
12890 NOREF(aVmNetRx);
12891 NOREF(aVmNetTx);
12892 return E_NOTIMPL;
12893#endif
12894}
12895
12896////////////////////////////////////////////////////////////////////////////////
12897//
12898// SessionMachine task records
12899//
12900////////////////////////////////////////////////////////////////////////////////
12901
12902/**
12903 * Task record for saving the machine state.
12904 */
12905class SessionMachine::SaveStateTask
12906 : public Machine::Task
12907{
12908public:
12909 SaveStateTask(SessionMachine *m,
12910 Progress *p,
12911 const Utf8Str &t,
12912 Reason_T enmReason,
12913 const Utf8Str &strStateFilePath)
12914 : Task(m, p, t),
12915 m_enmReason(enmReason),
12916 m_strStateFilePath(strStateFilePath)
12917 {}
12918
12919private:
12920 void handler()
12921 {
12922 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12923 }
12924
12925 Reason_T m_enmReason;
12926 Utf8Str m_strStateFilePath;
12927
12928 friend class SessionMachine;
12929};
12930
12931/**
12932 * Task thread implementation for SessionMachine::SaveState(), called from
12933 * SessionMachine::taskHandler().
12934 *
12935 * @note Locks this object for writing.
12936 *
12937 * @param task
12938 */
12939void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12940{
12941 LogFlowThisFuncEnter();
12942
12943 AutoCaller autoCaller(this);
12944 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12945 if (FAILED(autoCaller.hrc()))
12946 {
12947 /* we might have been uninitialized because the session was accidentally
12948 * closed by the client, so don't assert */
12949 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
12950 task.m_pProgress->i_notifyComplete(hrc);
12951 LogFlowThisFuncLeave();
12952 return;
12953 }
12954
12955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12956
12957 HRESULT hrc = S_OK;
12958
12959 try
12960 {
12961 ComPtr<IInternalSessionControl> directControl;
12962 if (mData->mSession.mLockType == LockType_VM)
12963 directControl = mData->mSession.mDirectControl;
12964 if (directControl.isNull())
12965 throw setError(VBOX_E_INVALID_VM_STATE,
12966 tr("Trying to save state without a running VM"));
12967 alock.release();
12968 BOOL fSuspendedBySave;
12969 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12970 Assert(!fSuspendedBySave);
12971 alock.acquire();
12972
12973 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
12974 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
12975 throw E_FAIL);
12976
12977 if (SUCCEEDED(hrc))
12978 {
12979 mSSData->strStateFilePath = task.m_strStateFilePath;
12980
12981 /* save all VM settings */
12982 hrc = i_saveSettings(NULL, alock);
12983 // no need to check whether VirtualBox.xml needs saving also since
12984 // we can't have a name change pending at this point
12985 }
12986 else
12987 {
12988 // On failure, set the state to the state we had at the beginning.
12989 i_setMachineState(task.m_machineStateBackup);
12990 i_updateMachineStateOnClient();
12991
12992 // Delete the saved state file (might have been already created).
12993 // No need to check whether this is shared with a snapshot here
12994 // because we certainly created a fresh saved state file here.
12995 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
12996 }
12997 }
12998 catch (HRESULT hrcXcpt)
12999 {
13000 hrc = hrcXcpt;
13001 }
13002
13003 task.m_pProgress->i_notifyComplete(hrc);
13004
13005 LogFlowThisFuncLeave();
13006}
13007
13008/**
13009 * @note Locks this object for writing.
13010 */
13011HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13012{
13013 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13014}
13015
13016HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13017{
13018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13019
13020 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13021 if (FAILED(hrc)) return hrc;
13022
13023 if ( mData->mMachineState != MachineState_Running
13024 && mData->mMachineState != MachineState_Paused
13025 )
13026 return setError(VBOX_E_INVALID_VM_STATE,
13027 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13028 Global::stringifyMachineState(mData->mMachineState));
13029
13030 ComObjPtr<Progress> pProgress;
13031 pProgress.createObject();
13032 hrc = pProgress->init(i_getVirtualBox(),
13033 static_cast<IMachine *>(this) /* aInitiator */,
13034 tr("Saving the execution state of the virtual machine"),
13035 FALSE /* aCancelable */);
13036 if (FAILED(hrc))
13037 return hrc;
13038
13039 Utf8Str strStateFilePath;
13040 i_composeSavedStateFilename(strStateFilePath);
13041
13042 /* create and start the task on a separate thread (note that it will not
13043 * start working until we release alock) */
13044 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13045 hrc = pTask->createThread();
13046 if (FAILED(hrc))
13047 return hrc;
13048
13049 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13050 i_setMachineState(MachineState_Saving);
13051 i_updateMachineStateOnClient();
13052
13053 pProgress.queryInterfaceTo(aProgress.asOutParam());
13054
13055 return S_OK;
13056}
13057
13058/**
13059 * @note Locks this object for writing.
13060 */
13061HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13062{
13063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13064
13065 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13066 if (FAILED(hrc)) return hrc;
13067
13068 if ( mData->mMachineState != MachineState_PoweredOff
13069 && mData->mMachineState != MachineState_Teleported
13070 && mData->mMachineState != MachineState_Aborted
13071 )
13072 return setError(VBOX_E_INVALID_VM_STATE,
13073 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13074 Global::stringifyMachineState(mData->mMachineState));
13075
13076 com::Utf8Str stateFilePathFull;
13077 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13078 if (RT_FAILURE(vrc))
13079 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13080 tr("Invalid saved state file path '%s' (%Rrc)"),
13081 aSavedStateFile.c_str(),
13082 vrc);
13083
13084 mSSData->strStateFilePath = stateFilePathFull;
13085
13086 /* The below i_setMachineState() will detect the state transition and will
13087 * update the settings file */
13088
13089 return i_setMachineState(MachineState_Saved);
13090}
13091
13092/**
13093 * @note Locks this object for writing.
13094 */
13095HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13096{
13097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13098
13099 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13100 if (FAILED(hrc)) return hrc;
13101
13102 if ( mData->mMachineState != MachineState_Saved
13103 && mData->mMachineState != MachineState_AbortedSaved)
13104 return setError(VBOX_E_INVALID_VM_STATE,
13105 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13106 Global::stringifyMachineState(mData->mMachineState));
13107
13108 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13109
13110 /*
13111 * Saved -> PoweredOff transition will be detected in the SessionMachine
13112 * and properly handled.
13113 */
13114 hrc = i_setMachineState(MachineState_PoweredOff);
13115 return hrc;
13116}
13117
13118
13119/**
13120 * @note Locks the same as #i_setMachineState() does.
13121 */
13122HRESULT SessionMachine::updateState(MachineState_T aState)
13123{
13124 return i_setMachineState(aState);
13125}
13126
13127/**
13128 * @note Locks this object for writing.
13129 */
13130HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13131{
13132 IProgress *pProgress(aProgress);
13133
13134 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13135
13136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13137
13138 if (mData->mSession.mState != SessionState_Locked)
13139 return VBOX_E_INVALID_OBJECT_STATE;
13140
13141 if (!mData->mSession.mProgress.isNull())
13142 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13143
13144 /* If we didn't reference the NAT network service yet, add a reference to
13145 * force a start */
13146 if (miNATNetworksStarted < 1)
13147 {
13148 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13149 {
13150 BOOL enabled;
13151 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13152 if ( FAILED(hrc)
13153 || !enabled)
13154 continue;
13155
13156 NetworkAttachmentType_T type;
13157 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13158 if ( SUCCEEDED(hrc)
13159 && type == NetworkAttachmentType_NATNetwork)
13160 {
13161 Bstr name;
13162 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13163 if (SUCCEEDED(hrc))
13164 {
13165 Utf8Str strName(name);
13166 LogRel(("VM '%s' starts using NAT network '%s'\n",
13167 mUserData->s.strName.c_str(), strName.c_str()));
13168 mPeer->lockHandle()->unlockWrite();
13169 mParent->i_natNetworkRefInc(strName);
13170#ifdef RT_LOCK_STRICT
13171 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13172#else
13173 mPeer->lockHandle()->lockWrite();
13174#endif
13175 }
13176 }
13177 }
13178 miNATNetworksStarted++;
13179 }
13180
13181 LogFlowThisFunc(("returns S_OK.\n"));
13182 return S_OK;
13183}
13184
13185/**
13186 * @note Locks this object for writing.
13187 */
13188HRESULT SessionMachine::endPowerUp(LONG aResult)
13189{
13190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13191
13192 if (mData->mSession.mState != SessionState_Locked)
13193 return VBOX_E_INVALID_OBJECT_STATE;
13194
13195 /* Finalize the LaunchVMProcess progress object. */
13196 if (mData->mSession.mProgress)
13197 {
13198 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13199 mData->mSession.mProgress.setNull();
13200 }
13201
13202 if (SUCCEEDED((HRESULT)aResult))
13203 {
13204#ifdef VBOX_WITH_RESOURCE_USAGE_API
13205 /* The VM has been powered up successfully, so it makes sense
13206 * now to offer the performance metrics for a running machine
13207 * object. Doing it earlier wouldn't be safe. */
13208 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13209 mData->mSession.mPID);
13210#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13211 }
13212
13213 return S_OK;
13214}
13215
13216/**
13217 * @note Locks this object for writing.
13218 */
13219HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13220{
13221 LogFlowThisFuncEnter();
13222
13223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13224
13225 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13226 E_FAIL);
13227
13228 /* create a progress object to track operation completion */
13229 ComObjPtr<Progress> pProgress;
13230 pProgress.createObject();
13231 pProgress->init(i_getVirtualBox(),
13232 static_cast<IMachine *>(this) /* aInitiator */,
13233 tr("Stopping the virtual machine"),
13234 FALSE /* aCancelable */);
13235
13236 /* fill in the console task data */
13237 mConsoleTaskData.mLastState = mData->mMachineState;
13238 mConsoleTaskData.mProgress = pProgress;
13239
13240 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13241 i_setMachineState(MachineState_Stopping);
13242
13243 pProgress.queryInterfaceTo(aProgress.asOutParam());
13244
13245 return S_OK;
13246}
13247
13248/**
13249 * @note Locks this object for writing.
13250 */
13251HRESULT SessionMachine::endPoweringDown(LONG aResult,
13252 const com::Utf8Str &aErrMsg)
13253{
13254 HRESULT const hrcResult = (HRESULT)aResult;
13255 LogFlowThisFuncEnter();
13256
13257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13258
13259 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13260 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13261 && mConsoleTaskData.mLastState != MachineState_Null,
13262 E_FAIL);
13263
13264 /*
13265 * On failure, set the state to the state we had when BeginPoweringDown()
13266 * was called (this is expected by Console::PowerDown() and the associated
13267 * task). On success the VM process already changed the state to
13268 * MachineState_PoweredOff, so no need to do anything.
13269 */
13270 if (FAILED(hrcResult))
13271 i_setMachineState(mConsoleTaskData.mLastState);
13272
13273 /* notify the progress object about operation completion */
13274 Assert(mConsoleTaskData.mProgress);
13275 if (SUCCEEDED(hrcResult))
13276 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13277 else
13278 {
13279 if (aErrMsg.length())
13280 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13281 COM_IIDOF(ISession),
13282 getComponentName(),
13283 aErrMsg.c_str());
13284 else
13285 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13286 }
13287
13288 /* clear out the temporary saved state data */
13289 mConsoleTaskData.mLastState = MachineState_Null;
13290 mConsoleTaskData.mProgress.setNull();
13291
13292 LogFlowThisFuncLeave();
13293 return S_OK;
13294}
13295
13296
13297/**
13298 * Goes through the USB filters of the given machine to see if the given
13299 * device matches any filter or not.
13300 *
13301 * @note Locks the same as USBController::hasMatchingFilter() does.
13302 */
13303HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13304 BOOL *aMatched,
13305 ULONG *aMaskedInterfaces)
13306{
13307 LogFlowThisFunc(("\n"));
13308
13309#ifdef VBOX_WITH_USB
13310 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13311#else
13312 NOREF(aDevice);
13313 NOREF(aMaskedInterfaces);
13314 *aMatched = FALSE;
13315#endif
13316
13317 return S_OK;
13318}
13319
13320/**
13321 * @note Locks the same as Host::captureUSBDevice() does.
13322 */
13323HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13324{
13325 LogFlowThisFunc(("\n"));
13326
13327#ifdef VBOX_WITH_USB
13328 /* if captureDeviceForVM() fails, it must have set extended error info */
13329 clearError();
13330 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13331 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13332 return hrc;
13333
13334 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13335 AssertReturn(service, E_FAIL);
13336 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13337#else
13338 RT_NOREF(aId, aCaptureFilename);
13339 return E_NOTIMPL;
13340#endif
13341}
13342
13343/**
13344 * @note Locks the same as Host::detachUSBDevice() does.
13345 */
13346HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13347 BOOL aDone)
13348{
13349 LogFlowThisFunc(("\n"));
13350
13351#ifdef VBOX_WITH_USB
13352 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13353 AssertReturn(service, E_FAIL);
13354 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13355#else
13356 NOREF(aId);
13357 NOREF(aDone);
13358 return E_NOTIMPL;
13359#endif
13360}
13361
13362/**
13363 * Inserts all machine filters to the USB proxy service and then calls
13364 * Host::autoCaptureUSBDevices().
13365 *
13366 * Called by Console from the VM process upon VM startup.
13367 *
13368 * @note Locks what called methods lock.
13369 */
13370HRESULT SessionMachine::autoCaptureUSBDevices()
13371{
13372 LogFlowThisFunc(("\n"));
13373
13374#ifdef VBOX_WITH_USB
13375 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13376 AssertComRC(hrc);
13377 NOREF(hrc);
13378
13379 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13380 AssertReturn(service, E_FAIL);
13381 return service->autoCaptureDevicesForVM(this);
13382#else
13383 return S_OK;
13384#endif
13385}
13386
13387/**
13388 * Removes all machine filters from the USB proxy service and then calls
13389 * Host::detachAllUSBDevices().
13390 *
13391 * Called by Console from the VM process upon normal VM termination or by
13392 * SessionMachine::uninit() upon abnormal VM termination (from under the
13393 * Machine/SessionMachine lock).
13394 *
13395 * @note Locks what called methods lock.
13396 */
13397HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13398{
13399 LogFlowThisFunc(("\n"));
13400
13401#ifdef VBOX_WITH_USB
13402 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13403 AssertComRC(hrc);
13404 NOREF(hrc);
13405
13406 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13407 AssertReturn(service, E_FAIL);
13408 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13409#else
13410 NOREF(aDone);
13411 return S_OK;
13412#endif
13413}
13414
13415/**
13416 * @note Locks this object for writing.
13417 */
13418HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13419 ComPtr<IProgress> &aProgress)
13420{
13421 LogFlowThisFuncEnter();
13422
13423 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13424 /*
13425 * We don't assert below because it might happen that a non-direct session
13426 * informs us it is closed right after we've been uninitialized -- it's ok.
13427 */
13428
13429 /* get IInternalSessionControl interface */
13430 ComPtr<IInternalSessionControl> control(aSession);
13431
13432 ComAssertRet(!control.isNull(), E_INVALIDARG);
13433
13434 /* Creating a Progress object requires the VirtualBox lock, and
13435 * thus locking it here is required by the lock order rules. */
13436 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13437
13438 if (control == mData->mSession.mDirectControl)
13439 {
13440 /* The direct session is being normally closed by the client process
13441 * ----------------------------------------------------------------- */
13442
13443 /* go to the closing state (essential for all open*Session() calls and
13444 * for #i_checkForDeath()) */
13445 Assert(mData->mSession.mState == SessionState_Locked);
13446 mData->mSession.mState = SessionState_Unlocking;
13447
13448 /* set direct control to NULL to release the remote instance */
13449 mData->mSession.mDirectControl.setNull();
13450 LogFlowThisFunc(("Direct control is set to NULL\n"));
13451
13452 if (mData->mSession.mProgress)
13453 {
13454 /* finalize the progress, someone might wait if a frontend
13455 * closes the session before powering on the VM. */
13456 mData->mSession.mProgress->notifyComplete(E_FAIL,
13457 COM_IIDOF(ISession),
13458 getComponentName(),
13459 tr("The VM session was closed before any attempt to power it on"));
13460 mData->mSession.mProgress.setNull();
13461 }
13462
13463 /* Create the progress object the client will use to wait until
13464 * #i_checkForDeath() is called to uninitialize this session object after
13465 * it releases the IPC semaphore.
13466 * Note! Because we're "reusing" mProgress here, this must be a proxy
13467 * object just like for LaunchVMProcess. */
13468 Assert(mData->mSession.mProgress.isNull());
13469 ComObjPtr<ProgressProxy> progress;
13470 progress.createObject();
13471 ComPtr<IUnknown> pPeer(mPeer);
13472 progress->init(mParent, pPeer,
13473 Bstr(tr("Closing session")).raw(),
13474 FALSE /* aCancelable */);
13475 progress.queryInterfaceTo(aProgress.asOutParam());
13476 mData->mSession.mProgress = progress;
13477 }
13478 else
13479 {
13480 /* the remote session is being normally closed */
13481 bool found = false;
13482 for (Data::Session::RemoteControlList::iterator
13483 it = mData->mSession.mRemoteControls.begin();
13484 it != mData->mSession.mRemoteControls.end();
13485 ++it)
13486 {
13487 if (control == *it)
13488 {
13489 found = true;
13490 // This MUST be erase(it), not remove(*it) as the latter
13491 // triggers a very nasty use after free due to the place where
13492 // the value "lives".
13493 mData->mSession.mRemoteControls.erase(it);
13494 break;
13495 }
13496 }
13497 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13498 E_INVALIDARG);
13499 }
13500
13501 /* signal the client watcher thread, because the client is going away */
13502 mParent->i_updateClientWatcher();
13503
13504 LogFlowThisFuncLeave();
13505 return S_OK;
13506}
13507
13508HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13509 std::vector<com::Utf8Str> &aValues,
13510 std::vector<LONG64> &aTimestamps,
13511 std::vector<com::Utf8Str> &aFlags)
13512{
13513 LogFlowThisFunc(("\n"));
13514
13515#ifdef VBOX_WITH_GUEST_PROPS
13516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13517
13518 size_t cEntries = mHWData->mGuestProperties.size();
13519 aNames.resize(cEntries);
13520 aValues.resize(cEntries);
13521 aTimestamps.resize(cEntries);
13522 aFlags.resize(cEntries);
13523
13524 size_t i = 0;
13525 for (HWData::GuestPropertyMap::const_iterator
13526 it = mHWData->mGuestProperties.begin();
13527 it != mHWData->mGuestProperties.end();
13528 ++it, ++i)
13529 {
13530 aNames[i] = it->first;
13531 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
13532 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13533
13534 aValues[i] = it->second.strValue;
13535 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
13536 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13537
13538 aTimestamps[i] = it->second.mTimestamp;
13539
13540 /* If it is NULL, keep it NULL. */
13541 if (it->second.mFlags)
13542 {
13543 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13544 GuestPropWriteFlags(it->second.mFlags, szFlags);
13545 aFlags[i] = szFlags;
13546 }
13547 else
13548 aFlags[i] = "";
13549 }
13550 return S_OK;
13551#else
13552 ReturnComNotImplemented();
13553#endif
13554}
13555
13556HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13557 const com::Utf8Str &aValue,
13558 LONG64 aTimestamp,
13559 const com::Utf8Str &aFlags,
13560 BOOL fWasDeleted)
13561{
13562 LogFlowThisFunc(("\n"));
13563
13564#ifdef VBOX_WITH_GUEST_PROPS
13565 try
13566 {
13567 /*
13568 * Convert input up front.
13569 */
13570 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13571 if (aFlags.length())
13572 {
13573 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13574 AssertRCReturn(vrc, E_INVALIDARG);
13575 }
13576
13577 /*
13578 * Now grab the object lock, validate the state and do the update.
13579 */
13580
13581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13582
13583 if (!Global::IsOnline(mData->mMachineState))
13584 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13585
13586 i_setModified(IsModified_MachineData);
13587 mHWData.backup();
13588
13589 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13590 if (it != mHWData->mGuestProperties.end())
13591 {
13592 if (!fWasDeleted)
13593 {
13594 it->second.strValue = aValue;
13595 it->second.mTimestamp = aTimestamp;
13596 it->second.mFlags = fFlags;
13597 }
13598 else
13599 mHWData->mGuestProperties.erase(it);
13600
13601 mData->mGuestPropertiesModified = TRUE;
13602 }
13603 else if (!fWasDeleted)
13604 {
13605 HWData::GuestProperty prop;
13606 prop.strValue = aValue;
13607 prop.mTimestamp = aTimestamp;
13608 prop.mFlags = fFlags;
13609
13610 mHWData->mGuestProperties[aName] = prop;
13611 mData->mGuestPropertiesModified = TRUE;
13612 }
13613
13614 alock.release();
13615
13616 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13617 }
13618 catch (...)
13619 {
13620 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13621 }
13622 return S_OK;
13623#else
13624 ReturnComNotImplemented();
13625#endif
13626}
13627
13628
13629HRESULT SessionMachine::lockMedia()
13630{
13631 AutoMultiWriteLock2 alock(this->lockHandle(),
13632 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13633
13634 AssertReturn( mData->mMachineState == MachineState_Starting
13635 || mData->mMachineState == MachineState_Restoring
13636 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13637
13638 clearError();
13639 alock.release();
13640 return i_lockMedia();
13641}
13642
13643HRESULT SessionMachine::unlockMedia()
13644{
13645 HRESULT hrc = i_unlockMedia();
13646 return hrc;
13647}
13648
13649HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13650 ComPtr<IMediumAttachment> &aNewAttachment)
13651{
13652 // request the host lock first, since might be calling Host methods for getting host drives;
13653 // next, protect the media tree all the while we're in here, as well as our member variables
13654 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13655 this->lockHandle(),
13656 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13657
13658 IMediumAttachment *iAttach = aAttachment;
13659 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13660
13661 Utf8Str ctrlName;
13662 LONG lPort;
13663 LONG lDevice;
13664 bool fTempEject;
13665 {
13666 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13667
13668 /* Need to query the details first, as the IMediumAttachment reference
13669 * might be to the original settings, which we are going to change. */
13670 ctrlName = pAttach->i_getControllerName();
13671 lPort = pAttach->i_getPort();
13672 lDevice = pAttach->i_getDevice();
13673 fTempEject = pAttach->i_getTempEject();
13674 }
13675
13676 if (!fTempEject)
13677 {
13678 /* Remember previously mounted medium. The medium before taking the
13679 * backup is not necessarily the same thing. */
13680 ComObjPtr<Medium> oldmedium;
13681 oldmedium = pAttach->i_getMedium();
13682
13683 i_setModified(IsModified_Storage);
13684 mMediumAttachments.backup();
13685
13686 // The backup operation makes the pAttach reference point to the
13687 // old settings. Re-get the correct reference.
13688 pAttach = i_findAttachment(*mMediumAttachments.data(),
13689 ctrlName,
13690 lPort,
13691 lDevice);
13692
13693 {
13694 AutoCaller autoAttachCaller(this);
13695 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
13696
13697 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13698 if (!oldmedium.isNull())
13699 oldmedium->i_removeBackReference(mData->mUuid);
13700
13701 pAttach->i_updateMedium(NULL);
13702 pAttach->i_updateEjected();
13703 }
13704
13705 i_setModified(IsModified_Storage);
13706 }
13707 else
13708 {
13709 {
13710 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13711 pAttach->i_updateEjected();
13712 }
13713 }
13714
13715 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13716
13717 return S_OK;
13718}
13719
13720HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13721 com::Utf8Str &aResult)
13722{
13723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13724
13725 HRESULT hrc = S_OK;
13726
13727 if (!mAuthLibCtx.hAuthLibrary)
13728 {
13729 /* Load the external authentication library. */
13730 Bstr authLibrary;
13731 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13732
13733 Utf8Str filename = authLibrary;
13734
13735 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13736 if (RT_FAILURE(vrc))
13737 hrc = setErrorBoth(E_FAIL, vrc,
13738 tr("Could not load the external authentication library '%s' (%Rrc)"),
13739 filename.c_str(), vrc);
13740 }
13741
13742 /* The auth library might need the machine lock. */
13743 alock.release();
13744
13745 if (FAILED(hrc))
13746 return hrc;
13747
13748 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13749 {
13750 enum VRDEAuthParams
13751 {
13752 parmUuid = 1,
13753 parmGuestJudgement,
13754 parmUser,
13755 parmPassword,
13756 parmDomain,
13757 parmClientId
13758 };
13759
13760 AuthResult result = AuthResultAccessDenied;
13761
13762 Guid uuid(aAuthParams[parmUuid]);
13763 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13764 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13765
13766 result = AuthLibAuthenticate(&mAuthLibCtx,
13767 uuid.raw(), guestJudgement,
13768 aAuthParams[parmUser].c_str(),
13769 aAuthParams[parmPassword].c_str(),
13770 aAuthParams[parmDomain].c_str(),
13771 u32ClientId);
13772
13773 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13774 size_t cbPassword = aAuthParams[parmPassword].length();
13775 if (cbPassword)
13776 {
13777 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13778 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13779 }
13780
13781 if (result == AuthResultAccessGranted)
13782 aResult = "granted";
13783 else
13784 aResult = "denied";
13785
13786 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13787 aAuthParams[parmUser].c_str(), aResult.c_str()));
13788 }
13789 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13790 {
13791 enum VRDEAuthDisconnectParams
13792 {
13793 parmUuid = 1,
13794 parmClientId
13795 };
13796
13797 Guid uuid(aAuthParams[parmUuid]);
13798 uint32_t u32ClientId = 0;
13799 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13800 }
13801 else
13802 {
13803 hrc = E_INVALIDARG;
13804 }
13805
13806 return hrc;
13807}
13808
13809// public methods only for internal purposes
13810/////////////////////////////////////////////////////////////////////////////
13811
13812#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13813/**
13814 * Called from the client watcher thread to check for expected or unexpected
13815 * death of the client process that has a direct session to this machine.
13816 *
13817 * On Win32 and on OS/2, this method is called only when we've got the
13818 * mutex (i.e. the client has either died or terminated normally) so it always
13819 * returns @c true (the client is terminated, the session machine is
13820 * uninitialized).
13821 *
13822 * On other platforms, the method returns @c true if the client process has
13823 * terminated normally or abnormally and the session machine was uninitialized,
13824 * and @c false if the client process is still alive.
13825 *
13826 * @note Locks this object for writing.
13827 */
13828bool SessionMachine::i_checkForDeath()
13829{
13830 Uninit::Reason reason;
13831 bool terminated = false;
13832
13833 /* Enclose autoCaller with a block because calling uninit() from under it
13834 * will deadlock. */
13835 {
13836 AutoCaller autoCaller(this);
13837 if (!autoCaller.isOk())
13838 {
13839 /* return true if not ready, to cause the client watcher to exclude
13840 * the corresponding session from watching */
13841 LogFlowThisFunc(("Already uninitialized!\n"));
13842 return true;
13843 }
13844
13845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13846
13847 /* Determine the reason of death: if the session state is Closing here,
13848 * everything is fine. Otherwise it means that the client did not call
13849 * OnSessionEnd() before it released the IPC semaphore. This may happen
13850 * either because the client process has abnormally terminated, or
13851 * because it simply forgot to call ISession::Close() before exiting. We
13852 * threat the latter also as an abnormal termination (see
13853 * Session::uninit() for details). */
13854 reason = mData->mSession.mState == SessionState_Unlocking ?
13855 Uninit::Normal :
13856 Uninit::Abnormal;
13857
13858 if (mClientToken)
13859 terminated = mClientToken->release();
13860 } /* AutoCaller block */
13861
13862 if (terminated)
13863 uninit(reason);
13864
13865 return terminated;
13866}
13867
13868void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13869{
13870 LogFlowThisFunc(("\n"));
13871
13872 strTokenId.setNull();
13873
13874 AutoCaller autoCaller(this);
13875 AssertComRCReturnVoid(autoCaller.hrc());
13876
13877 Assert(mClientToken);
13878 if (mClientToken)
13879 mClientToken->getId(strTokenId);
13880}
13881#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13882IToken *SessionMachine::i_getToken()
13883{
13884 LogFlowThisFunc(("\n"));
13885
13886 AutoCaller autoCaller(this);
13887 AssertComRCReturn(autoCaller.hrc(), NULL);
13888
13889 Assert(mClientToken);
13890 if (mClientToken)
13891 return mClientToken->getToken();
13892 else
13893 return NULL;
13894}
13895#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13896
13897Machine::ClientToken *SessionMachine::i_getClientToken()
13898{
13899 LogFlowThisFunc(("\n"));
13900
13901 AutoCaller autoCaller(this);
13902 AssertComRCReturn(autoCaller.hrc(), NULL);
13903
13904 return mClientToken;
13905}
13906
13907
13908/**
13909 * @note Locks this object for reading.
13910 */
13911HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13912{
13913 LogFlowThisFunc(("\n"));
13914
13915 AutoCaller autoCaller(this);
13916 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13917
13918 ComPtr<IInternalSessionControl> directControl;
13919 {
13920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13921 if (mData->mSession.mLockType == LockType_VM)
13922 directControl = mData->mSession.mDirectControl;
13923 }
13924
13925 /* ignore notifications sent after #OnSessionEnd() is called */
13926 if (!directControl)
13927 return S_OK;
13928
13929 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13930}
13931
13932/**
13933 * @note Locks this object for reading.
13934 */
13935HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13936 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13937 const Utf8Str &aGuestIp, LONG aGuestPort)
13938{
13939 LogFlowThisFunc(("\n"));
13940
13941 AutoCaller autoCaller(this);
13942 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13943
13944 ComPtr<IInternalSessionControl> directControl;
13945 {
13946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13947 if (mData->mSession.mLockType == LockType_VM)
13948 directControl = mData->mSession.mDirectControl;
13949 }
13950
13951 /* ignore notifications sent after #OnSessionEnd() is called */
13952 if (!directControl)
13953 return S_OK;
13954 /*
13955 * instead acting like callback we ask IVirtualBox deliver corresponding event
13956 */
13957
13958 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13959 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13960 return S_OK;
13961}
13962
13963/**
13964 * @note Locks this object for reading.
13965 */
13966HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 if (mData->mSession.mLockType == LockType_VM)
13977 directControl = mData->mSession.mDirectControl;
13978 }
13979
13980 /* ignore notifications sent after #OnSessionEnd() is called */
13981 if (!directControl)
13982 return S_OK;
13983
13984 return directControl->OnAudioAdapterChange(audioAdapter);
13985}
13986
13987/**
13988 * @note Locks this object for reading.
13989 */
13990HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994 AutoCaller autoCaller(this);
13995 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13996
13997 ComPtr<IInternalSessionControl> directControl;
13998 {
13999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14000 if (mData->mSession.mLockType == LockType_VM)
14001 directControl = mData->mSession.mDirectControl;
14002 }
14003
14004 /* ignore notifications sent after #OnSessionEnd() is called */
14005 if (!directControl)
14006 return S_OK;
14007
14008 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14009}
14010
14011/**
14012 * @note Locks this object for reading.
14013 */
14014HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14015{
14016 LogFlowThisFunc(("\n"));
14017
14018 AutoCaller autoCaller(this);
14019 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14020
14021 ComPtr<IInternalSessionControl> directControl;
14022 {
14023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14024 if (mData->mSession.mLockType == LockType_VM)
14025 directControl = mData->mSession.mDirectControl;
14026 }
14027
14028 /* ignore notifications sent after #OnSessionEnd() is called */
14029 if (!directControl)
14030 return S_OK;
14031
14032 return directControl->OnSerialPortChange(serialPort);
14033}
14034
14035/**
14036 * @note Locks this object for reading.
14037 */
14038HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14039{
14040 LogFlowThisFunc(("\n"));
14041
14042 AutoCaller autoCaller(this);
14043 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14044
14045 ComPtr<IInternalSessionControl> directControl;
14046 {
14047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14048 if (mData->mSession.mLockType == LockType_VM)
14049 directControl = mData->mSession.mDirectControl;
14050 }
14051
14052 /* ignore notifications sent after #OnSessionEnd() is called */
14053 if (!directControl)
14054 return S_OK;
14055
14056 return directControl->OnParallelPortChange(parallelPort);
14057}
14058
14059/**
14060 * @note Locks this object for reading.
14061 */
14062HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14063{
14064 LogFlowThisFunc(("\n"));
14065
14066 AutoCaller autoCaller(this);
14067 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14068
14069 ComPtr<IInternalSessionControl> directControl;
14070 {
14071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14072 if (mData->mSession.mLockType == LockType_VM)
14073 directControl = mData->mSession.mDirectControl;
14074 }
14075
14076 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14077
14078 /* ignore notifications sent after #OnSessionEnd() is called */
14079 if (!directControl)
14080 return S_OK;
14081
14082 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14083}
14084
14085/**
14086 * @note Locks this object for reading.
14087 */
14088HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14089{
14090 LogFlowThisFunc(("\n"));
14091
14092 AutoCaller autoCaller(this);
14093 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14094
14095 ComPtr<IInternalSessionControl> directControl;
14096 {
14097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14098 if (mData->mSession.mLockType == LockType_VM)
14099 directControl = mData->mSession.mDirectControl;
14100 }
14101
14102 mParent->i_onMediumChanged(aAttachment);
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnMediumChange(aAttachment, aForce);
14109}
14110
14111HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14112{
14113 LogFlowThisFunc(("\n"));
14114
14115 AutoCaller autoCaller(this);
14116 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14117
14118 ComPtr<IInternalSessionControl> directControl;
14119 {
14120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14121 if (mData->mSession.mLockType == LockType_VM)
14122 directControl = mData->mSession.mDirectControl;
14123 }
14124
14125 /* ignore notifications sent after #OnSessionEnd() is called */
14126 if (!directControl)
14127 return S_OK;
14128
14129 return directControl->OnVMProcessPriorityChange(aPriority);
14130}
14131
14132/**
14133 * @note Locks this object for reading.
14134 */
14135HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14136{
14137 LogFlowThisFunc(("\n"));
14138
14139 AutoCaller autoCaller(this);
14140 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14141
14142 ComPtr<IInternalSessionControl> directControl;
14143 {
14144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14145 if (mData->mSession.mLockType == LockType_VM)
14146 directControl = mData->mSession.mDirectControl;
14147 }
14148
14149 /* ignore notifications sent after #OnSessionEnd() is called */
14150 if (!directControl)
14151 return S_OK;
14152
14153 return directControl->OnCPUChange(aCPU, aRemove);
14154}
14155
14156HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14157{
14158 LogFlowThisFunc(("\n"));
14159
14160 AutoCaller autoCaller(this);
14161 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14162
14163 ComPtr<IInternalSessionControl> directControl;
14164 {
14165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14166 if (mData->mSession.mLockType == LockType_VM)
14167 directControl = mData->mSession.mDirectControl;
14168 }
14169
14170 /* ignore notifications sent after #OnSessionEnd() is called */
14171 if (!directControl)
14172 return S_OK;
14173
14174 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14175}
14176
14177/**
14178 * @note Locks this object for reading.
14179 */
14180HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14181{
14182 LogFlowThisFunc(("\n"));
14183
14184 AutoCaller autoCaller(this);
14185 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14186
14187 ComPtr<IInternalSessionControl> directControl;
14188 {
14189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14190 if (mData->mSession.mLockType == LockType_VM)
14191 directControl = mData->mSession.mDirectControl;
14192 }
14193
14194 /* ignore notifications sent after #OnSessionEnd() is called */
14195 if (!directControl)
14196 return S_OK;
14197
14198 return directControl->OnVRDEServerChange(aRestart);
14199}
14200
14201/**
14202 * @note Locks this object for reading.
14203 */
14204HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14205{
14206 LogFlowThisFunc(("\n"));
14207
14208 AutoCaller autoCaller(this);
14209 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14210
14211 ComPtr<IInternalSessionControl> directControl;
14212 {
14213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14214 if (mData->mSession.mLockType == LockType_VM)
14215 directControl = mData->mSession.mDirectControl;
14216 }
14217
14218 /* ignore notifications sent after #OnSessionEnd() is called */
14219 if (!directControl)
14220 return S_OK;
14221
14222 return directControl->OnRecordingChange(aEnable);
14223}
14224
14225/**
14226 * @note Locks this object for reading.
14227 */
14228HRESULT SessionMachine::i_onUSBControllerChange()
14229{
14230 LogFlowThisFunc(("\n"));
14231
14232 AutoCaller autoCaller(this);
14233 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14234
14235 ComPtr<IInternalSessionControl> directControl;
14236 {
14237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14238 if (mData->mSession.mLockType == LockType_VM)
14239 directControl = mData->mSession.mDirectControl;
14240 }
14241
14242 /* ignore notifications sent after #OnSessionEnd() is called */
14243 if (!directControl)
14244 return S_OK;
14245
14246 return directControl->OnUSBControllerChange();
14247}
14248
14249/**
14250 * @note Locks this object for reading.
14251 */
14252HRESULT SessionMachine::i_onSharedFolderChange()
14253{
14254 LogFlowThisFunc(("\n"));
14255
14256 AutoCaller autoCaller(this);
14257 AssertComRCReturnRC(autoCaller.hrc());
14258
14259 ComPtr<IInternalSessionControl> directControl;
14260 {
14261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14262 if (mData->mSession.mLockType == LockType_VM)
14263 directControl = mData->mSession.mDirectControl;
14264 }
14265
14266 /* ignore notifications sent after #OnSessionEnd() is called */
14267 if (!directControl)
14268 return S_OK;
14269
14270 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14271}
14272
14273/**
14274 * @note Locks this object for reading.
14275 */
14276HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14277{
14278 LogFlowThisFunc(("\n"));
14279
14280 AutoCaller autoCaller(this);
14281 AssertComRCReturnRC(autoCaller.hrc());
14282
14283 ComPtr<IInternalSessionControl> directControl;
14284 {
14285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14286 if (mData->mSession.mLockType == LockType_VM)
14287 directControl = mData->mSession.mDirectControl;
14288 }
14289
14290 /* ignore notifications sent after #OnSessionEnd() is called */
14291 if (!directControl)
14292 return S_OK;
14293
14294 return directControl->OnClipboardModeChange(aClipboardMode);
14295}
14296
14297/**
14298 * @note Locks this object for reading.
14299 */
14300HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14301{
14302 LogFlowThisFunc(("\n"));
14303
14304 AutoCaller autoCaller(this);
14305 AssertComRCReturnRC(autoCaller.hrc());
14306
14307 ComPtr<IInternalSessionControl> directControl;
14308 {
14309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14310 if (mData->mSession.mLockType == LockType_VM)
14311 directControl = mData->mSession.mDirectControl;
14312 }
14313
14314 /* ignore notifications sent after #OnSessionEnd() is called */
14315 if (!directControl)
14316 return S_OK;
14317
14318 return directControl->OnClipboardFileTransferModeChange(aEnable);
14319}
14320
14321/**
14322 * @note Locks this object for reading.
14323 */
14324HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14325{
14326 LogFlowThisFunc(("\n"));
14327
14328 AutoCaller autoCaller(this);
14329 AssertComRCReturnRC(autoCaller.hrc());
14330
14331 ComPtr<IInternalSessionControl> directControl;
14332 {
14333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14334 if (mData->mSession.mLockType == LockType_VM)
14335 directControl = mData->mSession.mDirectControl;
14336 }
14337
14338 /* ignore notifications sent after #OnSessionEnd() is called */
14339 if (!directControl)
14340 return S_OK;
14341
14342 return directControl->OnDnDModeChange(aDnDMode);
14343}
14344
14345/**
14346 * @note Locks this object for reading.
14347 */
14348HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14349{
14350 LogFlowThisFunc(("\n"));
14351
14352 AutoCaller autoCaller(this);
14353 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14354
14355 ComPtr<IInternalSessionControl> directControl;
14356 {
14357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14358 if (mData->mSession.mLockType == LockType_VM)
14359 directControl = mData->mSession.mDirectControl;
14360 }
14361
14362 /* ignore notifications sent after #OnSessionEnd() is called */
14363 if (!directControl)
14364 return S_OK;
14365
14366 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14367}
14368
14369/**
14370 * @note Locks this object for reading.
14371 */
14372HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14373{
14374 LogFlowThisFunc(("\n"));
14375
14376 AutoCaller autoCaller(this);
14377 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14378
14379 ComPtr<IInternalSessionControl> directControl;
14380 {
14381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14382 if (mData->mSession.mLockType == LockType_VM)
14383 directControl = mData->mSession.mDirectControl;
14384 }
14385
14386 /* ignore notifications sent after #OnSessionEnd() is called */
14387 if (!directControl)
14388 return S_OK;
14389
14390 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14391}
14392
14393/**
14394 * @note Locks this object for reading.
14395 */
14396HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14397{
14398 LogFlowThisFunc(("\n"));
14399
14400 AutoCaller autoCaller(this);
14401 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14402
14403 ComPtr<IInternalSessionControl> directControl;
14404 {
14405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14406 if (mData->mSession.mLockType == LockType_VM)
14407 directControl = mData->mSession.mDirectControl;
14408 }
14409
14410 /* ignore notifications sent after #OnSessionEnd() is called */
14411 if (!directControl)
14412 return S_OK;
14413
14414 return directControl->OnGuestDebugControlChange(guestDebugControl);
14415}
14416
14417/**
14418 * Returns @c true if this machine's USB controller reports it has a matching
14419 * filter for the given USB device and @c false otherwise.
14420 *
14421 * @note locks this object for reading.
14422 */
14423bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14424{
14425 AutoCaller autoCaller(this);
14426 /* silently return if not ready -- this method may be called after the
14427 * direct machine session has been called */
14428 if (!autoCaller.isOk())
14429 return false;
14430
14431#ifdef VBOX_WITH_USB
14432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14433
14434 switch (mData->mMachineState)
14435 {
14436 case MachineState_Starting:
14437 case MachineState_Restoring:
14438 case MachineState_TeleportingIn:
14439 case MachineState_Paused:
14440 case MachineState_Running:
14441 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14442 * elsewhere... */
14443 alock.release();
14444 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14445 default: break;
14446 }
14447#else
14448 NOREF(aDevice);
14449 NOREF(aMaskedIfs);
14450#endif
14451 return false;
14452}
14453
14454/**
14455 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14456 */
14457HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14458 IVirtualBoxErrorInfo *aError,
14459 ULONG aMaskedIfs,
14460 const com::Utf8Str &aCaptureFilename)
14461{
14462 LogFlowThisFunc(("\n"));
14463
14464 AutoCaller autoCaller(this);
14465
14466 /* This notification may happen after the machine object has been
14467 * uninitialized (the session was closed), so don't assert. */
14468 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14469
14470 ComPtr<IInternalSessionControl> directControl;
14471 {
14472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14473 if (mData->mSession.mLockType == LockType_VM)
14474 directControl = mData->mSession.mDirectControl;
14475 }
14476
14477 /* fail on notifications sent after #OnSessionEnd() is called, it is
14478 * expected by the caller */
14479 if (!directControl)
14480 return E_FAIL;
14481
14482 /* No locks should be held at this point. */
14483 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14484 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14485
14486 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14487}
14488
14489/**
14490 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14491 */
14492HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14493 IVirtualBoxErrorInfo *aError)
14494{
14495 LogFlowThisFunc(("\n"));
14496
14497 AutoCaller autoCaller(this);
14498
14499 /* This notification may happen after the machine object has been
14500 * uninitialized (the session was closed), so don't assert. */
14501 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14502
14503 ComPtr<IInternalSessionControl> directControl;
14504 {
14505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14506 if (mData->mSession.mLockType == LockType_VM)
14507 directControl = mData->mSession.mDirectControl;
14508 }
14509
14510 /* fail on notifications sent after #OnSessionEnd() is called, it is
14511 * expected by the caller */
14512 if (!directControl)
14513 return E_FAIL;
14514
14515 /* No locks should be held at this point. */
14516 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14517 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14518
14519 return directControl->OnUSBDeviceDetach(aId, aError);
14520}
14521
14522// protected methods
14523/////////////////////////////////////////////////////////////////////////////
14524
14525/**
14526 * Deletes the given file if it is no longer in use by either the current machine state
14527 * (if the machine is "saved") or any of the machine's snapshots.
14528 *
14529 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14530 * but is different for each SnapshotMachine. When calling this, the order of calling this
14531 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14532 * is therefore critical. I know, it's all rather messy.
14533 *
14534 * @param strStateFile
14535 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14536 * the test for whether the saved state file is in use.
14537 */
14538void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14539 Snapshot *pSnapshotToIgnore)
14540{
14541 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14542 if ( (strStateFile.isNotEmpty())
14543 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14544 )
14545 // ... and it must also not be shared with other snapshots
14546 if ( !mData->mFirstSnapshot
14547 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14548 // this checks the SnapshotMachine's state file paths
14549 )
14550 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
14551}
14552
14553/**
14554 * Locks the attached media.
14555 *
14556 * All attached hard disks are locked for writing and DVD/floppy are locked for
14557 * reading. Parents of attached hard disks (if any) are locked for reading.
14558 *
14559 * This method also performs accessibility check of all media it locks: if some
14560 * media is inaccessible, the method will return a failure and a bunch of
14561 * extended error info objects per each inaccessible medium.
14562 *
14563 * Note that this method is atomic: if it returns a success, all media are
14564 * locked as described above; on failure no media is locked at all (all
14565 * succeeded individual locks will be undone).
14566 *
14567 * The caller is responsible for doing the necessary state sanity checks.
14568 *
14569 * The locks made by this method must be undone by calling #unlockMedia() when
14570 * no more needed.
14571 */
14572HRESULT SessionMachine::i_lockMedia()
14573{
14574 AutoCaller autoCaller(this);
14575 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14576
14577 AutoMultiWriteLock2 alock(this->lockHandle(),
14578 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14579
14580 /* bail out if trying to lock things with already set up locking */
14581 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14582
14583 MultiResult hrcMult(S_OK);
14584
14585 /* Collect locking information for all medium objects attached to the VM. */
14586 for (MediumAttachmentList::const_iterator
14587 it = mMediumAttachments->begin();
14588 it != mMediumAttachments->end();
14589 ++it)
14590 {
14591 MediumAttachment *pAtt = *it;
14592 DeviceType_T devType = pAtt->i_getType();
14593 Medium *pMedium = pAtt->i_getMedium();
14594
14595 MediumLockList *pMediumLockList(new MediumLockList());
14596 // There can be attachments without a medium (floppy/dvd), and thus
14597 // it's impossible to create a medium lock list. It still makes sense
14598 // to have the empty medium lock list in the map in case a medium is
14599 // attached later.
14600 if (pMedium != NULL)
14601 {
14602 MediumType_T mediumType = pMedium->i_getType();
14603 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14604 || mediumType == MediumType_Shareable;
14605 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14606
14607 alock.release();
14608 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14609 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14610 false /* fMediumLockWriteAll */,
14611 NULL,
14612 *pMediumLockList);
14613 alock.acquire();
14614 if (FAILED(hrcMult))
14615 {
14616 delete pMediumLockList;
14617 mData->mSession.mLockedMedia.Clear();
14618 break;
14619 }
14620 }
14621
14622 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14623 if (FAILED(hrc))
14624 {
14625 mData->mSession.mLockedMedia.Clear();
14626 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
14627 break;
14628 }
14629 }
14630
14631 if (SUCCEEDED(hrcMult))
14632 {
14633 /* Now lock all media. If this fails, nothing is locked. */
14634 alock.release();
14635 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
14636 alock.acquire();
14637 if (FAILED(hrc))
14638 hrcMult = setError(hrc,
14639 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14640 }
14641
14642 return hrcMult;
14643}
14644
14645/**
14646 * Undoes the locks made by by #lockMedia().
14647 */
14648HRESULT SessionMachine::i_unlockMedia()
14649{
14650 AutoCaller autoCaller(this);
14651 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
14652
14653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14654
14655 /* we may be holding important error info on the current thread;
14656 * preserve it */
14657 ErrorInfoKeeper eik;
14658
14659 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
14660 AssertComRC(hrc);
14661 return hrc;
14662}
14663
14664/**
14665 * Helper to change the machine state (reimplementation).
14666 *
14667 * @note Locks this object for writing.
14668 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14669 * it can cause crashes in random places due to unexpectedly committing
14670 * the current settings. The caller is responsible for that. The call
14671 * to saveStateSettings is fine, because this method does not commit.
14672 */
14673HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14674{
14675 LogFlowThisFuncEnter();
14676
14677 AutoCaller autoCaller(this);
14678 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14679
14680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14681
14682 MachineState_T oldMachineState = mData->mMachineState;
14683
14684 AssertMsgReturn(oldMachineState != aMachineState,
14685 ("oldMachineState=%s, aMachineState=%s\n",
14686 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14687 E_FAIL);
14688
14689 HRESULT hrc = S_OK;
14690
14691 int stsFlags = 0;
14692 bool deleteSavedState = false;
14693
14694 /* detect some state transitions */
14695
14696 if ( ( ( oldMachineState == MachineState_Saved
14697 || oldMachineState == MachineState_AbortedSaved
14698 )
14699 && aMachineState == MachineState_Restoring
14700 )
14701 || ( ( oldMachineState == MachineState_PoweredOff
14702 || oldMachineState == MachineState_Teleported
14703 || oldMachineState == MachineState_Aborted
14704 )
14705 && ( aMachineState == MachineState_TeleportingIn
14706 || aMachineState == MachineState_Starting
14707 )
14708 )
14709 )
14710 {
14711 /* The EMT thread is about to start */
14712
14713 /* Nothing to do here for now... */
14714
14715 /// @todo NEWMEDIA don't let mDVDDrive and other children
14716 /// change anything when in the Starting/Restoring state
14717 }
14718 else if ( ( oldMachineState == MachineState_Running
14719 || oldMachineState == MachineState_Paused
14720 || oldMachineState == MachineState_Teleporting
14721 || oldMachineState == MachineState_OnlineSnapshotting
14722 || oldMachineState == MachineState_LiveSnapshotting
14723 || oldMachineState == MachineState_Stuck
14724 || oldMachineState == MachineState_Starting
14725 || oldMachineState == MachineState_Stopping
14726 || oldMachineState == MachineState_Saving
14727 || oldMachineState == MachineState_Restoring
14728 || oldMachineState == MachineState_TeleportingPausedVM
14729 || oldMachineState == MachineState_TeleportingIn
14730 )
14731 && ( aMachineState == MachineState_PoweredOff
14732 || aMachineState == MachineState_Saved
14733 || aMachineState == MachineState_Teleported
14734 || aMachineState == MachineState_Aborted
14735 || aMachineState == MachineState_AbortedSaved
14736 )
14737 )
14738 {
14739 /* The EMT thread has just stopped, unlock attached media. Note that as
14740 * opposed to locking that is done from Console, we do unlocking here
14741 * because the VM process may have aborted before having a chance to
14742 * properly unlock all media it locked. */
14743
14744 unlockMedia();
14745 }
14746
14747 if (oldMachineState == MachineState_Restoring)
14748 {
14749 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14750 {
14751 /*
14752 * delete the saved state file once the machine has finished
14753 * restoring from it (note that Console sets the state from
14754 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14755 * to give the user an ability to fix an error and retry --
14756 * we keep the saved state file in this case)
14757 */
14758 deleteSavedState = true;
14759 }
14760 }
14761 else if ( ( oldMachineState == MachineState_Saved
14762 || oldMachineState == MachineState_AbortedSaved
14763 )
14764 && ( aMachineState == MachineState_PoweredOff
14765 || aMachineState == MachineState_Teleported
14766 )
14767 )
14768 {
14769 /* delete the saved state after SessionMachine::discardSavedState() is called */
14770 deleteSavedState = true;
14771 mData->mCurrentStateModified = TRUE;
14772 stsFlags |= SaveSTS_CurStateModified;
14773 }
14774 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14775 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14776
14777 if ( aMachineState == MachineState_Starting
14778 || aMachineState == MachineState_Restoring
14779 || aMachineState == MachineState_TeleportingIn
14780 )
14781 {
14782 /* set the current state modified flag to indicate that the current
14783 * state is no more identical to the state in the
14784 * current snapshot */
14785 if (!mData->mCurrentSnapshot.isNull())
14786 {
14787 mData->mCurrentStateModified = TRUE;
14788 stsFlags |= SaveSTS_CurStateModified;
14789 }
14790 }
14791
14792 if (deleteSavedState)
14793 {
14794 if (mRemoveSavedState)
14795 {
14796 Assert(!mSSData->strStateFilePath.isEmpty());
14797
14798 // it is safe to delete the saved state file if ...
14799 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14800 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14801 // ... none of the snapshots share the saved state file
14802 )
14803 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
14804 }
14805
14806 mSSData->strStateFilePath.setNull();
14807 stsFlags |= SaveSTS_StateFilePath;
14808 }
14809
14810 /* redirect to the underlying peer machine */
14811 mPeer->i_setMachineState(aMachineState);
14812
14813 if ( oldMachineState != MachineState_RestoringSnapshot
14814 && ( aMachineState == MachineState_PoweredOff
14815 || aMachineState == MachineState_Teleported
14816 || aMachineState == MachineState_Aborted
14817 || aMachineState == MachineState_AbortedSaved
14818 || aMachineState == MachineState_Saved))
14819 {
14820 /* the machine has stopped execution
14821 * (or the saved state file was adopted) */
14822 stsFlags |= SaveSTS_StateTimeStamp;
14823 }
14824
14825 if ( ( oldMachineState == MachineState_PoweredOff
14826 || oldMachineState == MachineState_Aborted
14827 || oldMachineState == MachineState_Teleported
14828 )
14829 && aMachineState == MachineState_Saved)
14830 {
14831 /* the saved state file was adopted */
14832 Assert(!mSSData->strStateFilePath.isEmpty());
14833 stsFlags |= SaveSTS_StateFilePath;
14834 }
14835
14836#ifdef VBOX_WITH_GUEST_PROPS
14837 if ( aMachineState == MachineState_PoweredOff
14838 || aMachineState == MachineState_Aborted
14839 || aMachineState == MachineState_Teleported)
14840 {
14841 /* Make sure any transient guest properties get removed from the
14842 * property store on shutdown. */
14843 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14844
14845 /* remove it from the settings representation */
14846 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14847 for (settings::GuestPropertiesList::iterator
14848 it = llGuestProperties.begin();
14849 it != llGuestProperties.end();
14850 /*nothing*/)
14851 {
14852 const settings::GuestProperty &prop = *it;
14853 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14854 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14855 {
14856 it = llGuestProperties.erase(it);
14857 fNeedsSaving = true;
14858 }
14859 else
14860 {
14861 ++it;
14862 }
14863 }
14864
14865 /* Additionally remove it from the HWData representation. Required to
14866 * keep everything in sync, as this is what the API keeps using. */
14867 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14868 for (HWData::GuestPropertyMap::iterator
14869 it = llHWGuestProperties.begin();
14870 it != llHWGuestProperties.end();
14871 /*nothing*/)
14872 {
14873 uint32_t fFlags = it->second.mFlags;
14874 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14875 {
14876 /* iterator where we need to continue after the erase call
14877 * (C++03 is a fact still, and it doesn't return the iterator
14878 * which would allow continuing) */
14879 HWData::GuestPropertyMap::iterator it2 = it;
14880 ++it2;
14881 llHWGuestProperties.erase(it);
14882 it = it2;
14883 fNeedsSaving = true;
14884 }
14885 else
14886 {
14887 ++it;
14888 }
14889 }
14890
14891 if (fNeedsSaving)
14892 {
14893 mData->mCurrentStateModified = TRUE;
14894 stsFlags |= SaveSTS_CurStateModified;
14895 }
14896 }
14897#endif /* VBOX_WITH_GUEST_PROPS */
14898
14899 hrc = i_saveStateSettings(stsFlags);
14900
14901 if ( ( oldMachineState != MachineState_PoweredOff
14902 && oldMachineState != MachineState_Aborted
14903 && oldMachineState != MachineState_Teleported
14904 )
14905 && ( aMachineState == MachineState_PoweredOff
14906 || aMachineState == MachineState_Aborted
14907 || aMachineState == MachineState_Teleported
14908 )
14909 )
14910 {
14911 /* we've been shut down for any reason */
14912 /* no special action so far */
14913 }
14914
14915 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
14916 LogFlowThisFuncLeave();
14917 return hrc;
14918}
14919
14920/**
14921 * Sends the current machine state value to the VM process.
14922 *
14923 * @note Locks this object for reading, then calls a client process.
14924 */
14925HRESULT SessionMachine::i_updateMachineStateOnClient()
14926{
14927 AutoCaller autoCaller(this);
14928 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14929
14930 ComPtr<IInternalSessionControl> directControl;
14931 {
14932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14933 AssertReturn(!!mData, E_FAIL);
14934 if (mData->mSession.mLockType == LockType_VM)
14935 directControl = mData->mSession.mDirectControl;
14936
14937 /* directControl may be already set to NULL here in #OnSessionEnd()
14938 * called too early by the direct session process while there is still
14939 * some operation (like deleting the snapshot) in progress. The client
14940 * process in this case is waiting inside Session::close() for the
14941 * "end session" process object to complete, while #uninit() called by
14942 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14943 * operation to complete. For now, we accept this inconsistent behavior
14944 * and simply do nothing here. */
14945
14946 if (mData->mSession.mState == SessionState_Unlocking)
14947 return S_OK;
14948 }
14949
14950 /* ignore notifications sent after #OnSessionEnd() is called */
14951 if (!directControl)
14952 return S_OK;
14953
14954 return directControl->UpdateMachineState(mData->mMachineState);
14955}
14956
14957
14958/*static*/
14959HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14960{
14961 va_list args;
14962 va_start(args, pcszMsg);
14963 HRESULT hrc = setErrorInternalV(aResultCode,
14964 getStaticClassIID(),
14965 getStaticComponentName(),
14966 pcszMsg, args,
14967 false /* aWarning */,
14968 true /* aLogIt */);
14969 va_end(args);
14970 return hrc;
14971}
14972
14973
14974HRESULT Machine::updateState(MachineState_T aState)
14975{
14976 NOREF(aState);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14981{
14982 NOREF(aProgress);
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::endPowerUp(LONG aResult)
14987{
14988 NOREF(aResult);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14993{
14994 NOREF(aProgress);
14995 ReturnComNotImplemented();
14996}
14997
14998HRESULT Machine::endPoweringDown(LONG aResult,
14999 const com::Utf8Str &aErrMsg)
15000{
15001 NOREF(aResult);
15002 NOREF(aErrMsg);
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15007 BOOL *aMatched,
15008 ULONG *aMaskedInterfaces)
15009{
15010 NOREF(aDevice);
15011 NOREF(aMatched);
15012 NOREF(aMaskedInterfaces);
15013 ReturnComNotImplemented();
15014
15015}
15016
15017HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15018{
15019 NOREF(aId); NOREF(aCaptureFilename);
15020 ReturnComNotImplemented();
15021}
15022
15023HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15024 BOOL aDone)
15025{
15026 NOREF(aId);
15027 NOREF(aDone);
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::autoCaptureUSBDevices()
15032{
15033 ReturnComNotImplemented();
15034}
15035
15036HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15037{
15038 NOREF(aDone);
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15043 ComPtr<IProgress> &aProgress)
15044{
15045 NOREF(aSession);
15046 NOREF(aProgress);
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::finishOnlineMergeMedium()
15051{
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15056 std::vector<com::Utf8Str> &aValues,
15057 std::vector<LONG64> &aTimestamps,
15058 std::vector<com::Utf8Str> &aFlags)
15059{
15060 NOREF(aNames);
15061 NOREF(aValues);
15062 NOREF(aTimestamps);
15063 NOREF(aFlags);
15064 ReturnComNotImplemented();
15065}
15066
15067HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15068 const com::Utf8Str &aValue,
15069 LONG64 aTimestamp,
15070 const com::Utf8Str &aFlags,
15071 BOOL fWasDeleted)
15072{
15073 NOREF(aName);
15074 NOREF(aValue);
15075 NOREF(aTimestamp);
15076 NOREF(aFlags);
15077 NOREF(fWasDeleted);
15078 ReturnComNotImplemented();
15079}
15080
15081HRESULT Machine::lockMedia()
15082{
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::unlockMedia()
15087{
15088 ReturnComNotImplemented();
15089}
15090
15091HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15092 ComPtr<IMediumAttachment> &aNewAttachment)
15093{
15094 NOREF(aAttachment);
15095 NOREF(aNewAttachment);
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15100 ULONG aCpuUser,
15101 ULONG aCpuKernel,
15102 ULONG aCpuIdle,
15103 ULONG aMemTotal,
15104 ULONG aMemFree,
15105 ULONG aMemBalloon,
15106 ULONG aMemShared,
15107 ULONG aMemCache,
15108 ULONG aPagedTotal,
15109 ULONG aMemAllocTotal,
15110 ULONG aMemFreeTotal,
15111 ULONG aMemBalloonTotal,
15112 ULONG aMemSharedTotal,
15113 ULONG aVmNetRx,
15114 ULONG aVmNetTx)
15115{
15116 NOREF(aValidStats);
15117 NOREF(aCpuUser);
15118 NOREF(aCpuKernel);
15119 NOREF(aCpuIdle);
15120 NOREF(aMemTotal);
15121 NOREF(aMemFree);
15122 NOREF(aMemBalloon);
15123 NOREF(aMemShared);
15124 NOREF(aMemCache);
15125 NOREF(aPagedTotal);
15126 NOREF(aMemAllocTotal);
15127 NOREF(aMemFreeTotal);
15128 NOREF(aMemBalloonTotal);
15129 NOREF(aMemSharedTotal);
15130 NOREF(aVmNetRx);
15131 NOREF(aVmNetTx);
15132 ReturnComNotImplemented();
15133}
15134
15135HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15136 com::Utf8Str &aResult)
15137{
15138 NOREF(aAuthParams);
15139 NOREF(aResult);
15140 ReturnComNotImplemented();
15141}
15142
15143com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15144{
15145 com::Utf8Str strControllerName = "Unknown";
15146 switch (aBusType)
15147 {
15148 case StorageBus_IDE:
15149 {
15150 strControllerName = "IDE";
15151 break;
15152 }
15153 case StorageBus_SATA:
15154 {
15155 strControllerName = "SATA";
15156 break;
15157 }
15158 case StorageBus_SCSI:
15159 {
15160 strControllerName = "SCSI";
15161 break;
15162 }
15163 case StorageBus_Floppy:
15164 {
15165 strControllerName = "Floppy";
15166 break;
15167 }
15168 case StorageBus_SAS:
15169 {
15170 strControllerName = "SAS";
15171 break;
15172 }
15173 case StorageBus_USB:
15174 {
15175 strControllerName = "USB";
15176 break;
15177 }
15178 case StorageBus_PCIe:
15179 {
15180 strControllerName = "PCIe";
15181 break;
15182 }
15183 case StorageBus_VirtioSCSI:
15184 {
15185 strControllerName = "VirtioSCSI";
15186 break;
15187 }
15188 default:
15189 AssertFailed(); /* Catch missing case above. */
15190 break;
15191 }
15192 return strControllerName;
15193}
15194
15195HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15196{
15197 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15198
15199 AutoCaller autoCaller(this);
15200 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15201
15202 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15203 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15204 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15205 if (FAILED(hrc)) return hrc;
15206
15207 NOREF(aFlags);
15208 com::Utf8Str osTypeId;
15209 ComObjPtr<GuestOSType> osType = NULL;
15210
15211 /* Get the guest os type as a string from the VB. */
15212 hrc = getOSTypeId(osTypeId);
15213 if (FAILED(hrc)) return hrc;
15214
15215 /* Get the os type obj that coresponds, can be used to get
15216 * the defaults for this guest OS. */
15217 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15218 if (FAILED(hrc)) return hrc;
15219
15220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15221
15222 mPlatform->i_applyDefaults(osType);
15223
15224 /* This one covers IOAPICEnabled. */
15225 mFirmwareSettings->i_applyDefaults(osType);
15226
15227 /* Initialize default record settings. */
15228 mRecordingSettings->i_applyDefaults();
15229
15230 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15231 if (FAILED(hrc)) return hrc;
15232
15233 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15234 if (FAILED(hrc)) return hrc;
15235
15236 /* Graphics stuff. */
15237 GraphicsControllerType_T graphicsController;
15238 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15239 if (FAILED(hrc)) return hrc;
15240
15241 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15242 if (FAILED(hrc)) return hrc;
15243
15244 ULONG vramSize;
15245 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15246 if (FAILED(hrc)) return hrc;
15247
15248 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15249 if (FAILED(hrc)) return hrc;
15250
15251 BOOL fAccelerate2DVideoEnabled;
15252 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15253 if (FAILED(hrc)) return hrc;
15254
15255 hrc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15256 if (FAILED(hrc)) return hrc;
15257
15258 BOOL fAccelerate3DEnabled;
15259 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15260 if (FAILED(hrc)) return hrc;
15261
15262 hrc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15263 if (FAILED(hrc)) return hrc;
15264
15265 /* Apply network adapters defaults */
15266 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15267 mNetworkAdapters[slot]->i_applyDefaults(osType);
15268
15269#ifdef VBOX_WITH_ARMV8_HARDCODED_DEFAULTS
15270 /* Configuration is done in Machine::init(). */
15271#else
15272 /* Apply serial port defaults */
15273 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15274 mSerialPorts[slot]->i_applyDefaults(osType);
15275#endif
15276
15277 /* Apply parallel port defaults - not OS dependent*/
15278 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15279 mParallelPorts[slot]->i_applyDefaults();
15280
15281 /* This one covers the TPM type. */
15282 mTrustedPlatformModule->i_applyDefaults(osType);
15283
15284 /* This one covers secure boot. */
15285 hrc = mNvramStore->i_applyDefaults(osType);
15286 if (FAILED(hrc)) return hrc;
15287
15288 /* Audio stuff. */
15289 hrc = mAudioSettings->i_applyDefaults(osType);
15290 if (FAILED(hrc)) return hrc;
15291
15292 /* Storage Controllers */
15293 StorageControllerType_T hdStorageControllerType;
15294 StorageBus_T hdStorageBusType;
15295 StorageControllerType_T dvdStorageControllerType;
15296 StorageBus_T dvdStorageBusType;
15297 BOOL recommendedFloppy;
15298 ComPtr<IStorageController> floppyController;
15299 ComPtr<IStorageController> hdController;
15300 ComPtr<IStorageController> dvdController;
15301 Utf8Str strFloppyName, strDVDName, strHDName;
15302
15303 /* GUI auto generates controller names using bus type. Do the same*/
15304 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15305
15306 /* Floppy recommended? add one. */
15307 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15308 if (FAILED(hrc)) return hrc;
15309 if (recommendedFloppy)
15310 {
15311 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15312 if (FAILED(hrc)) return hrc;
15313 }
15314
15315 /* Setup one DVD storage controller. */
15316 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15317 if (FAILED(hrc)) return hrc;
15318
15319 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15320 if (FAILED(hrc)) return hrc;
15321
15322 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15323
15324 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15325 if (FAILED(hrc)) return hrc;
15326
15327 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15328 if (FAILED(hrc)) return hrc;
15329
15330 /* Setup one HDD storage controller. */
15331 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15332 if (FAILED(hrc)) return hrc;
15333
15334 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15335 if (FAILED(hrc)) return hrc;
15336
15337 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15338
15339 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15340 {
15341 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15342 if (FAILED(hrc)) return hrc;
15343
15344 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15345 if (FAILED(hrc)) return hrc;
15346 }
15347 else
15348 {
15349 /* The HD controller is the same as DVD: */
15350 hdController = dvdController;
15351 }
15352
15353 /* Limit the AHCI port count if it's used because windows has trouble with
15354 * too many ports and other guest (OS X in particular) may take extra long
15355 * boot: */
15356
15357 // pParent = static_cast<Medium*>(aP)
15358 IStorageController *temp = hdController;
15359 ComObjPtr<StorageController> storageController;
15360 storageController = static_cast<StorageController *>(temp);
15361
15362 // tempHDController = aHDController;
15363 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15364 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15365 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15366 storageController->COMSETTER(PortCount)(1);
15367
15368#ifdef VBOX_WITH_ARMV8_HARDCODED_DEFAULTS
15369 /* ARM VMs only support VirtioSCSI for now -- set two ports here, one for HDD and one for DVD drive. */
15370 if (hdStorageControllerType == StorageControllerType_VirtioSCSI)
15371 {
15372 hrc = storageController->COMSETTER(PortCount)(2);
15373 if (FAILED(hrc)) return hrc;
15374 }
15375#endif
15376
15377 /* USB stuff */
15378
15379 bool ohciEnabled = false;
15380
15381 ComPtr<IUSBController> usbController;
15382 BOOL recommendedUSB3;
15383 BOOL recommendedUSB;
15384 BOOL usbProxyAvailable;
15385
15386 getUSBProxyAvailable(&usbProxyAvailable);
15387 if (FAILED(hrc)) return hrc;
15388
15389 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15390 if (FAILED(hrc)) return hrc;
15391 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15392 if (FAILED(hrc)) return hrc;
15393
15394 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15395 {
15396 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15397 if (FAILED(hrc)) return hrc;
15398
15399 /* xHci includes OHCI */
15400 ohciEnabled = true;
15401 }
15402 if ( !ohciEnabled
15403 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15404 {
15405 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15406 if (FAILED(hrc)) return hrc;
15407 ohciEnabled = true;
15408
15409 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15410 if (FAILED(hrc)) return hrc;
15411 }
15412
15413 /* Set recommended human interface device types: */
15414 BOOL recommendedUSBHID;
15415 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15416 if (FAILED(hrc)) return hrc;
15417
15418 if (recommendedUSBHID)
15419 {
15420 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15421 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15422 if (!ohciEnabled && !usbDeviceFilters.isNull())
15423 {
15424 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15425 if (FAILED(hrc)) return hrc;
15426 }
15427 }
15428
15429 BOOL recommendedUSBTablet;
15430 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15431 if (FAILED(hrc)) return hrc;
15432
15433 if (recommendedUSBTablet)
15434 {
15435 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15436 if (!ohciEnabled && !usbDeviceFilters.isNull())
15437 {
15438 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15439 if (FAILED(hrc)) return hrc;
15440 }
15441 }
15442
15443 /* Enable the VMMDev testing feature for bootsector VMs: */
15444 if (osTypeId == "VBoxBS_64")
15445 {
15446 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15447 if (FAILED(hrc))
15448 return hrc;
15449 }
15450
15451 return S_OK;
15452}
15453
15454#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15455/**
15456 * Task record for change encryption settins.
15457 */
15458class Machine::ChangeEncryptionTask
15459 : public Machine::Task
15460{
15461public:
15462 ChangeEncryptionTask(Machine *m,
15463 Progress *p,
15464 const Utf8Str &t,
15465 const com::Utf8Str &aCurrentPassword,
15466 const com::Utf8Str &aCipher,
15467 const com::Utf8Str &aNewPassword,
15468 const com::Utf8Str &aNewPasswordId,
15469 const BOOL aForce,
15470 const MediaList &llMedia)
15471 : Task(m, p, t),
15472 mstrNewPassword(aNewPassword),
15473 mstrCurrentPassword(aCurrentPassword),
15474 mstrCipher(aCipher),
15475 mstrNewPasswordId(aNewPasswordId),
15476 mForce(aForce),
15477 mllMedia(llMedia)
15478 {}
15479
15480 ~ChangeEncryptionTask()
15481 {
15482 if (mstrNewPassword.length())
15483 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15484 if (mstrCurrentPassword.length())
15485 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15486 if (m_pCryptoIf)
15487 {
15488 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15489 m_pCryptoIf = NULL;
15490 }
15491 }
15492
15493 Utf8Str mstrNewPassword;
15494 Utf8Str mstrCurrentPassword;
15495 Utf8Str mstrCipher;
15496 Utf8Str mstrNewPasswordId;
15497 BOOL mForce;
15498 MediaList mllMedia;
15499 PCVBOXCRYPTOIF m_pCryptoIf;
15500private:
15501 void handler()
15502 {
15503 try
15504 {
15505 m_pMachine->i_changeEncryptionHandler(*this);
15506 }
15507 catch (...)
15508 {
15509 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
15510 }
15511 }
15512
15513 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
15514};
15515
15516/**
15517 * Scans specified directory and fills list by files found
15518 *
15519 * @returns VBox status code.
15520 * @param lstFiles
15521 * @param strDir
15522 * @param filePattern
15523 */
15524int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
15525 const com::Utf8Str &strPattern)
15526{
15527 /* To get all entries including subdirectories. */
15528 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
15529 if (!pszFilePattern)
15530 return VERR_NO_STR_MEMORY;
15531
15532 PRTDIRENTRYEX pDirEntry = NULL;
15533 RTDIR hDir;
15534 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
15535 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
15536 if (RT_SUCCESS(vrc))
15537 {
15538 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
15539 if (pDirEntry)
15540 {
15541 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
15542 != VERR_NO_MORE_FILES)
15543 {
15544 char *pszFilePath = NULL;
15545
15546 if (vrc == VERR_BUFFER_OVERFLOW)
15547 {
15548 /* allocate new buffer. */
15549 RTMemFree(pDirEntry);
15550 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
15551 if (!pDirEntry)
15552 {
15553 vrc = VERR_NO_MEMORY;
15554 break;
15555 }
15556 /* Retry. */
15557 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
15558 if (RT_FAILURE(vrc))
15559 break;
15560 }
15561 else if (RT_FAILURE(vrc))
15562 break;
15563
15564 /* Exclude . and .. */
15565 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
15566 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
15567 continue;
15568 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
15569 {
15570 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15571 if (!pszSubDirPath)
15572 {
15573 vrc = VERR_NO_STR_MEMORY;
15574 break;
15575 }
15576 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
15577 RTMemFree(pszSubDirPath);
15578 if (RT_FAILURE(vrc))
15579 break;
15580 continue;
15581 }
15582
15583 /* We got the new entry. */
15584 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
15585 continue;
15586
15587 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
15588 continue;
15589
15590 /* Prepend the path to the libraries. */
15591 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15592 if (!pszFilePath)
15593 {
15594 vrc = VERR_NO_STR_MEMORY;
15595 break;
15596 }
15597
15598 lstFiles.push_back(pszFilePath);
15599 RTStrFree(pszFilePath);
15600 }
15601
15602 RTMemFree(pDirEntry);
15603 }
15604 else
15605 vrc = VERR_NO_MEMORY;
15606
15607 RTDirClose(hDir);
15608 }
15609 else
15610 {
15611 /* On Windows the above immediately signals that there are no
15612 * files matching, while on other platforms enumerating the
15613 * files below fails. Either way: stop searching. */
15614 }
15615
15616 if ( vrc == VERR_NO_MORE_FILES
15617 || vrc == VERR_FILE_NOT_FOUND
15618 || vrc == VERR_PATH_NOT_FOUND)
15619 vrc = VINF_SUCCESS;
15620 RTStrFree(pszFilePattern);
15621 return vrc;
15622}
15623
15624/**
15625 * Helper to set up an I/O stream to read or write a possibly encrypted file.
15626 *
15627 * @returns VBox status code.
15628 * @param pszFilename The file to open.
15629 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
15630 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
15631 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
15632 * @param fOpen The open flags for the file.
15633 * @param phVfsIos Where to store the handle to the I/O stream on success.
15634 */
15635int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
15636 const char *pszKeyStore, const char *pszPassword,
15637 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
15638{
15639 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
15640 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
15641 if (RT_SUCCESS(vrc))
15642 {
15643 if (pCryptoIf)
15644 {
15645 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
15646 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
15647 if (RT_SUCCESS(vrc))
15648 {
15649 RTVfsFileRelease(hVfsFile);
15650 hVfsFile = hVfsFileCrypto;
15651 }
15652 }
15653
15654 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
15655 RTVfsFileRelease(hVfsFile);
15656 }
15657
15658 return vrc;
15659}
15660
15661/**
15662 * Helper function processing all actions for one component (saved state files,
15663 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
15664 *
15665 * @param task
15666 * @param strDirectory
15667 * @param strFilePattern
15668 * @param strMagic
15669 * @param strKeyStore
15670 * @param strKeyId
15671 * @return
15672 */
15673HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
15674 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
15675 com::Utf8Str &strKeyId, int iCipherMode)
15676{
15677 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
15678 && task.mstrCipher.isEmpty()
15679 && task.mstrNewPassword.isEmpty()
15680 && task.mstrNewPasswordId.isEmpty();
15681 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
15682 && task.mstrCipher.isNotEmpty()
15683 && task.mstrNewPassword.isNotEmpty()
15684 && task.mstrNewPasswordId.isNotEmpty();
15685
15686 /* check if the cipher is changed which causes the reencryption*/
15687
15688 const char *pszTaskCipher = NULL;
15689 if (task.mstrCipher.isNotEmpty())
15690 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
15691
15692 if (!task.mForce && !fDecrypt && !fEncrypt)
15693 {
15694 char *pszCipher = NULL;
15695 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
15696 NULL /*pszPassword*/,
15697 NULL /*ppbKey*/,
15698 NULL /*pcbKey*/,
15699 &pszCipher);
15700 if (RT_SUCCESS(vrc))
15701 {
15702 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
15703 RTMemFree(pszCipher);
15704 }
15705 else
15706 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
15707 strFilePattern.c_str(), vrc);
15708 }
15709
15710 /* Only the password needs to be changed */
15711 if (!task.mForce && !fDecrypt && !fEncrypt)
15712 {
15713 Assert(task.m_pCryptoIf);
15714
15715 VBOXCRYPTOCTX hCryptoCtx;
15716 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
15717 if (RT_FAILURE(vrc))
15718 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
15719 strFilePattern.c_str(), vrc);
15720 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15721 if (RT_FAILURE(vrc))
15722 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
15723 strFilePattern.c_str(), vrc);
15724
15725 char *pszKeyStore = NULL;
15726 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15727 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15728 if (RT_FAILURE(vrc))
15729 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
15730 strFilePattern.c_str(), vrc);
15731 strKeyStore = pszKeyStore;
15732 RTMemFree(pszKeyStore);
15733 strKeyId = task.mstrNewPasswordId;
15734 return S_OK;
15735 }
15736
15737 /* Reencryption required */
15738 HRESULT hrc = S_OK;
15739 int vrc = VINF_SUCCESS;
15740
15741 std::list<com::Utf8Str> lstFiles;
15742 if (SUCCEEDED(hrc))
15743 {
15744 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
15745 if (RT_FAILURE(vrc))
15746 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
15747 }
15748 com::Utf8Str strNewKeyStore;
15749 if (SUCCEEDED(hrc))
15750 {
15751 if (!fDecrypt)
15752 {
15753 VBOXCRYPTOCTX hCryptoCtx;
15754 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
15755 if (RT_FAILURE(vrc))
15756 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
15757 strFilePattern.c_str(), vrc);
15758
15759 char *pszKeyStore = NULL;
15760 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15761 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15762 if (RT_FAILURE(vrc))
15763 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
15764 strFilePattern.c_str(), vrc);
15765 strNewKeyStore = pszKeyStore;
15766 RTMemFree(pszKeyStore);
15767 }
15768
15769 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15770 it != lstFiles.end();
15771 ++it)
15772 {
15773 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
15774 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
15775
15776 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
15777 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
15778
15779 vrc = i_createIoStreamForFile((*it).c_str(),
15780 fEncrypt ? NULL : task.m_pCryptoIf,
15781 fEncrypt ? NULL : strKeyStore.c_str(),
15782 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
15783 fOpenForRead, &hVfsIosOld);
15784 if (RT_SUCCESS(vrc))
15785 {
15786 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
15787 fDecrypt ? NULL : task.m_pCryptoIf,
15788 fDecrypt ? NULL : strNewKeyStore.c_str(),
15789 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
15790 fOpenForWrite, &hVfsIosNew);
15791 if (RT_FAILURE(vrc))
15792 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
15793 (*it + ".tmp").c_str(), vrc);
15794 }
15795 else
15796 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
15797
15798 if (RT_SUCCESS(vrc))
15799 {
15800 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
15801 if (RT_FAILURE(vrc))
15802 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
15803 (*it).c_str(), vrc);
15804 }
15805
15806 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
15807 RTVfsIoStrmRelease(hVfsIosOld);
15808 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
15809 RTVfsIoStrmRelease(hVfsIosNew);
15810 }
15811 }
15812
15813 if (SUCCEEDED(hrc))
15814 {
15815 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15816 it != lstFiles.end();
15817 ++it)
15818 {
15819 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
15820 if (RT_FAILURE(vrc))
15821 {
15822 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
15823 break;
15824 }
15825 }
15826 }
15827
15828 if (SUCCEEDED(hrc))
15829 {
15830 strKeyStore = strNewKeyStore;
15831 strKeyId = task.mstrNewPasswordId;
15832 }
15833
15834 return hrc;
15835}
15836
15837/**
15838 * Task thread implementation for Machine::changeEncryption(), called from
15839 * Machine::taskHandler().
15840 *
15841 * @note Locks this object for writing.
15842 *
15843 * @param task
15844 * @return
15845 */
15846void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
15847{
15848 LogFlowThisFuncEnter();
15849
15850 AutoCaller autoCaller(this);
15851 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
15852 if (FAILED(autoCaller.hrc()))
15853 {
15854 /* we might have been uninitialized because the session was accidentally
15855 * closed by the client, so don't assert */
15856 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
15857 task.m_pProgress->i_notifyComplete(hrc);
15858 LogFlowThisFuncLeave();
15859 return;
15860 }
15861
15862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15863
15864 HRESULT hrc = S_OK;
15865 com::Utf8Str strOldKeyId = mData->mstrKeyId;
15866 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
15867 try
15868 {
15869 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
15870 if (FAILED(hrc))
15871 throw hrc;
15872
15873 if (task.mstrCurrentPassword.isEmpty())
15874 {
15875 if (mData->mstrKeyStore.isNotEmpty())
15876 throw setError(VBOX_E_PASSWORD_INCORRECT,
15877 tr("The password given for the encrypted VM is incorrect"));
15878 }
15879 else
15880 {
15881 if (mData->mstrKeyStore.isEmpty())
15882 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15883 tr("The VM is not configured for encryption"));
15884 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
15885 if (hrc == VBOX_E_PASSWORD_INCORRECT)
15886 throw setError(VBOX_E_PASSWORD_INCORRECT,
15887 tr("The password to decrypt the VM is incorrect"));
15888 }
15889
15890 if (task.mstrCipher.isNotEmpty())
15891 {
15892 if ( task.mstrNewPassword.isEmpty()
15893 && task.mstrNewPasswordId.isEmpty()
15894 && task.mstrCurrentPassword.isNotEmpty())
15895 {
15896 /* An empty password and password ID will default to the current password. */
15897 task.mstrNewPassword = task.mstrCurrentPassword;
15898 }
15899 else if (task.mstrNewPassword.isEmpty())
15900 throw setError(VBOX_E_OBJECT_NOT_FOUND,
15901 tr("A password must be given for the VM encryption"));
15902 else if (task.mstrNewPasswordId.isEmpty())
15903 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15904 tr("A valid identifier for the password must be given"));
15905 }
15906 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
15907 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15908 tr("The password and password identifier must be empty if the output should be unencrypted"));
15909
15910 /*
15911 * Save config.
15912 * Must be first operation to prevent making encrypted copies
15913 * for old version of the config file.
15914 */
15915 int fSave = Machine::SaveS_Force;
15916 if (task.mstrNewPassword.isNotEmpty())
15917 {
15918 VBOXCRYPTOCTX hCryptoCtx;
15919
15920 int vrc = VINF_SUCCESS;
15921 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
15922 {
15923 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
15924 task.mstrNewPassword.c_str(), &hCryptoCtx);
15925 if (RT_FAILURE(vrc))
15926 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
15927 }
15928 else
15929 {
15930 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
15931 task.mstrCurrentPassword.c_str(),
15932 &hCryptoCtx);
15933 if (RT_FAILURE(vrc))
15934 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
15935 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15936 if (RT_FAILURE(vrc))
15937 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
15938 }
15939
15940 char *pszKeyStore;
15941 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15942 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15943 if (RT_FAILURE(vrc))
15944 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
15945 mData->mstrKeyStore = pszKeyStore;
15946 RTStrFree(pszKeyStore);
15947 mData->mstrKeyId = task.mstrNewPasswordId;
15948 size_t cbPassword = task.mstrNewPassword.length() + 1;
15949 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
15950 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
15951 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
15952 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
15953
15954 /*
15955 * Remove backuped config after saving because it can contain
15956 * unencrypted version of the config
15957 */
15958 fSave |= Machine::SaveS_RemoveBackup;
15959 }
15960 else
15961 {
15962 mData->mstrKeyId.setNull();
15963 mData->mstrKeyStore.setNull();
15964 }
15965
15966 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
15967 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
15968 Bstr bstrNewPassword(task.mstrNewPassword);
15969 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
15970 /* encrypt media */
15971 alock.release();
15972 for (MediaList::iterator it = task.mllMedia.begin();
15973 it != task.mllMedia.end();
15974 ++it)
15975 {
15976 ComPtr<IProgress> pProgress1;
15977 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
15978 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
15979 pProgress1.asOutParam());
15980 if (FAILED(hrc)) throw hrc;
15981 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
15982 if (FAILED(hrc)) throw hrc;
15983 }
15984 alock.acquire();
15985
15986 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
15987
15988 Utf8Str strFullSnapshotFolder;
15989 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
15990
15991 /* .sav files (main and snapshots) */
15992 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
15993 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
15994 if (FAILED(hrc))
15995 /* the helper function already sets error object */
15996 throw hrc;
15997
15998 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
15999
16000 /* .nvram files */
16001 com::Utf8Str strNVRAMKeyId;
16002 com::Utf8Str strNVRAMKeyStore;
16003 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16004 if (FAILED(hrc))
16005 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16006
16007 Utf8Str strMachineFolder;
16008 i_calculateFullPath(".", strMachineFolder);
16009
16010 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16011 if (FAILED(hrc))
16012 /* the helper function already sets error object */
16013 throw hrc;
16014
16015 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16016 if (FAILED(hrc))
16017 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16018
16019 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16020
16021 /* .log files */
16022 com::Utf8Str strLogFolder;
16023 i_getLogFolder(strLogFolder);
16024 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16025 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16026 if (FAILED(hrc))
16027 /* the helper function already sets error object */
16028 throw hrc;
16029
16030 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16031
16032 i_saveSettings(NULL, alock, fSave);
16033 }
16034 catch (HRESULT hrcXcpt)
16035 {
16036 hrc = hrcXcpt;
16037 mData->mstrKeyId = strOldKeyId;
16038 mData->mstrKeyStore = strOldKeyStore;
16039 }
16040
16041 task.m_pProgress->i_notifyComplete(hrc);
16042
16043 LogFlowThisFuncLeave();
16044}
16045#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16046
16047HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16048 const com::Utf8Str &aCipher,
16049 const com::Utf8Str &aNewPassword,
16050 const com::Utf8Str &aNewPasswordId,
16051 BOOL aForce,
16052 ComPtr<IProgress> &aProgress)
16053{
16054 LogFlowFuncEnter();
16055
16056#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16057 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16058 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16059#else
16060 /* make the VM accessible */
16061 if (!mData->mAccessible)
16062 {
16063 if ( aCurrentPassword.isEmpty()
16064 || mData->mstrKeyId.isEmpty())
16065 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16066
16067 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16068 if (FAILED(hrc))
16069 return hrc;
16070 }
16071
16072 AutoLimitedCaller autoCaller(this);
16073 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16074
16075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16076
16077 /* define media to be change encryption */
16078
16079 MediaList llMedia;
16080 for (MediumAttachmentList::iterator
16081 it = mMediumAttachments->begin();
16082 it != mMediumAttachments->end();
16083 ++it)
16084 {
16085 ComObjPtr<MediumAttachment> &pAttach = *it;
16086 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16087
16088 if (!pMedium.isNull())
16089 {
16090 AutoCaller mac(pMedium);
16091 if (FAILED(mac.hrc())) return mac.hrc();
16092 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16093 DeviceType_T devType = pMedium->i_getDeviceType();
16094 if (devType == DeviceType_HardDisk)
16095 {
16096 /*
16097 * We need to move to last child because the Medium::changeEncryption
16098 * encrypts all chain of specified medium with its parents.
16099 * Also we perform cheking of back reference and children for
16100 * all media in the chain to raise error before we start any action.
16101 * So, we first move into root parent and then we will move to last child
16102 * keeping latter in the list for encryption.
16103 */
16104
16105 /* move to root parent */
16106 ComObjPtr<Medium> pTmpMedium = pMedium;
16107 while (pTmpMedium.isNotNull())
16108 {
16109 AutoCaller mediumAC(pTmpMedium);
16110 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16111 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16112
16113 /* Cannot encrypt media which are attached to more than one virtual machine. */
16114 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16115 if (cBackRefs > 1)
16116 return setError(VBOX_E_INVALID_OBJECT_STATE,
16117 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16118 pTmpMedium->i_getName().c_str(), cBackRefs);
16119
16120 size_t cChildren = pTmpMedium->i_getChildren().size();
16121 if (cChildren > 1)
16122 return setError(VBOX_E_INVALID_OBJECT_STATE,
16123 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16124 pTmpMedium->i_getName().c_str(), cChildren);
16125
16126 pTmpMedium = pTmpMedium->i_getParent();
16127 }
16128 /* move to last child */
16129 pTmpMedium = pMedium;
16130 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16131 {
16132 AutoCaller mediumAC(pTmpMedium);
16133 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16134 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16135
16136 /* Cannot encrypt media which are attached to more than one virtual machine. */
16137 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16138 if (cBackRefs > 1)
16139 return setError(VBOX_E_INVALID_OBJECT_STATE,
16140 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16141 pTmpMedium->i_getName().c_str(), cBackRefs);
16142
16143 size_t cChildren = pTmpMedium->i_getChildren().size();
16144 if (cChildren > 1)
16145 return setError(VBOX_E_INVALID_OBJECT_STATE,
16146 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16147 pTmpMedium->i_getName().c_str(), cChildren);
16148
16149 pTmpMedium = pTmpMedium->i_getChildren().front();
16150 }
16151 llMedia.push_back(pTmpMedium);
16152 }
16153 }
16154 }
16155
16156 ComObjPtr<Progress> pProgress;
16157 pProgress.createObject();
16158 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16159 static_cast<IMachine*>(this) /* aInitiator */,
16160 tr("Change encryption"),
16161 TRUE /* fCancellable */,
16162 (ULONG)(4 + + llMedia.size()), // cOperations
16163 tr("Change encryption of the mediuma"));
16164 if (FAILED(hrc))
16165 return hrc;
16166
16167 /* create and start the task on a separate thread (note that it will not
16168 * start working until we release alock) */
16169 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16170 aCurrentPassword, aCipher, aNewPassword,
16171 aNewPasswordId, aForce, llMedia);
16172 hrc = pTask->createThread();
16173 pTask = NULL;
16174 if (FAILED(hrc))
16175 return hrc;
16176
16177 pProgress.queryInterfaceTo(aProgress.asOutParam());
16178
16179 LogFlowFuncLeave();
16180
16181 return S_OK;
16182#endif
16183}
16184
16185HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16186 com::Utf8Str &aPasswordId)
16187{
16188#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16189 RT_NOREF(aCipher, aPasswordId);
16190 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16191#else
16192 AutoLimitedCaller autoCaller(this);
16193 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16194
16195 PCVBOXCRYPTOIF pCryptoIf = NULL;
16196 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16197 if (FAILED(hrc)) return hrc; /* Error is set */
16198
16199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16200
16201 if (mData->mstrKeyStore.isNotEmpty())
16202 {
16203 char *pszCipher = NULL;
16204 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16205 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16206 if (RT_SUCCESS(vrc))
16207 {
16208 aCipher = getCipherStringWithoutMode(pszCipher);
16209 RTStrFree(pszCipher);
16210 aPasswordId = mData->mstrKeyId;
16211 }
16212 else
16213 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16214 tr("Failed to query the encryption settings with %Rrc"),
16215 vrc);
16216 }
16217 else
16218 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16219
16220 mParent->i_releaseCryptoIf(pCryptoIf);
16221
16222 return hrc;
16223#endif
16224}
16225
16226HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16227{
16228#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16229 RT_NOREF(aPassword);
16230 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16231#else
16232 AutoLimitedCaller autoCaller(this);
16233 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16234
16235 PCVBOXCRYPTOIF pCryptoIf = NULL;
16236 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16237 if (FAILED(hrc)) return hrc; /* Error is set */
16238
16239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16240
16241 if (mData->mstrKeyStore.isNotEmpty())
16242 {
16243 char *pszCipher = NULL;
16244 uint8_t *pbDek = NULL;
16245 size_t cbDek = 0;
16246 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16247 &pbDek, &cbDek, &pszCipher);
16248 if (RT_SUCCESS(vrc))
16249 {
16250 RTStrFree(pszCipher);
16251 RTMemSaferFree(pbDek, cbDek);
16252 }
16253 else
16254 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16255 tr("The password supplied for the encrypted machine is incorrect"));
16256 }
16257 else
16258 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16259
16260 mParent->i_releaseCryptoIf(pCryptoIf);
16261
16262 return hrc;
16263#endif
16264}
16265
16266HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16267 const com::Utf8Str &aPassword)
16268{
16269#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16270 RT_NOREF(aId, aPassword);
16271 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16272#else
16273 AutoLimitedCaller autoCaller(this);
16274 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16275
16276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16277
16278 size_t cbPassword = aPassword.length() + 1;
16279 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16280
16281 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16282
16283 if ( mData->mAccessible
16284 && mData->mSession.mState == SessionState_Locked
16285 && mData->mSession.mLockType == LockType_VM
16286 && mData->mSession.mDirectControl != NULL)
16287 {
16288 /* get the console from the direct session */
16289 ComPtr<IConsole> console;
16290 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16291 ComAssertComRC(hrc);
16292 /* send passsword to console */
16293 console->AddEncryptionPassword(Bstr(aId).raw(),
16294 Bstr(aPassword).raw(),
16295 TRUE);
16296 }
16297
16298 if (mData->mstrKeyId == aId)
16299 {
16300 HRESULT hrc = checkEncryptionPassword(aPassword);
16301 if (FAILED(hrc))
16302 return hrc;
16303
16304 if (SUCCEEDED(hrc))
16305 {
16306 /*
16307 * Encryption is used and password is correct,
16308 * Reinit the machine if required.
16309 */
16310 BOOL fAccessible;
16311 alock.release();
16312 getAccessible(&fAccessible);
16313 alock.acquire();
16314 }
16315 }
16316
16317 /*
16318 * Add the password into the NvramStore only after
16319 * the machine becomes accessible and the NvramStore
16320 * contains key id and key store.
16321 */
16322 if (mNvramStore.isNotNull())
16323 mNvramStore->i_addPassword(aId, aPassword);
16324
16325 return S_OK;
16326#endif
16327}
16328
16329HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16330 const std::vector<com::Utf8Str> &aPasswords)
16331{
16332#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16333 RT_NOREF(aIds, aPasswords);
16334 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16335#else
16336 if (aIds.size() != aPasswords.size())
16337 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16338
16339 HRESULT hrc = S_OK;
16340 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16341 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16342
16343 return hrc;
16344#endif
16345}
16346
16347HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16348{
16349#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16350 RT_NOREF(autoCaller, aId);
16351 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16352#else
16353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16354
16355 if ( mData->mAccessible
16356 && mData->mSession.mState == SessionState_Locked
16357 && mData->mSession.mLockType == LockType_VM
16358 && mData->mSession.mDirectControl != NULL)
16359 {
16360 /* get the console from the direct session */
16361 ComPtr<IConsole> console;
16362 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16363 ComAssertComRC(hrc);
16364 /* send passsword to console */
16365 console->RemoveEncryptionPassword(Bstr(aId).raw());
16366 }
16367
16368 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16369 {
16370 if (Global::IsOnlineOrTransient(mData->mMachineState))
16371 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16372 alock.release();
16373 autoCaller.release();
16374 /* return because all passwords are purged when machine becomes inaccessible; */
16375 return i_setInaccessible();
16376 }
16377
16378 if (mNvramStore.isNotNull())
16379 mNvramStore->i_removePassword(aId);
16380 mData->mpKeyStore->deleteSecretKey(aId);
16381 return S_OK;
16382#endif
16383}
16384
16385HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16386{
16387#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16388 RT_NOREF(autoCaller);
16389 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16390#else
16391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16392
16393 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16394 {
16395 if (Global::IsOnlineOrTransient(mData->mMachineState))
16396 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16397 alock.release();
16398 autoCaller.release();
16399 /* return because all passwords are purged when machine becomes inaccessible; */
16400 return i_setInaccessible();
16401 }
16402
16403 mNvramStore->i_removeAllPasswords();
16404 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16405 return S_OK;
16406#endif
16407}
16408
16409#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16410HRESULT Machine::i_setInaccessible()
16411{
16412 if (!mData->mAccessible)
16413 return S_OK;
16414
16415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16416 VirtualBox *pParent = mParent;
16417 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16418 Guid id(i_getId());
16419
16420 alock.release();
16421
16422 uninit();
16423 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16424
16425 alock.acquire();
16426 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16427 return hrc;
16428}
16429#endif
16430
16431/* This isn't handled entirely by the wrapper generator yet. */
16432#ifdef VBOX_WITH_XPCOM
16433NS_DECL_CLASSINFO(SessionMachine)
16434NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16435
16436NS_DECL_CLASSINFO(SnapshotMachine)
16437NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16438#endif
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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