VirtualBox

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

Last change on this file since 102869 was 102455, checked in by vboxsync, 12 months ago

Main: Removed some more BUGBUG entries and documented empty / non-implemented code areas where needed. bugref:10384

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 580.6 KB
Line 
1/* $Id: MachineImpl.cpp 102455 2023-12-04 15:53:11Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "StorageControllerImpl.h"
61#include "MachineImplMoveVM.h"
62#include "ExtPackManagerImpl.h"
63#include "MachineLaunchVMCommonWorker.h"
64#include "CryptoUtils.h"
65
66// generated header
67#include "VBoxEvents.h"
68
69#ifdef VBOX_WITH_USB
70# include "USBProxyService.h"
71#endif
72
73#include "AutoCaller.h"
74#include "HashedPw.h"
75#include "Performance.h"
76#include "StringifyEnums.h"
77
78#include <iprt/asm.h>
79#include <iprt/path.h>
80#include <iprt/dir.h>
81#include <iprt/env.h>
82#include <iprt/lockvalidator.h>
83#include <iprt/memsafer.h>
84#include <iprt/process.h>
85#include <iprt/cpp/utils.h>
86#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
87#include <iprt/sha.h>
88#include <iprt/string.h>
89#include <iprt/ctype.h>
90
91#include <VBox/com/array.h>
92#include <VBox/com/list.h>
93#include <VBox/VBoxCryptoIf.h>
94
95#include <VBox/err.h>
96#include <VBox/param.h>
97#include <VBox/settings.h>
98#include <VBox/VMMDev.h>
99#include <VBox/vmm/ssm.h>
100
101#ifdef VBOX_WITH_GUEST_PROPS
102# include <VBox/HostServices/GuestPropertySvc.h>
103# include <VBox/com/array.h>
104#endif
105
106#ifdef VBOX_WITH_SHARED_CLIPBOARD
107# include <VBox/HostServices/VBoxClipboardSvc.h>
108#endif
109
110#include "VBox/com/MultiResult.h"
111
112#include <algorithm>
113
114#ifdef VBOX_WITH_DTRACE_R3_MAIN
115# include "dtrace/VBoxAPI.h"
116#endif
117
118#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
119# define HOSTSUFF_EXE ".exe"
120#else /* !RT_OS_WINDOWS */
121# define HOSTSUFF_EXE ""
122#endif /* !RT_OS_WINDOWS */
123
124// defines / prototypes
125/////////////////////////////////////////////////////////////////////////////
126
127#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
128# define BUF_DATA_SIZE _64K
129
130enum CipherMode
131{
132 CipherModeGcm = 0,
133 CipherModeCtr,
134 CipherModeXts,
135 CipherModeMax
136};
137
138enum AesSize
139{
140 Aes128 = 0,
141 Aes256,
142 AesMax
143};
144
145const char *g_apszCipher[AesMax][CipherModeMax] =
146{
147 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
148 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
149};
150const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
151
152static const char *getCipherString(const char *pszAlgo, const int iMode)
153{
154 if (iMode >= CipherModeMax)
155 return pszAlgo;
156
157 for (int i = 0; i < AesMax; i++)
158 {
159 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
160 return g_apszCipher[i][iMode];
161 }
162 return pszAlgo;
163}
164
165static const char *getCipherStringWithoutMode(const char *pszAlgo)
166{
167 for (int i = 0; i < AesMax; i++)
168 {
169 for (int j = 0; j < CipherModeMax; j++)
170 {
171 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
172 return g_apszCipherAlgo[i];
173 }
174 }
175 return pszAlgo;
176}
177#endif
178
179/////////////////////////////////////////////////////////////////////////////
180// Machine::Data structure
181/////////////////////////////////////////////////////////////////////////////
182
183Machine::Data::Data()
184{
185 mRegistered = FALSE;
186 pMachineConfigFile = NULL;
187 /* Contains hints on what has changed when the user is using the VM (config
188 * changes, running the VM, ...). This is used to decide if a config needs
189 * to be written to disk. */
190 flModifications = 0;
191 /* VM modification usually also trigger setting the current state to
192 * "Modified". Although this is not always the case. An e.g. is the VM
193 * initialization phase or when snapshot related data is changed. The
194 * actually behavior is controlled by the following flag. */
195 m_fAllowStateModification = false;
196 mAccessible = FALSE;
197 /* mUuid is initialized in Machine::init() */
198
199 mMachineState = MachineState_PoweredOff;
200 RTTimeNow(&mLastStateChange);
201
202 mMachineStateDeps = 0;
203 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
204 mMachineStateChangePending = 0;
205
206 mCurrentStateModified = TRUE;
207 mGuestPropertiesModified = FALSE;
208
209 mSession.mPID = NIL_RTPROCESS;
210 mSession.mLockType = LockType_Null;
211 mSession.mState = SessionState_Unlocked;
212
213#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
214 mpKeyStore = NULL;
215#endif
216}
217
218Machine::Data::~Data()
219{
220 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
221 {
222 RTSemEventMultiDestroy(mMachineStateDepsSem);
223 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
224 }
225 if (pMachineConfigFile)
226 {
227 delete pMachineConfigFile;
228 pMachineConfigFile = NULL;
229 }
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine::HWData structure
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::HWData::HWData()
237{
238 /* default values for a newly created machine for x86. */
239 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
240 mMemorySize = 128;
241 mCPUCount = 1;
242 mCPUHotPlugEnabled = false;
243 mMemoryBalloonSize = 0;
244 mPageFusionEnabled = false;
245 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
246 mCpuIdPortabilityLevel = 0;
247 mCpuProfile = "host";
248
249 /* default boot order: floppy - DVD - HDD */
250 mBootOrder[0] = DeviceType_Floppy;
251 mBootOrder[1] = DeviceType_DVD;
252 mBootOrder[2] = DeviceType_HardDisk;
253 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
254 mBootOrder[i] = DeviceType_Null;
255
256 mClipboardMode = ClipboardMode_Disabled;
257 mClipboardFileTransfersEnabled = FALSE;
258
259 mDnDMode = DnDMode_Disabled;
260
261 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
262 mPointingHIDType = PointingHIDType_PS2Mouse;
263 mParavirtProvider = ParavirtProvider_Default;
264 mEmulatedUSBCardReaderEnabled = FALSE;
265
266 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
267 mCPUAttached[i] = false;
268
269 mIOCacheEnabled = true;
270 mIOCacheSize = 5; /* 5MB */
271}
272
273Machine::HWData::~HWData()
274{
275}
276
277/////////////////////////////////////////////////////////////////////////////
278// Machine class
279/////////////////////////////////////////////////////////////////////////////
280
281// constructor / destructor
282/////////////////////////////////////////////////////////////////////////////
283
284Machine::Machine() :
285#ifdef VBOX_WITH_RESOURCE_USAGE_API
286 mCollectorGuest(NULL),
287#endif
288 mPeer(NULL),
289 mParent(NULL),
290 mSerialPorts(),
291 mParallelPorts(),
292 uRegistryNeedsSaving(0)
293{}
294
295Machine::~Machine()
296{}
297
298HRESULT Machine::FinalConstruct()
299{
300 LogFlowThisFunc(("\n"));
301 return BaseFinalConstruct();
302}
303
304void Machine::FinalRelease()
305{
306 LogFlowThisFunc(("\n"));
307 uninit();
308 BaseFinalRelease();
309}
310
311/**
312 * Initializes a new machine instance; this init() variant creates a new, empty machine.
313 * This gets called from VirtualBox::CreateMachine().
314 *
315 * @param aParent Associated parent object.
316 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
317 * @param strName Name for the machine.
318 * @param aArchitecture Architecture to use for the machine.
319 * If a valid guest OS type is set via \a aOsType, the guest OS' type will be used instead then.
320 * @param llGroups list of groups for the machine.
321 * @param strOsType OS Type string (stored as is if aOsType is NULL).
322 * @param aOsType OS Type of this machine or NULL.
323 * @param aId UUID for the new machine.
324 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
325 * @param fDirectoryIncludesUUID
326 * Whether the use a special VM directory naming scheme (includes the UUID).
327 * @param aCipher The cipher to encrypt the VM with.
328 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
329 * @param aPassword The password to encrypt the VM with.
330 *
331 * @return Success indicator. if not S_OK, the machine object is invalid.
332 */
333HRESULT Machine::init(VirtualBox *aParent,
334 const Utf8Str &strConfigFile,
335 const Utf8Str &strName,
336 PlatformArchitecture_T aArchitecture,
337 const StringsList &llGroups,
338 const Utf8Str &strOsType,
339 GuestOSType *aOsType,
340 const Guid &aId,
341 bool fForceOverwrite,
342 bool fDirectoryIncludesUUID,
343 const com::Utf8Str &aCipher,
344 const com::Utf8Str &aPasswordId,
345 const com::Utf8Str &aPassword)
346{
347 LogFlowThisFuncEnter();
348 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
349
350#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
351 RT_NOREF(aCipher);
352 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
353 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
354#endif
355
356 /* Enclose the state transition NotReady->InInit->Ready */
357 AutoInitSpan autoInitSpan(this);
358 AssertReturn(autoInitSpan.isOk(), E_FAIL);
359
360 HRESULT hrc = initImpl(aParent, strConfigFile);
361 if (FAILED(hrc)) return hrc;
362
363#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
364 com::Utf8Str strSsmKeyId;
365 com::Utf8Str strSsmKeyStore;
366 com::Utf8Str strNVRAMKeyId;
367 com::Utf8Str strNVRAMKeyStore;
368
369 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
370 {
371 /* Resolve the cryptographic interface. */
372 PCVBOXCRYPTOIF pCryptoIf = NULL;
373 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
374 if (SUCCEEDED(hrc))
375 {
376 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
377 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
378 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
379
380 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
381 {
382 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
383 if (!pszCipher)
384 {
385 hrc = setError(VBOX_E_NOT_SUPPORTED,
386 tr("The cipher '%s' is not supported"), aCipher.c_str());
387 break;
388 }
389
390 VBOXCRYPTOCTX hCryptoCtx;
391 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
392 if (RT_FAILURE(vrc))
393 {
394 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
395 break;
396 }
397
398 char *pszKeyStore;
399 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
400 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
401 AssertRC(vrc2);
402
403 if (RT_FAILURE(vrc))
404 {
405 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
406 break;
407 }
408
409 *(astrKeyStore[i]) = pszKeyStore;
410 RTMemFree(pszKeyStore);
411 *(astrKeyId[i]) = aPasswordId;
412 }
413
414 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
415 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
416
417 if (FAILED(hrc))
418 return hrc; /* Error is set. */
419 }
420 else
421 return hrc; /* Error is set. */
422 }
423#endif
424
425 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
426 if (FAILED(hrc)) return hrc;
427
428 if (SUCCEEDED(hrc))
429 {
430 // create an empty machine config
431 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
432
433 hrc = initDataAndChildObjects();
434 }
435
436 if (SUCCEEDED(hrc))
437 {
438#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
439 mSSData->strStateKeyId = strSsmKeyId;
440 mSSData->strStateKeyStore = strSsmKeyStore;
441#endif
442
443 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
444 mData->mAccessible = TRUE;
445
446 unconst(mData->mUuid) = aId;
447
448 mUserData->s.strName = strName;
449
450 if (llGroups.size())
451 mUserData->s.llGroups = llGroups;
452
453 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
454 // the "name sync" flag determines whether the machine directory gets renamed along
455 // with the machine file; say so if the settings file name is the same as the
456 // settings file parent directory (machine directory)
457 mUserData->s.fNameSync = i_isInOwnDir();
458
459 // initialize the default snapshots folder
460 hrc = COMSETTER(SnapshotFolder)(NULL);
461 AssertComRC(hrc);
462
463 /* Use the platform architecture which was handed-in by default. */
464 PlatformArchitecture_T enmPlatformArch = aArchitecture;
465
466 if (aOsType)
467 {
468 /* Store OS type */
469 mUserData->s.strOsType = aOsType->i_id();
470
471 /* Use the platform architecture of the found guest OS type. */
472 enmPlatformArch = aOsType->i_platformArchitecture();
473 }
474 else if (!strOsType.isEmpty())
475 {
476 /* Store OS type */
477 mUserData->s.strOsType = strOsType;
478 }
479
480 /* Set the platform architecture first before applying the defaults below. */
481 hrc = mPlatform->i_initArchitecture(enmPlatformArch);
482 if (FAILED(hrc)) return hrc;
483
484 /* Apply platform defaults. */
485 mPlatform->i_applyDefaults(aOsType);
486 i_platformPropertiesUpdate();
487
488 /* Apply firmware defaults. */
489 mFirmwareSettings->i_applyDefaults(aOsType);
490
491 /* Apply TPM defaults. */
492 mTrustedPlatformModule->i_applyDefaults(aOsType);
493
494 /* Apply recording defaults. */
495 mRecordingSettings->i_applyDefaults();
496
497 /* Apply network adapters defaults */
498 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
499 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
500
501 /* Apply serial port defaults */
502 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
503 mSerialPorts[slot]->i_applyDefaults(aOsType);
504
505 /* Apply parallel port defaults */
506 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
507 mParallelPorts[slot]->i_applyDefaults();
508
509 /* Enable the VMMDev testing feature for bootsector VMs: */
510 if (aOsType && aOsType->i_id() == GUEST_OS_ID_STR_X64("VBoxBS"))
511 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
512
513#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
514 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
515#endif
516 if (SUCCEEDED(hrc))
517 {
518 /* At this point the changing of the current state modification
519 * flag is allowed. */
520 i_allowStateModification();
521
522 /* commit all changes made during the initialization */
523 i_commit();
524 }
525 }
526
527 /* Confirm a successful initialization when it's the case */
528 if (SUCCEEDED(hrc))
529 {
530#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
531 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
532 {
533 size_t cbPassword = aPassword.length() + 1;
534 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
535 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
536 }
537#endif
538
539 if (mData->mAccessible)
540 autoInitSpan.setSucceeded();
541 else
542 autoInitSpan.setLimited();
543 }
544
545 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
546 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
547 mData->mRegistered,
548 mData->mAccessible,
549 hrc));
550
551 LogFlowThisFuncLeave();
552
553 return hrc;
554}
555
556/**
557 * Initializes a new instance with data from machine XML (formerly Init_Registered).
558 * Gets called in two modes:
559 *
560 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
561 * UUID is specified and we mark the machine as "registered";
562 *
563 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
564 * and the machine remains unregistered until RegisterMachine() is called.
565 *
566 * @param aParent Associated parent object
567 * @param strConfigFile Local file system path to the VM settings file (can
568 * be relative to the VirtualBox config directory).
569 * @param aId UUID of the machine or NULL (see above).
570 * @param strPassword Password for decrypting the config
571 *
572 * @return Success indicator. if not S_OK, the machine object is invalid
573 */
574HRESULT Machine::initFromSettings(VirtualBox *aParent,
575 const Utf8Str &strConfigFile,
576 const Guid *aId,
577 const com::Utf8Str &strPassword)
578{
579 LogFlowThisFuncEnter();
580 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
581
582 PCVBOXCRYPTOIF pCryptoIf = NULL;
583#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
584 if (strPassword.isNotEmpty())
585 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
586#else
587 if (strPassword.isNotEmpty())
588 {
589 /* Get at the crpytographic interface. */
590 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
591 if (FAILED(hrc))
592 return hrc; /* Error is set. */
593 }
594#endif
595
596 /* Enclose the state transition NotReady->InInit->Ready */
597 AutoInitSpan autoInitSpan(this);
598 AssertReturn(autoInitSpan.isOk(), E_FAIL);
599
600 HRESULT hrc = initImpl(aParent, strConfigFile);
601 if (FAILED(hrc)) return hrc;
602
603 if (aId)
604 {
605 // loading a registered VM:
606 unconst(mData->mUuid) = *aId;
607 mData->mRegistered = TRUE;
608 // now load the settings from XML:
609 hrc = i_registeredInit();
610 // this calls initDataAndChildObjects() and loadSettings()
611 }
612 else
613 {
614 // opening an unregistered VM (VirtualBox::OpenMachine()):
615 hrc = initDataAndChildObjects();
616 if (SUCCEEDED(hrc))
617 {
618 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
619 mData->mAccessible = TRUE;
620
621 try
622 {
623 // load and parse machine XML; this will throw on XML or logic errors
624 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
625 pCryptoIf,
626 strPassword.c_str());
627
628 // reject VM UUID duplicates, they can happen if someone
629 // tries to register an already known VM config again
630 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
631 true /* fPermitInaccessible */,
632 false /* aDoSetError */,
633 NULL) != VBOX_E_OBJECT_NOT_FOUND)
634 {
635 throw setError(E_FAIL,
636 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
637 mData->m_strConfigFile.c_str());
638 }
639
640 // use UUID from machine config
641 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
642
643#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
644 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
645 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
646 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
647 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
648#endif
649
650 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
651 {
652 // We just set the inaccessible state and fill the error info allowing the caller
653 // to register the machine with encrypted config even if the password is incorrect
654 mData->mAccessible = FALSE;
655
656 /* fetch the current error info */
657 mData->mAccessError = com::ErrorInfo();
658
659 setError(VBOX_E_PASSWORD_INCORRECT,
660 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
661 mData->pMachineConfigFile->uuid.raw());
662 }
663 else
664 {
665#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
666 if (strPassword.isNotEmpty())
667 {
668 size_t cbKey = strPassword.length() + 1; /* Include terminator */
669 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
670 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
671 }
672#endif
673
674 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
675 if (FAILED(hrc)) throw hrc;
676
677 /* At this point the changing of the current state modification
678 * flag is allowed. */
679 i_allowStateModification();
680
681 i_commit();
682 }
683 }
684 catch (HRESULT err)
685 {
686 /* we assume that error info is set by the thrower */
687 hrc = err;
688 }
689 catch (...)
690 {
691 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
692 }
693 }
694 }
695
696 /* Confirm a successful initialization when it's the case */
697 if (SUCCEEDED(hrc))
698 {
699 if (mData->mAccessible)
700 autoInitSpan.setSucceeded();
701 else
702 {
703 autoInitSpan.setLimited();
704
705 // uninit media from this machine's media registry, or else
706 // reloading the settings will fail
707 mParent->i_unregisterMachineMedia(i_getId());
708 }
709 }
710
711#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
712 if (pCryptoIf)
713 {
714 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
715 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
716 }
717#endif
718
719 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
720 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
721
722 LogFlowThisFuncLeave();
723
724 return hrc;
725}
726
727/**
728 * Initializes a new instance from a machine config that is already in memory
729 * (import OVF case). Since we are importing, the UUID in the machine
730 * config is ignored and we always generate a fresh one.
731 *
732 * @param aParent Associated parent object.
733 * @param strName Name for the new machine; this overrides what is specified in config.
734 * @param strSettingsFilename File name of .vbox file.
735 * @param config Machine configuration loaded and parsed from XML.
736 *
737 * @return Success indicator. if not S_OK, the machine object is invalid
738 */
739HRESULT Machine::init(VirtualBox *aParent,
740 const Utf8Str &strName,
741 const Utf8Str &strSettingsFilename,
742 const settings::MachineConfigFile &config)
743{
744 LogFlowThisFuncEnter();
745
746 /* Enclose the state transition NotReady->InInit->Ready */
747 AutoInitSpan autoInitSpan(this);
748 AssertReturn(autoInitSpan.isOk(), E_FAIL);
749
750 HRESULT hrc = initImpl(aParent, strSettingsFilename);
751 if (FAILED(hrc)) return hrc;
752
753 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
754 if (FAILED(hrc)) return hrc;
755
756 hrc = initDataAndChildObjects();
757 if (SUCCEEDED(hrc))
758 {
759 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
760 mData->mAccessible = TRUE;
761
762 // create empty machine config for instance data
763 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
764
765 // generate fresh UUID, ignore machine config
766 unconst(mData->mUuid).create();
767
768 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
769
770 // override VM name as well, it may be different
771 mUserData->s.strName = strName;
772
773 if (SUCCEEDED(hrc))
774 {
775 /* At this point the changing of the current state modification
776 * flag is allowed. */
777 i_allowStateModification();
778
779 /* commit all changes made during the initialization */
780 i_commit();
781 }
782 }
783
784 /* Confirm a successful initialization when it's the case */
785 if (SUCCEEDED(hrc))
786 {
787 if (mData->mAccessible)
788 autoInitSpan.setSucceeded();
789 else
790 {
791 /* Ignore all errors from unregistering, they would destroy
792- * the more interesting error information we already have,
793- * pinpointing the issue with the VM config. */
794 ErrorInfoKeeper eik;
795
796 autoInitSpan.setLimited();
797
798 // uninit media from this machine's media registry, or else
799 // reloading the settings will fail
800 mParent->i_unregisterMachineMedia(i_getId());
801 }
802 }
803
804 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
805 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
806
807 LogFlowThisFuncLeave();
808
809 return hrc;
810}
811
812/**
813 * Shared code between the various init() implementations.
814 *
815 * @returns HRESULT
816 * @param aParent The VirtualBox object.
817 * @param strConfigFile Settings file.
818 */
819HRESULT Machine::initImpl(VirtualBox *aParent,
820 const Utf8Str &strConfigFile)
821{
822 LogFlowThisFuncEnter();
823
824 AssertReturn(aParent, E_INVALIDARG);
825 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
826
827 HRESULT hrc = S_OK;
828
829 /* share the parent weakly */
830 unconst(mParent) = aParent;
831
832 /* allocate the essential machine data structure (the rest will be
833 * allocated later by initDataAndChildObjects() */
834 mData.allocate();
835
836 /* memorize the config file name (as provided) */
837 mData->m_strConfigFile = strConfigFile;
838
839 /* get the full file name */
840 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
841 if (RT_FAILURE(vrc1))
842 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
843 tr("Invalid machine settings file name '%s' (%Rrc)"),
844 strConfigFile.c_str(),
845 vrc1);
846
847#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
848 /** @todo Only create when the machine is going to be encrypted. */
849 /* Non-pageable memory is not accessible for non-VM process */
850 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
851 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
852#endif
853
854 LogFlowThisFuncLeave();
855
856 return hrc;
857}
858
859/**
860 * Tries to create a machine settings file in the path stored in the machine
861 * instance data. Used when a new machine is created to fail gracefully if
862 * the settings file could not be written (e.g. because machine dir is read-only).
863 * @return
864 */
865HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
866{
867 HRESULT hrc = S_OK;
868
869 // when we create a new machine, we must be able to create the settings file
870 RTFILE f = NIL_RTFILE;
871 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
872 if ( RT_SUCCESS(vrc)
873 || vrc == VERR_SHARING_VIOLATION
874 )
875 {
876 if (RT_SUCCESS(vrc))
877 RTFileClose(f);
878 if (!fForceOverwrite)
879 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
880 else
881 {
882 /* try to delete the config file, as otherwise the creation
883 * of a new settings file will fail. */
884 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
885 }
886 }
887 else if ( vrc != VERR_FILE_NOT_FOUND
888 && vrc != VERR_PATH_NOT_FOUND
889 )
890 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
891 mData->m_strConfigFileFull.c_str(), vrc);
892 return hrc;
893}
894
895/**
896 * Initializes the registered machine by loading the settings file.
897 * This method is separated from #init() in order to make it possible to
898 * retry the operation after VirtualBox startup instead of refusing to
899 * startup the whole VirtualBox server in case if the settings file of some
900 * registered VM is invalid or inaccessible.
901 *
902 * @note Must be always called from this object's write lock
903 * (unless called from #init() that doesn't need any locking).
904 * @note Locks the mUSBController method for writing.
905 * @note Subclasses must not call this method.
906 */
907HRESULT Machine::i_registeredInit()
908{
909 AssertReturn(!i_isSessionMachine(), E_FAIL);
910 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
911 AssertReturn(mData->mUuid.isValid(), E_FAIL);
912 AssertReturn(!mData->mAccessible, E_FAIL);
913
914 HRESULT hrc = initDataAndChildObjects();
915 if (SUCCEEDED(hrc))
916 {
917 /* Temporarily reset the registered flag in order to let setters
918 * potentially called from loadSettings() succeed (isMutable() used in
919 * all setters will return FALSE for a Machine instance if mRegistered
920 * is TRUE). */
921 mData->mRegistered = FALSE;
922
923 PCVBOXCRYPTOIF pCryptoIf = NULL;
924 SecretKey *pKey = NULL;
925 const char *pszPassword = NULL;
926#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
927 /* Resolve password and cryptographic support interface if machine is encrypted. */
928 if (mData->mstrKeyId.isNotEmpty())
929 {
930 /* Get at the crpytographic interface. */
931 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
932 if (SUCCEEDED(hrc))
933 {
934 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
935 if (RT_SUCCESS(vrc))
936 pszPassword = (const char *)pKey->getKeyBuffer();
937 else
938 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
939 mData->mstrKeyId.c_str(), vrc);
940 }
941 }
942#else
943 RT_NOREF(pKey);
944#endif
945
946 if (SUCCEEDED(hrc))
947 {
948 try
949 {
950 // load and parse machine XML; this will throw on XML or logic errors
951 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
952 pCryptoIf, pszPassword);
953
954 if (mData->mUuid != mData->pMachineConfigFile->uuid)
955 throw setError(E_FAIL,
956 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
957 mData->pMachineConfigFile->uuid.raw(),
958 mData->m_strConfigFileFull.c_str(),
959 mData->mUuid.toString().c_str(),
960 mParent->i_settingsFilePath().c_str());
961
962#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
963 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
964 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
965 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
966 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
967
968 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
969 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
970 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
971 mData->pMachineConfigFile->uuid.raw());
972 else
973#endif
974 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
975 if (FAILED(hrc)) throw hrc;
976 }
977 catch (HRESULT err)
978 {
979 /* we assume that error info is set by the thrower */
980 hrc = err;
981 }
982 catch (...)
983 {
984 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
985 }
986
987 /* Restore the registered flag (even on failure) */
988 mData->mRegistered = TRUE;
989 }
990
991#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
992 if (pCryptoIf)
993 mParent->i_releaseCryptoIf(pCryptoIf);
994 if (pKey)
995 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
996#endif
997 }
998
999 if (SUCCEEDED(hrc))
1000 {
1001 /* Set mAccessible to TRUE only if we successfully locked and loaded
1002 * the settings file */
1003 mData->mAccessible = TRUE;
1004
1005 /* commit all changes made during loading the settings file */
1006 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1007 /// @todo r=klaus for some reason the settings loading logic backs up
1008 // the settings, and therefore a commit is needed. Should probably be changed.
1009 }
1010 else
1011 {
1012 /* If the machine is registered, then, instead of returning a
1013 * failure, we mark it as inaccessible and set the result to
1014 * success to give it a try later */
1015
1016 /* fetch the current error info */
1017 mData->mAccessError = com::ErrorInfo();
1018 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1019
1020 /* rollback all changes */
1021 i_rollback(false /* aNotify */);
1022
1023 // uninit media from this machine's media registry, or else
1024 // reloading the settings will fail
1025 mParent->i_unregisterMachineMedia(i_getId());
1026
1027 /* uninitialize the common part to make sure all data is reset to
1028 * default (null) values */
1029 uninitDataAndChildObjects();
1030
1031 hrc = S_OK;
1032 }
1033
1034 return hrc;
1035}
1036
1037/**
1038 * Uninitializes the instance.
1039 * Called either from FinalRelease() or by the parent when it gets destroyed.
1040 *
1041 * @note The caller of this method must make sure that this object
1042 * a) doesn't have active callers on the current thread and b) is not locked
1043 * by the current thread; otherwise uninit() will hang either a) due to
1044 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1045 * a dead-lock caused by this thread waiting for all callers on the other
1046 * threads are done but preventing them from doing so by holding a lock.
1047 */
1048void Machine::uninit()
1049{
1050 LogFlowThisFuncEnter();
1051
1052 Assert(!isWriteLockOnCurrentThread());
1053
1054 Assert(!uRegistryNeedsSaving);
1055 if (uRegistryNeedsSaving)
1056 {
1057 AutoCaller autoCaller(this);
1058 if (SUCCEEDED(autoCaller.hrc()))
1059 {
1060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1061 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1062 }
1063 }
1064
1065 /* Enclose the state transition Ready->InUninit->NotReady */
1066 AutoUninitSpan autoUninitSpan(this);
1067 if (autoUninitSpan.uninitDone())
1068 return;
1069
1070 Assert(!i_isSnapshotMachine());
1071 Assert(!i_isSessionMachine());
1072 Assert(!!mData);
1073
1074 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1075 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1076
1077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 if (!mData->mSession.mMachine.isNull())
1080 {
1081 /* Theoretically, this can only happen if the VirtualBox server has been
1082 * terminated while there were clients running that owned open direct
1083 * sessions. Since in this case we are definitely called by
1084 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1085 * won't happen on the client watcher thread (because it has a
1086 * VirtualBox caller for the duration of the
1087 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1088 * cannot happen until the VirtualBox caller is released). This is
1089 * important, because SessionMachine::uninit() cannot correctly operate
1090 * after we return from this method (it expects the Machine instance is
1091 * still valid). We'll call it ourselves below.
1092 */
1093 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1094 (SessionMachine*)mData->mSession.mMachine));
1095
1096 if (Global::IsOnlineOrTransient(mData->mMachineState))
1097 {
1098 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1099 /* set machine state using SessionMachine reimplementation */
1100 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1101 }
1102
1103 /*
1104 * Uninitialize SessionMachine using public uninit() to indicate
1105 * an unexpected uninitialization.
1106 */
1107 mData->mSession.mMachine->uninit();
1108 /* SessionMachine::uninit() must set mSession.mMachine to null */
1109 Assert(mData->mSession.mMachine.isNull());
1110 }
1111
1112 // uninit media from this machine's media registry, if they're still there
1113 Guid uuidMachine(i_getId());
1114
1115 /* the lock is no more necessary (SessionMachine is uninitialized) */
1116 alock.release();
1117
1118 /* XXX This will fail with
1119 * "cannot be closed because it is still attached to 1 virtual machines"
1120 * because at this point we did not call uninitDataAndChildObjects() yet
1121 * and therefore also removeBackReference() for all these media was not called! */
1122
1123 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1124 mParent->i_unregisterMachineMedia(uuidMachine);
1125
1126 // has machine been modified?
1127 if (mData->flModifications)
1128 {
1129 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1130 i_rollback(false /* aNotify */);
1131 }
1132
1133 if (mData->mAccessible)
1134 uninitDataAndChildObjects();
1135
1136#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1137 if (mData->mpKeyStore != NULL)
1138 delete mData->mpKeyStore;
1139#endif
1140
1141 /* free the essential data structure last */
1142 mData.free();
1143
1144 LogFlowThisFuncLeave();
1145}
1146
1147// Wrapped IMachine properties
1148/////////////////////////////////////////////////////////////////////////////
1149HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1150{
1151 /* mParent is constant during life time, no need to lock */
1152 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1153 aParent = pVirtualBox;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1159{
1160 /* mPlatform is constant during life time, no need to lock */
1161 ComObjPtr<Platform> pPlatform(mPlatform);
1162 aPlatform = pPlatform;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::getAccessible(BOOL *aAccessible)
1168{
1169 /* In some cases (medium registry related), it is necessary to be able to
1170 * go through the list of all machines. Happens when an inaccessible VM
1171 * has a sensible medium registry. */
1172 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT hrc = S_OK;
1176
1177 if (!mData->mAccessible)
1178 {
1179 /* try to initialize the VM once more if not accessible */
1180
1181 AutoReinitSpan autoReinitSpan(this);
1182 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1183
1184#ifdef DEBUG
1185 LogFlowThisFunc(("Dumping media backreferences\n"));
1186 mParent->i_dumpAllBackRefs();
1187#endif
1188
1189 if (mData->pMachineConfigFile)
1190 {
1191 // reset the XML file to force loadSettings() (called from i_registeredInit())
1192 // to parse it again; the file might have changed
1193 delete mData->pMachineConfigFile;
1194 mData->pMachineConfigFile = NULL;
1195 }
1196
1197 hrc = i_registeredInit();
1198
1199 if (SUCCEEDED(hrc) && mData->mAccessible)
1200 {
1201 autoReinitSpan.setSucceeded();
1202
1203 /* make sure interesting parties will notice the accessibility
1204 * state change */
1205 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1206 mParent->i_onMachineDataChanged(mData->mUuid);
1207 }
1208 }
1209
1210 if (SUCCEEDED(hrc))
1211 *aAccessible = mData->mAccessible;
1212
1213 LogFlowThisFuncLeave();
1214
1215 return hrc;
1216}
1217
1218HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1223 {
1224 /* return shortly */
1225 aAccessError = NULL;
1226 return S_OK;
1227 }
1228
1229 HRESULT hrc = S_OK;
1230
1231 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1232 hrc = errorInfo.createObject();
1233 if (SUCCEEDED(hrc))
1234 {
1235 errorInfo->init(mData->mAccessError.getResultCode(),
1236 mData->mAccessError.getInterfaceID().ref(),
1237 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1238 Utf8Str(mData->mAccessError.getText()));
1239 aAccessError = errorInfo;
1240 }
1241
1242 return hrc;
1243}
1244
1245HRESULT Machine::getName(com::Utf8Str &aName)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 aName = mUserData->s.strName;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setName(const com::Utf8Str &aName)
1255{
1256 // prohibit setting a UUID only as the machine name, or else it can
1257 // never be found by findMachine()
1258 Guid test(aName);
1259
1260 if (test.isValid())
1261 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1262
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1266 if (FAILED(hrc)) return hrc;
1267
1268 i_setModified(IsModified_MachineData);
1269 mUserData.backup();
1270 mUserData->s.strName = aName;
1271
1272 return S_OK;
1273}
1274
1275HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1276{
1277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 aDescription = mUserData->s.strDescription;
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1285{
1286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 // this can be done in principle in any state as it doesn't affect the VM
1289 // significantly, but play safe by not messing around while complex
1290 // activities are going on
1291 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1292 if (FAILED(hrc)) return hrc;
1293
1294 i_setModified(IsModified_MachineData);
1295 mUserData.backup();
1296 mUserData->s.strDescription = aDescription;
1297
1298 return S_OK;
1299}
1300
1301HRESULT Machine::getId(com::Guid &aId)
1302{
1303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1304
1305 aId = mData->mUuid;
1306
1307 return S_OK;
1308}
1309
1310HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1311{
1312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1313 aGroups.resize(mUserData->s.llGroups.size());
1314 size_t i = 0;
1315 for (StringsList::const_iterator
1316 it = mUserData->s.llGroups.begin();
1317 it != mUserData->s.llGroups.end();
1318 ++it, ++i)
1319 aGroups[i] = (*it);
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1325{
1326 StringsList llGroups;
1327 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1328 if (FAILED(hrc))
1329 return hrc;
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1334 if (FAILED(hrc)) return hrc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mUserData.backup();
1338 mUserData->s.llGroups = llGroups;
1339
1340 mParent->i_onMachineGroupsChanged(mData->mUuid);
1341 return S_OK;
1342}
1343
1344HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1345{
1346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1347
1348 aOSTypeId = mUserData->s.strOsType;
1349
1350 return S_OK;
1351}
1352
1353HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1354{
1355 /* look up the object by Id to check it is valid */
1356 ComObjPtr<GuestOSType> pGuestOSType;
1357 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1358
1359 /* when setting, always use the "etalon" value for consistency -- lookup
1360 * by ID is case-insensitive and the input value may have different case */
1361 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1362
1363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1364
1365 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1366 if (FAILED(hrc)) return hrc;
1367
1368 i_setModified(IsModified_MachineData);
1369 mUserData.backup();
1370 mUserData->s.strOsType = osTypeId;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1385{
1386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1387
1388 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1389 if (FAILED(hrc)) return hrc;
1390
1391 i_setModified(IsModified_MachineData);
1392 mHWData.backup();
1393 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1394
1395 return S_OK;
1396}
1397
1398HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1399{
1400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 *aPointingHIDType = mHWData->mPointingHIDType;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1408{
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1412 if (FAILED(hrc)) return hrc;
1413
1414 i_setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mPointingHIDType = aPointingHIDType;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 aParavirtDebug = mHWData->mParavirtDebug;
1426 return S_OK;
1427}
1428
1429HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1430{
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(hrc)) return hrc;
1435
1436 /** @todo Parse/validate options? */
1437 if (aParavirtDebug != mHWData->mParavirtDebug)
1438 {
1439 i_setModified(IsModified_MachineData);
1440 mHWData.backup();
1441 mHWData->mParavirtDebug = aParavirtDebug;
1442 }
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1448{
1449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 *aParavirtProvider = mHWData->mParavirtProvider;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1457{
1458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1461 if (FAILED(hrc)) return hrc;
1462
1463 if (aParavirtProvider != mHWData->mParavirtProvider)
1464 {
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mParavirtProvider = aParavirtProvider;
1468 }
1469
1470 return S_OK;
1471}
1472
1473HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1474{
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 *aParavirtProvider = mHWData->mParavirtProvider;
1478 switch (mHWData->mParavirtProvider)
1479 {
1480 case ParavirtProvider_None:
1481 case ParavirtProvider_HyperV:
1482 case ParavirtProvider_KVM:
1483 case ParavirtProvider_Minimal:
1484 break;
1485
1486 /* Resolve dynamic provider types to the effective types. */
1487 default:
1488 {
1489 ComObjPtr<GuestOSType> pGuestOSType;
1490 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1491 pGuestOSType);
1492 if (FAILED(hrc2) || pGuestOSType.isNull())
1493 {
1494 *aParavirtProvider = ParavirtProvider_None;
1495 break;
1496 }
1497
1498 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1499 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1500
1501 switch (mHWData->mParavirtProvider)
1502 {
1503 case ParavirtProvider_Legacy:
1504 {
1505 if (fOsXGuest)
1506 *aParavirtProvider = ParavirtProvider_Minimal;
1507 else
1508 *aParavirtProvider = ParavirtProvider_None;
1509 break;
1510 }
1511
1512 case ParavirtProvider_Default:
1513 {
1514 Assert(strlen(GUEST_OS_ID_STR_X64("")) > 0);
1515 if (fOsXGuest)
1516 *aParavirtProvider = ParavirtProvider_Minimal;
1517 else if ( mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows11")
1518 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows10")
1519 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows10")
1520 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows81")
1521 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows81")
1522 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows8")
1523 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows8")
1524 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows7")
1525 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows7")
1526 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("WindowsVista")
1527 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("WindowsVista")
1528 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1529 || mUserData->s.strOsType.startsWith("Windows201"))
1530 && mUserData->s.strOsType.endsWith(GUEST_OS_ID_STR_X64("")))
1531 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2012")
1532 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2012")
1533 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2008")
1534 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2008"))
1535 *aParavirtProvider = ParavirtProvider_HyperV;
1536 else if ( guestTypeFamilyId == "Linux"
1537 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux22") // Linux22 and Linux24{_64} excluded as they're too old
1538 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux24") // to have any KVM paravirtualization support.
1539 && mUserData->s.strOsType != GUEST_OS_ID_STR_X64("Linux24"))
1540 *aParavirtProvider = ParavirtProvider_KVM;
1541 else
1542 *aParavirtProvider = ParavirtProvider_None;
1543 break;
1544 }
1545
1546 default: AssertFailedBreak(); /* Shut up MSC. */
1547 }
1548 break;
1549 }
1550 }
1551
1552 Assert( *aParavirtProvider == ParavirtProvider_None
1553 || *aParavirtProvider == ParavirtProvider_Minimal
1554 || *aParavirtProvider == ParavirtProvider_HyperV
1555 || *aParavirtProvider == ParavirtProvider_KVM);
1556 return S_OK;
1557}
1558
1559HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1560{
1561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 aHardwareVersion = mHWData->mHWVersion;
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1569{
1570 /* check known version */
1571 Utf8Str hwVersion = aHardwareVersion;
1572 if ( hwVersion.compare("1") != 0
1573 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1574 return setError(E_INVALIDARG,
1575 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(hrc)) return hrc;
1581
1582 i_setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 mHWData->mHWVersion = aHardwareVersion;
1585
1586 return S_OK;
1587}
1588
1589HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1590{
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 if (!mHWData->mHardwareUUID.isZero())
1594 aHardwareUUID = mHWData->mHardwareUUID;
1595 else
1596 aHardwareUUID = mData->mUuid;
1597
1598 return S_OK;
1599}
1600
1601HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1602{
1603 if (!aHardwareUUID.isValid())
1604 return E_INVALIDARG;
1605
1606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1609 if (FAILED(hrc)) return hrc;
1610
1611 i_setModified(IsModified_MachineData);
1612 mHWData.backup();
1613 if (aHardwareUUID == mData->mUuid)
1614 mHWData->mHardwareUUID.clear();
1615 else
1616 mHWData->mHardwareUUID = aHardwareUUID;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aMemorySize = mHWData->mMemorySize;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setMemorySize(ULONG aMemorySize)
1631{
1632 /* check RAM limits */
1633 if ( aMemorySize < MM_RAM_MIN_IN_MB
1634 || aMemorySize > MM_RAM_MAX_IN_MB
1635 )
1636 return setError(E_INVALIDARG,
1637 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1638 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1639
1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1643 if (FAILED(hrc)) return hrc;
1644
1645 i_setModified(IsModified_MachineData);
1646 mHWData.backup();
1647 mHWData->mMemorySize = aMemorySize;
1648
1649 return S_OK;
1650}
1651
1652HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 *aCPUCount = mHWData->mCPUCount;
1657
1658 return S_OK;
1659}
1660
1661HRESULT Machine::setCPUCount(ULONG aCPUCount)
1662{
1663 /* check CPU limits */
1664 if ( aCPUCount < SchemaDefs::MinCPUCount
1665 || aCPUCount > SchemaDefs::MaxCPUCount
1666 )
1667 return setError(E_INVALIDARG,
1668 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1669 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1670
1671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1672
1673 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1674 if (mHWData->mCPUHotPlugEnabled)
1675 {
1676 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1677 {
1678 if (mHWData->mCPUAttached[idx])
1679 return setError(E_INVALIDARG,
1680 tr("There is still a CPU attached to socket %lu."
1681 "Detach the CPU before removing the socket"),
1682 aCPUCount, idx+1);
1683 }
1684 }
1685
1686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1687 if (FAILED(hrc)) return hrc;
1688
1689 i_setModified(IsModified_MachineData);
1690 mHWData.backup();
1691 mHWData->mCPUCount = aCPUCount;
1692
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1701
1702 return S_OK;
1703}
1704
1705HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1706{
1707 /* check throttle limits */
1708 if ( aCPUExecutionCap < 1
1709 || aCPUExecutionCap > 100
1710 )
1711 return setError(E_INVALIDARG,
1712 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1713 aCPUExecutionCap, 1, 100);
1714
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1718 if (FAILED(hrc)) return hrc;
1719
1720 alock.release();
1721 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1722 alock.acquire();
1723 if (FAILED(hrc)) return hrc;
1724
1725 i_setModified(IsModified_MachineData);
1726 mHWData.backup();
1727 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1728
1729 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1730 if (Global::IsOnline(mData->mMachineState))
1731 i_saveSettings(NULL, alock);
1732
1733 return S_OK;
1734}
1735
1736HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1737{
1738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1746{
1747 HRESULT hrc = S_OK;
1748
1749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1750
1751 hrc = i_checkStateDependency(MutableStateDep);
1752 if (FAILED(hrc)) return hrc;
1753
1754 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1755 {
1756 if (aCPUHotPlugEnabled)
1757 {
1758 i_setModified(IsModified_MachineData);
1759 mHWData.backup();
1760
1761 /* Add the amount of CPUs currently attached */
1762 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1763 mHWData->mCPUAttached[i] = true;
1764 }
1765 else
1766 {
1767 /*
1768 * We can disable hotplug only if the amount of maximum CPUs is equal
1769 * to the amount of attached CPUs
1770 */
1771 unsigned cCpusAttached = 0;
1772 unsigned iHighestId = 0;
1773
1774 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1775 {
1776 if (mHWData->mCPUAttached[i])
1777 {
1778 cCpusAttached++;
1779 iHighestId = i;
1780 }
1781 }
1782
1783 if ( (cCpusAttached != mHWData->mCPUCount)
1784 || (iHighestId >= mHWData->mCPUCount))
1785 return setError(E_INVALIDARG,
1786 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1787
1788 i_setModified(IsModified_MachineData);
1789 mHWData.backup();
1790 }
1791 }
1792
1793 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1794
1795 return hrc;
1796}
1797
1798HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1808{
1809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1812 if (SUCCEEDED(hrc))
1813 {
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1817 }
1818 return hrc;
1819}
1820
1821HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824 aCPUProfile = mHWData->mCpuProfile;
1825 return S_OK;
1826}
1827
1828HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1829{
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1832 if (SUCCEEDED(hrc))
1833 {
1834 i_setModified(IsModified_MachineData);
1835 mHWData.backup();
1836 /* Empty equals 'host'. */
1837 if (aCPUProfile.isNotEmpty())
1838 mHWData->mCpuProfile = aCPUProfile;
1839 else
1840 mHWData->mCpuProfile = "host";
1841 }
1842 return hrc;
1843}
1844
1845HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1846{
1847#ifdef VBOX_WITH_USB_CARDREADER
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1851
1852 return S_OK;
1853#else
1854 NOREF(aEmulatedUSBCardReaderEnabled);
1855 return E_NOTIMPL;
1856#endif
1857}
1858
1859HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1860{
1861#ifdef VBOX_WITH_USB_CARDREADER
1862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1865 if (FAILED(hrc)) return hrc;
1866
1867 i_setModified(IsModified_MachineData);
1868 mHWData.backup();
1869 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1870
1871 return S_OK;
1872#else
1873 NOREF(aEmulatedUSBCardReaderEnabled);
1874 return E_NOTIMPL;
1875#endif
1876}
1877
1878/** @todo this method should not be public */
1879HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1880{
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1884
1885 return S_OK;
1886}
1887
1888/**
1889 * Set the memory balloon size.
1890 *
1891 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1892 * we have to make sure that we never call IGuest from here.
1893 */
1894HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1895{
1896 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1897#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1898 /* check limits */
1899 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1900 return setError(E_INVALIDARG,
1901 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1902 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1903
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1907 if (FAILED(hrc)) return hrc;
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1912
1913 return S_OK;
1914#else
1915 NOREF(aMemoryBalloonSize);
1916 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1917#endif
1918}
1919
1920HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1921{
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1929{
1930#ifdef VBOX_WITH_PAGE_SHARING
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1934 if (FAILED(hrc)) return hrc;
1935
1936 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1937 i_setModified(IsModified_MachineData);
1938 mHWData.backup();
1939 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1940 return S_OK;
1941#else
1942 NOREF(aPageFusionEnabled);
1943 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1944#endif
1945}
1946
1947HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1948{
1949 /* mFirmwareSettings is constant during life time, no need to lock */
1950 aFirmwareSettings = mFirmwareSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1956{
1957 /* mTrustedPlatformModule is constant during life time, no need to lock */
1958 aTrustedPlatformModule = mTrustedPlatformModule;
1959
1960 return S_OK;
1961}
1962
1963HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1964{
1965 /* mNvramStore is constant during life time, no need to lock */
1966 aNvramStore = mNvramStore;
1967
1968 return S_OK;
1969}
1970
1971HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1972{
1973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 aRecordingSettings = mRecordingSettings;
1976
1977 return S_OK;
1978}
1979
1980HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1981{
1982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 aGraphicsAdapter = mGraphicsAdapter;
1985
1986 return S_OK;
1987}
1988
1989HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
1994
1995 return S_OK;
1996}
1997
1998HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
1999{
2000 /** @todo (r=dmik):
2001 * 1. Allow to change the name of the snapshot folder containing snapshots
2002 * 2. Rename the folder on disk instead of just changing the property
2003 * value (to be smart and not to leave garbage). Note that it cannot be
2004 * done here because the change may be rolled back. Thus, the right
2005 * place is #saveSettings().
2006 */
2007
2008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
2010 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2011 if (FAILED(hrc)) return hrc;
2012
2013 if (!mData->mCurrentSnapshot.isNull())
2014 return setError(E_FAIL,
2015 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2016
2017 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2018
2019 if (strSnapshotFolder.isEmpty())
2020 strSnapshotFolder = "Snapshots";
2021 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2022 if (RT_FAILURE(vrc))
2023 return setErrorBoth(E_FAIL, vrc,
2024 tr("Invalid snapshot folder '%s' (%Rrc)"),
2025 strSnapshotFolder.c_str(), vrc);
2026
2027 i_setModified(IsModified_MachineData);
2028 mUserData.backup();
2029
2030 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2031
2032 return S_OK;
2033}
2034
2035HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2036{
2037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2038
2039 aMediumAttachments.resize(mMediumAttachments->size());
2040 size_t i = 0;
2041 for (MediumAttachmentList::const_iterator
2042 it = mMediumAttachments->begin();
2043 it != mMediumAttachments->end();
2044 ++it, ++i)
2045 aMediumAttachments[i] = *it;
2046
2047 return S_OK;
2048}
2049
2050HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2051{
2052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 Assert(!!mVRDEServer);
2055
2056 aVRDEServer = mVRDEServer;
2057
2058 return S_OK;
2059}
2060
2061HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2062{
2063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 aAudioSettings = mAudioSettings;
2066
2067 return S_OK;
2068}
2069
2070HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2071{
2072#ifdef VBOX_WITH_VUSB
2073 clearError();
2074 MultiResult hrcMult(S_OK);
2075
2076# ifdef VBOX_WITH_USB
2077 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2078 if (FAILED(hrcMult)) return hrcMult;
2079# endif
2080
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 aUSBControllers.resize(mUSBControllers->size());
2084 size_t i = 0;
2085 for (USBControllerList::const_iterator
2086 it = mUSBControllers->begin();
2087 it != mUSBControllers->end();
2088 ++it, ++i)
2089 aUSBControllers[i] = *it;
2090
2091 return S_OK;
2092#else
2093 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2094 * extended error info to indicate that USB is simply not available
2095 * (w/o treating it as a failure), for example, as in OSE */
2096 NOREF(aUSBControllers);
2097 ReturnComNotImplemented();
2098#endif /* VBOX_WITH_VUSB */
2099}
2100
2101HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2102{
2103#ifdef VBOX_WITH_VUSB
2104 clearError();
2105 MultiResult hrcMult(S_OK);
2106
2107# ifdef VBOX_WITH_USB
2108 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2109 if (FAILED(hrcMult)) return hrcMult;
2110# endif
2111
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 aUSBDeviceFilters = mUSBDeviceFilters;
2115 return hrcMult;
2116#else
2117 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2118 * extended error info to indicate that USB is simply not available
2119 * (w/o treating it as a failure), for example, as in OSE */
2120 NOREF(aUSBDeviceFilters);
2121 ReturnComNotImplemented();
2122#endif /* VBOX_WITH_VUSB */
2123}
2124
2125HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2126{
2127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 aSettingsFilePath = mData->m_strConfigFileFull;
2130
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2135{
2136 RT_NOREF(aSettingsFilePath);
2137 ReturnComNotImplemented();
2138}
2139
2140HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2141{
2142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2145 if (FAILED(hrc)) return hrc;
2146
2147 if (!mData->pMachineConfigFile->fileExists())
2148 // this is a new machine, and no config file exists yet:
2149 *aSettingsModified = TRUE;
2150 else
2151 *aSettingsModified = (mData->flModifications != 0);
2152
2153 return S_OK;
2154}
2155
2156HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2157{
2158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 *aSessionState = mData->mSession.mState;
2161
2162 return S_OK;
2163}
2164
2165HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2166{
2167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 aSessionName = mData->mSession.mName;
2170
2171 return S_OK;
2172}
2173
2174HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2175{
2176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2177
2178 *aSessionPID = mData->mSession.mPID;
2179
2180 return S_OK;
2181}
2182
2183HRESULT Machine::getState(MachineState_T *aState)
2184{
2185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 *aState = mData->mMachineState;
2188 Assert(mData->mMachineState != MachineState_Null);
2189
2190 return S_OK;
2191}
2192
2193HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2194{
2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2198
2199 return S_OK;
2200}
2201
2202HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2203{
2204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2205
2206 aStateFilePath = mSSData->strStateFilePath;
2207
2208 return S_OK;
2209}
2210
2211HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2212{
2213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 i_getLogFolder(aLogFolder);
2216
2217 return S_OK;
2218}
2219
2220HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2221{
2222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2223
2224 aCurrentSnapshot = mData->mCurrentSnapshot;
2225
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2234 ? 0
2235 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2236
2237 return S_OK;
2238}
2239
2240HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2241{
2242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 /* Note: for machines with no snapshots, we always return FALSE
2245 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2246 * reasons :) */
2247
2248 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2249 ? FALSE
2250 : mData->mCurrentStateModified;
2251
2252 return S_OK;
2253}
2254
2255HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2256{
2257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 aSharedFolders.resize(mHWData->mSharedFolders.size());
2260 size_t i = 0;
2261 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2262 it = mHWData->mSharedFolders.begin();
2263 it != mHWData->mSharedFolders.end();
2264 ++it, ++i)
2265 aSharedFolders[i] = *it;
2266
2267 return S_OK;
2268}
2269
2270HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2271{
2272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 *aClipboardMode = mHWData->mClipboardMode;
2275
2276 return S_OK;
2277}
2278
2279HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2280{
2281 HRESULT hrc = S_OK;
2282
2283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2284
2285 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2286 if (FAILED(hrc)) return hrc;
2287
2288 alock.release();
2289 hrc = i_onClipboardModeChange(aClipboardMode);
2290 alock.acquire();
2291 if (FAILED(hrc)) return hrc;
2292
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mClipboardMode = aClipboardMode;
2296
2297 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2298 if (Global::IsOnline(mData->mMachineState))
2299 i_saveSettings(NULL, alock);
2300
2301 return S_OK;
2302}
2303
2304HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2305{
2306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2307
2308 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2309
2310 return S_OK;
2311}
2312
2313HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2314{
2315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2316
2317 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2318 if (FAILED(hrc)) return hrc;
2319
2320 alock.release();
2321 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2322 alock.acquire();
2323 if (FAILED(hrc)) return hrc;
2324
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2328
2329 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2330 if (Global::IsOnline(mData->mMachineState))
2331 i_saveSettings(NULL, alock);
2332
2333 return S_OK;
2334}
2335
2336HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2337{
2338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2339
2340 *aDnDMode = mHWData->mDnDMode;
2341
2342 return S_OK;
2343}
2344
2345HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2346{
2347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2348
2349 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2350 if (FAILED(hrc)) return hrc;
2351
2352 alock.release();
2353 hrc = i_onDnDModeChange(aDnDMode);
2354
2355 alock.acquire();
2356 if (FAILED(hrc)) return hrc;
2357
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mDnDMode = aDnDMode;
2361
2362 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2363 if (Global::IsOnline(mData->mMachineState))
2364 i_saveSettings(NULL, alock);
2365
2366 return S_OK;
2367}
2368
2369HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2370{
2371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 aStorageControllers.resize(mStorageControllers->size());
2374 size_t i = 0;
2375 for (StorageControllerList::const_iterator
2376 it = mStorageControllers->begin();
2377 it != mStorageControllers->end();
2378 ++it, ++i)
2379 aStorageControllers[i] = *it;
2380
2381 return S_OK;
2382}
2383
2384HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2385{
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 *aEnabled = mUserData->s.fTeleporterEnabled;
2389
2390 return S_OK;
2391}
2392
2393HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2394{
2395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2396
2397 /* Only allow it to be set to true when PoweredOff or Aborted.
2398 (Clearing it is always permitted.) */
2399 if ( aTeleporterEnabled
2400 && mData->mRegistered
2401 && ( !i_isSessionMachine()
2402 || ( mData->mMachineState != MachineState_PoweredOff
2403 && mData->mMachineState != MachineState_Teleported
2404 && mData->mMachineState != MachineState_Aborted
2405 )
2406 )
2407 )
2408 return setError(VBOX_E_INVALID_VM_STATE,
2409 tr("The machine is not powered off (state is %s)"),
2410 Global::stringifyMachineState(mData->mMachineState));
2411
2412 i_setModified(IsModified_MachineData);
2413 mUserData.backup();
2414 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2415
2416 return S_OK;
2417}
2418
2419HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2420{
2421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2424
2425 return S_OK;
2426}
2427
2428HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2429{
2430 if (aTeleporterPort >= _64K)
2431 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2432
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2436 if (FAILED(hrc)) return hrc;
2437
2438 i_setModified(IsModified_MachineData);
2439 mUserData.backup();
2440 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2441
2442 return S_OK;
2443}
2444
2445HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2446{
2447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2448
2449 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2450
2451 return S_OK;
2452}
2453
2454HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2455{
2456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2459 if (FAILED(hrc)) return hrc;
2460
2461 i_setModified(IsModified_MachineData);
2462 mUserData.backup();
2463 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2464
2465 return S_OK;
2466}
2467
2468HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2469{
2470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2471 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2472
2473 return S_OK;
2474}
2475
2476HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2477{
2478 /*
2479 * Hash the password first.
2480 */
2481 com::Utf8Str aT = aTeleporterPassword;
2482
2483 if (!aT.isEmpty())
2484 {
2485 if (VBoxIsPasswordHashed(&aT))
2486 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2487 VBoxHashPassword(&aT);
2488 }
2489
2490 /*
2491 * Do the update.
2492 */
2493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2494 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2495 if (SUCCEEDED(hrc))
2496 {
2497 i_setModified(IsModified_MachineData);
2498 mUserData.backup();
2499 mUserData->s.strTeleporterPassword = aT;
2500 }
2501
2502 return hrc;
2503}
2504
2505HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2515{
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2519 if (FAILED(hrc)) return hrc;
2520
2521 i_setModified(IsModified_MachineData);
2522 mHWData.backup();
2523 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2524
2525 return S_OK;
2526}
2527
2528HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2529{
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 *aIOCacheSize = mHWData->mIOCacheSize;
2533
2534 return S_OK;
2535}
2536
2537HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2538{
2539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2540
2541 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2542 if (FAILED(hrc)) return hrc;
2543
2544 i_setModified(IsModified_MachineData);
2545 mHWData.backup();
2546 mHWData->mIOCacheSize = aIOCacheSize;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2556 aKeyId = mSSData->strStateKeyId;
2557#else
2558 aKeyId = com::Utf8Str::Empty;
2559#endif
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2569 aKeyStore = mSSData->strStateKeyStore;
2570#else
2571 aKeyStore = com::Utf8Str::Empty;
2572#endif
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2582 aKeyId = mData->mstrLogKeyId;
2583#else
2584 aKeyId = com::Utf8Str::Empty;
2585#endif
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2595 aKeyStore = mData->mstrLogKeyStore;
2596#else
2597 aKeyStore = com::Utf8Str::Empty;
2598#endif
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2604{
2605 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2606
2607 return S_OK;
2608}
2609
2610
2611/**
2612 * @note Locks objects!
2613 */
2614HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2615 LockType_T aLockType)
2616{
2617 /* check the session state */
2618 SessionState_T state;
2619 HRESULT hrc = aSession->COMGETTER(State)(&state);
2620 if (FAILED(hrc)) return hrc;
2621
2622 if (state != SessionState_Unlocked)
2623 return setError(VBOX_E_INVALID_OBJECT_STATE,
2624 tr("The given session is busy"));
2625
2626 // get the client's IInternalSessionControl interface
2627 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2628 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2629 E_INVALIDARG);
2630
2631 // session name (only used in some code paths)
2632 Utf8Str strSessionName;
2633
2634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 if (!mData->mRegistered)
2637 return setError(E_UNEXPECTED,
2638 tr("The machine '%s' is not registered"),
2639 mUserData->s.strName.c_str());
2640
2641 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2642
2643 SessionState_T oldState = mData->mSession.mState;
2644 /* Hack: in case the session is closing and there is a progress object
2645 * which allows waiting for the session to be closed, take the opportunity
2646 * and do a limited wait (max. 1 second). This helps a lot when the system
2647 * is busy and thus session closing can take a little while. */
2648 if ( mData->mSession.mState == SessionState_Unlocking
2649 && mData->mSession.mProgress)
2650 {
2651 alock.release();
2652 mData->mSession.mProgress->WaitForCompletion(1000);
2653 alock.acquire();
2654 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2655 }
2656
2657 // try again now
2658 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2659 // (i.e. session machine exists)
2660 && (aLockType == LockType_Shared) // caller wants a shared link to the
2661 // existing session that holds the write lock:
2662 )
2663 {
2664 // OK, share the session... we are now dealing with three processes:
2665 // 1) VBoxSVC (where this code runs);
2666 // 2) process C: the caller's client process (who wants a shared session);
2667 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2668
2669 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2670 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2671 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2672 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2673 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2674
2675 /*
2676 * Release the lock before calling the client process. It's safe here
2677 * since the only thing to do after we get the lock again is to add
2678 * the remote control to the list (which doesn't directly influence
2679 * anything).
2680 */
2681 alock.release();
2682
2683 // get the console of the session holding the write lock (this is a remote call)
2684 ComPtr<IConsole> pConsoleW;
2685 if (mData->mSession.mLockType == LockType_VM)
2686 {
2687 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2688 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2689 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2690 if (FAILED(hrc))
2691 // the failure may occur w/o any error info (from RPC), so provide one
2692 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2693 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2694 }
2695
2696 // share the session machine and W's console with the caller's session
2697 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2698 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2699 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2700
2701 if (FAILED(hrc))
2702 // the failure may occur w/o any error info (from RPC), so provide one
2703 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2704 alock.acquire();
2705
2706 // need to revalidate the state after acquiring the lock again
2707 if (mData->mSession.mState != SessionState_Locked)
2708 {
2709 pSessionControl->Uninitialize();
2710 return setError(VBOX_E_INVALID_SESSION_STATE,
2711 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2712 mUserData->s.strName.c_str());
2713 }
2714
2715 // add the caller's session to the list
2716 mData->mSession.mRemoteControls.push_back(pSessionControl);
2717 }
2718 else if ( mData->mSession.mState == SessionState_Locked
2719 || mData->mSession.mState == SessionState_Unlocking
2720 )
2721 {
2722 // sharing not permitted, or machine still unlocking:
2723 return setError(VBOX_E_INVALID_OBJECT_STATE,
2724 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2725 mUserData->s.strName.c_str());
2726 }
2727 else
2728 {
2729 // machine is not locked: then write-lock the machine (create the session machine)
2730
2731 // must not be busy
2732 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2733
2734 // get the caller's session PID
2735 RTPROCESS pid = NIL_RTPROCESS;
2736 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2737 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2738 Assert(pid != NIL_RTPROCESS);
2739
2740 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2741
2742 if (fLaunchingVMProcess)
2743 {
2744 if (mData->mSession.mPID == NIL_RTPROCESS)
2745 {
2746 // two or more clients racing for a lock, the one which set the
2747 // session state to Spawning will win, the others will get an
2748 // error as we can't decide here if waiting a little would help
2749 // (only for shared locks this would avoid an error)
2750 return setError(VBOX_E_INVALID_OBJECT_STATE,
2751 tr("The machine '%s' already has a lock request pending"),
2752 mUserData->s.strName.c_str());
2753 }
2754
2755 // this machine is awaiting for a spawning session to be opened:
2756 // then the calling process must be the one that got started by
2757 // LaunchVMProcess()
2758
2759 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2760 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2761
2762#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2763 /* Hardened windows builds spawns three processes when a VM is
2764 launched, the 3rd one is the one that will end up here. */
2765 RTPROCESS pidParent;
2766 int vrc = RTProcQueryParent(pid, &pidParent);
2767 if (RT_SUCCESS(vrc))
2768 vrc = RTProcQueryParent(pidParent, &pidParent);
2769 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2770 || vrc == VERR_ACCESS_DENIED)
2771 {
2772 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2773 mData->mSession.mPID = pid;
2774 }
2775#endif
2776
2777 if (mData->mSession.mPID != pid)
2778 return setError(E_ACCESSDENIED,
2779 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2780 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2781 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2782 }
2783
2784 // create the mutable SessionMachine from the current machine
2785 ComObjPtr<SessionMachine> sessionMachine;
2786 sessionMachine.createObject();
2787 hrc = sessionMachine->init(this);
2788 AssertComRC(hrc);
2789
2790 /* NOTE: doing return from this function after this point but
2791 * before the end is forbidden since it may call SessionMachine::uninit()
2792 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2793 * lock while still holding the Machine lock in alock so that a deadlock
2794 * is possible due to the wrong lock order. */
2795
2796 if (SUCCEEDED(hrc))
2797 {
2798 /*
2799 * Set the session state to Spawning to protect against subsequent
2800 * attempts to open a session and to unregister the machine after
2801 * we release the lock.
2802 */
2803 SessionState_T origState = mData->mSession.mState;
2804 mData->mSession.mState = SessionState_Spawning;
2805
2806#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2807 /* Get the client token ID to be passed to the client process */
2808 Utf8Str strTokenId;
2809 sessionMachine->i_getTokenId(strTokenId);
2810 Assert(!strTokenId.isEmpty());
2811#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2812 /* Get the client token to be passed to the client process */
2813 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2814 /* The token is now "owned" by pToken, fix refcount */
2815 if (!pToken.isNull())
2816 pToken->Release();
2817#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2818
2819 /*
2820 * Release the lock before calling the client process -- it will call
2821 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2822 * because the state is Spawning, so that LaunchVMProcess() and
2823 * LockMachine() calls will fail. This method, called before we
2824 * acquire the lock again, will fail because of the wrong PID.
2825 *
2826 * Note that mData->mSession.mRemoteControls accessed outside
2827 * the lock may not be modified when state is Spawning, so it's safe.
2828 */
2829 alock.release();
2830
2831 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2832#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2833 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2834#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2835 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2836 /* Now the token is owned by the client process. */
2837 pToken.setNull();
2838#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2839 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2840
2841 /* The failure may occur w/o any error info (from RPC), so provide one */
2842 if (FAILED(hrc))
2843 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2844
2845 // get session name, either to remember or to compare against
2846 // the already known session name.
2847 {
2848 Bstr bstrSessionName;
2849 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2850 if (SUCCEEDED(hrc2))
2851 strSessionName = bstrSessionName;
2852 }
2853
2854 if ( SUCCEEDED(hrc)
2855 && fLaunchingVMProcess
2856 )
2857 {
2858 /* complete the remote session initialization */
2859
2860 /* get the console from the direct session */
2861 ComPtr<IConsole> console;
2862 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2863 ComAssertComRC(hrc);
2864
2865 if (SUCCEEDED(hrc) && !console)
2866 {
2867 ComAssert(!!console);
2868 hrc = E_FAIL;
2869 }
2870
2871 /* assign machine & console to the remote session */
2872 if (SUCCEEDED(hrc))
2873 {
2874 /*
2875 * after LaunchVMProcess(), the first and the only
2876 * entry in remoteControls is that remote session
2877 */
2878 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2879 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2880 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2881
2882 /* The failure may occur w/o any error info (from RPC), so provide one */
2883 if (FAILED(hrc))
2884 setError(VBOX_E_VM_ERROR,
2885 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2886 }
2887
2888 if (FAILED(hrc))
2889 pSessionControl->Uninitialize();
2890 }
2891
2892 /* acquire the lock again */
2893 alock.acquire();
2894
2895 /* Restore the session state */
2896 mData->mSession.mState = origState;
2897 }
2898
2899 // finalize spawning anyway (this is why we don't return on errors above)
2900 if (fLaunchingVMProcess)
2901 {
2902 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2903 /* Note that the progress object is finalized later */
2904 /** @todo Consider checking mData->mSession.mProgress for cancellation
2905 * around here. */
2906
2907 /* We don't reset mSession.mPID here because it is necessary for
2908 * SessionMachine::uninit() to reap the child process later. */
2909
2910 if (FAILED(hrc))
2911 {
2912 /* Close the remote session, remove the remote control from the list
2913 * and reset session state to Closed (@note keep the code in sync
2914 * with the relevant part in checkForSpawnFailure()). */
2915
2916 Assert(mData->mSession.mRemoteControls.size() == 1);
2917 if (mData->mSession.mRemoteControls.size() == 1)
2918 {
2919 ErrorInfoKeeper eik;
2920 mData->mSession.mRemoteControls.front()->Uninitialize();
2921 }
2922
2923 mData->mSession.mRemoteControls.clear();
2924 mData->mSession.mState = SessionState_Unlocked;
2925 }
2926 }
2927 else
2928 {
2929 /* memorize PID of the directly opened session */
2930 if (SUCCEEDED(hrc))
2931 mData->mSession.mPID = pid;
2932 }
2933
2934 if (SUCCEEDED(hrc))
2935 {
2936 mData->mSession.mLockType = aLockType;
2937 /* memorize the direct session control and cache IUnknown for it */
2938 mData->mSession.mDirectControl = pSessionControl;
2939 mData->mSession.mState = SessionState_Locked;
2940 if (!fLaunchingVMProcess)
2941 mData->mSession.mName = strSessionName;
2942 /* associate the SessionMachine with this Machine */
2943 mData->mSession.mMachine = sessionMachine;
2944
2945 /* request an IUnknown pointer early from the remote party for later
2946 * identity checks (it will be internally cached within mDirectControl
2947 * at least on XPCOM) */
2948 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2949 NOREF(unk);
2950
2951#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2952 if (aLockType == LockType_VM)
2953 {
2954 /* get the console from the direct session */
2955 ComPtr<IConsole> console;
2956 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2957 ComAssertComRC(hrc);
2958 /* send passswords to console */
2959 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2960 it != mData->mpKeyStore->end();
2961 ++it)
2962 {
2963 SecretKey *pKey = it->second;
2964 pKey->retain();
2965 console->AddEncryptionPassword(Bstr(it->first).raw(),
2966 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2967 TRUE);
2968 pKey->release();
2969 }
2970
2971 }
2972#endif
2973 }
2974
2975 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2976 * would break the lock order */
2977 alock.release();
2978
2979 /* uninitialize the created session machine on failure */
2980 if (FAILED(hrc))
2981 sessionMachine->uninit();
2982 }
2983
2984 if (SUCCEEDED(hrc))
2985 {
2986 /*
2987 * tell the client watcher thread to update the set of
2988 * machines that have open sessions
2989 */
2990 mParent->i_updateClientWatcher();
2991
2992 if (oldState != SessionState_Locked)
2993 /* fire an event */
2994 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
2995 }
2996
2997 return hrc;
2998}
2999
3000/**
3001 * @note Locks objects!
3002 */
3003HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3004 const com::Utf8Str &aName,
3005 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3006 ComPtr<IProgress> &aProgress)
3007{
3008 Utf8Str strFrontend(aName);
3009 /* "emergencystop" doesn't need the session, so skip the checks/interface
3010 * retrieval. This code doesn't quite fit in here, but introducing a
3011 * special API method would be even more effort, and would require explicit
3012 * support by every API client. It's better to hide the feature a bit. */
3013 if (strFrontend != "emergencystop")
3014 CheckComArgNotNull(aSession);
3015
3016 HRESULT hrc = S_OK;
3017 if (strFrontend.isEmpty())
3018 {
3019 Bstr bstrFrontend;
3020 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3021 if (FAILED(hrc))
3022 return hrc;
3023 strFrontend = bstrFrontend;
3024 if (strFrontend.isEmpty())
3025 {
3026 ComPtr<ISystemProperties> systemProperties;
3027 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3028 if (FAILED(hrc))
3029 return hrc;
3030 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3031 if (FAILED(hrc))
3032 return hrc;
3033 strFrontend = bstrFrontend;
3034 }
3035 /* paranoia - emergencystop is not a valid default */
3036 if (strFrontend == "emergencystop")
3037 strFrontend = Utf8Str::Empty;
3038 }
3039 /* default frontend: Qt GUI */
3040 if (strFrontend.isEmpty())
3041 strFrontend = "GUI/Qt";
3042
3043 if (strFrontend != "emergencystop")
3044 {
3045 /* check the session state */
3046 SessionState_T state;
3047 hrc = aSession->COMGETTER(State)(&state);
3048 if (FAILED(hrc))
3049 return hrc;
3050
3051 if (state != SessionState_Unlocked)
3052 return setError(VBOX_E_INVALID_OBJECT_STATE,
3053 tr("The given session is busy"));
3054
3055 /* get the IInternalSessionControl interface */
3056 ComPtr<IInternalSessionControl> control(aSession);
3057 ComAssertMsgRet(!control.isNull(),
3058 ("No IInternalSessionControl interface"),
3059 E_INVALIDARG);
3060
3061 /* get the teleporter enable state for the progress object init. */
3062 BOOL fTeleporterEnabled;
3063 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3064 if (FAILED(hrc))
3065 return hrc;
3066
3067 /* create a progress object */
3068 ComObjPtr<ProgressProxy> progress;
3069 progress.createObject();
3070 hrc = progress->init(mParent,
3071 static_cast<IMachine*>(this),
3072 Bstr(tr("Starting VM")).raw(),
3073 TRUE /* aCancelable */,
3074 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3075 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3076 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3077 2 /* uFirstOperationWeight */,
3078 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3079 if (SUCCEEDED(hrc))
3080 {
3081 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3082 if (SUCCEEDED(hrc))
3083 {
3084 aProgress = progress;
3085
3086 /* signal the client watcher thread */
3087 mParent->i_updateClientWatcher();
3088
3089 /* fire an event */
3090 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3091 }
3092 }
3093 }
3094 else
3095 {
3096 /* no progress object - either instant success or failure */
3097 aProgress = NULL;
3098
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 if (mData->mSession.mState != SessionState_Locked)
3102 return setError(VBOX_E_INVALID_OBJECT_STATE,
3103 tr("The machine '%s' is not locked by a session"),
3104 mUserData->s.strName.c_str());
3105
3106 /* must have a VM process associated - do not kill normal API clients
3107 * with an open session */
3108 if (!Global::IsOnline(mData->mMachineState))
3109 return setError(VBOX_E_INVALID_OBJECT_STATE,
3110 tr("The machine '%s' does not have a VM process"),
3111 mUserData->s.strName.c_str());
3112
3113 /* forcibly terminate the VM process */
3114 if (mData->mSession.mPID != NIL_RTPROCESS)
3115 RTProcTerminate(mData->mSession.mPID);
3116
3117 /* signal the client watcher thread, as most likely the client has
3118 * been terminated */
3119 mParent->i_updateClientWatcher();
3120 }
3121
3122 return hrc;
3123}
3124
3125HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3126{
3127 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3128 return setError(E_INVALIDARG,
3129 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3130 aPosition, SchemaDefs::MaxBootPosition);
3131
3132 if (aDevice == DeviceType_USB)
3133 return setError(E_NOTIMPL,
3134 tr("Booting from USB device is currently not supported"));
3135
3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3139 if (FAILED(hrc)) return hrc;
3140
3141 i_setModified(IsModified_MachineData);
3142 mHWData.backup();
3143 mHWData->mBootOrder[aPosition - 1] = aDevice;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3149{
3150 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3151 return setError(E_INVALIDARG,
3152 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3153 aPosition, SchemaDefs::MaxBootPosition);
3154
3155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3156
3157 *aDevice = mHWData->mBootOrder[aPosition - 1];
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3163 LONG aControllerPort,
3164 LONG aDevice,
3165 DeviceType_T aType,
3166 const ComPtr<IMedium> &aMedium)
3167{
3168 IMedium *aM = aMedium;
3169 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3170 aName.c_str(), aControllerPort, aDevice, aType, aM));
3171
3172 // request the host lock first, since might be calling Host methods for getting host drives;
3173 // next, protect the media tree all the while we're in here, as well as our member variables
3174 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3175 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3176
3177 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3178 if (FAILED(hrc)) return hrc;
3179
3180 /// @todo NEWMEDIA implicit machine registration
3181 if (!mData->mRegistered)
3182 return setError(VBOX_E_INVALID_OBJECT_STATE,
3183 tr("Cannot attach storage devices to an unregistered machine"));
3184
3185 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3186
3187 /* Check for an existing controller. */
3188 ComObjPtr<StorageController> ctl;
3189 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3190 if (FAILED(hrc)) return hrc;
3191
3192 StorageControllerType_T ctrlType;
3193 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3194 if (FAILED(hrc))
3195 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3196
3197 bool fSilent = false;
3198
3199 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3200 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3201 if ( mData->mMachineState == MachineState_Paused
3202 && strReconfig == "1")
3203 fSilent = true;
3204
3205 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3206 bool fHotplug = false;
3207 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3208 fHotplug = true;
3209
3210 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3211 return setError(VBOX_E_INVALID_VM_STATE,
3212 tr("Controller '%s' does not support hot-plugging"),
3213 aName.c_str());
3214
3215 // check that the port and device are not out of range
3216 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3217 if (FAILED(hrc)) return hrc;
3218
3219 /* check if the device slot is already busy */
3220 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3221 aName,
3222 aControllerPort,
3223 aDevice);
3224 if (pAttachTemp)
3225 {
3226 Medium *pMedium = pAttachTemp->i_getMedium();
3227 if (pMedium)
3228 {
3229 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3230 return setError(VBOX_E_OBJECT_IN_USE,
3231 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3232 pMedium->i_getLocationFull().c_str(),
3233 aControllerPort,
3234 aDevice,
3235 aName.c_str());
3236 }
3237 else
3238 return setError(VBOX_E_OBJECT_IN_USE,
3239 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3240 aControllerPort, aDevice, aName.c_str());
3241 }
3242
3243 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3244 if (aMedium && medium.isNull())
3245 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3246
3247 AutoCaller mediumCaller(medium);
3248 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3249
3250 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3251
3252 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3253 if ( pAttachTemp
3254 && !medium.isNull()
3255 && ( medium->i_getType() != MediumType_Readonly
3256 || medium->i_getDeviceType() != DeviceType_DVD)
3257 )
3258 return setError(VBOX_E_OBJECT_IN_USE,
3259 tr("Medium '%s' is already attached to this virtual machine"),
3260 medium->i_getLocationFull().c_str());
3261
3262 if (!medium.isNull())
3263 {
3264 MediumType_T mtype = medium->i_getType();
3265 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3266 // For DVDs it's not written to the config file, so needs no global config
3267 // version bump. For floppies it's a new attribute "type", which is ignored
3268 // by older VirtualBox version, so needs no global config version bump either.
3269 // For hard disks this type is not accepted.
3270 if (mtype == MediumType_MultiAttach)
3271 {
3272 // This type is new with VirtualBox 4.0 and therefore requires settings
3273 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3274 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3275 // two reasons: The medium type is a property of the media registry tree, which
3276 // can reside in the global config file (for pre-4.0 media); we would therefore
3277 // possibly need to bump the global config version. We don't want to do that though
3278 // because that might make downgrading to pre-4.0 impossible.
3279 // As a result, we can only use these two new types if the medium is NOT in the
3280 // global registry:
3281 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3282 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3283 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3284 )
3285 return setError(VBOX_E_INVALID_OBJECT_STATE,
3286 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3287 "to machines that were created with VirtualBox 4.0 or later"),
3288 medium->i_getLocationFull().c_str());
3289 }
3290 }
3291
3292 bool fIndirect = false;
3293 if (!medium.isNull())
3294 fIndirect = medium->i_isReadOnly();
3295 bool associate = true;
3296
3297 do
3298 {
3299 if ( aType == DeviceType_HardDisk
3300 && mMediumAttachments.isBackedUp())
3301 {
3302 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3303
3304 /* check if the medium was attached to the VM before we started
3305 * changing attachments in which case the attachment just needs to
3306 * be restored */
3307 pAttachTemp = i_findAttachment(oldAtts, medium);
3308 if (pAttachTemp)
3309 {
3310 AssertReturn(!fIndirect, E_FAIL);
3311
3312 /* see if it's the same bus/channel/device */
3313 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3314 {
3315 /* the simplest case: restore the whole attachment
3316 * and return, nothing else to do */
3317 mMediumAttachments->push_back(pAttachTemp);
3318
3319 /* Reattach the medium to the VM. */
3320 if (fHotplug || fSilent)
3321 {
3322 mediumLock.release();
3323 treeLock.release();
3324 alock.release();
3325
3326 MediumLockList *pMediumLockList(new MediumLockList());
3327
3328 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3329 medium /* pToLockWrite */,
3330 false /* fMediumLockWriteAll */,
3331 NULL,
3332 *pMediumLockList);
3333 alock.acquire();
3334 if (FAILED(hrc))
3335 delete pMediumLockList;
3336 else
3337 {
3338 Assert(mData->mSession.mLockedMedia.IsLocked());
3339 mData->mSession.mLockedMedia.Unlock();
3340 alock.release();
3341 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3342 mData->mSession.mLockedMedia.Lock();
3343 alock.acquire();
3344 }
3345 alock.release();
3346
3347 if (SUCCEEDED(hrc))
3348 {
3349 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3350 /* Remove lock list in case of error. */
3351 if (FAILED(hrc))
3352 {
3353 mData->mSession.mLockedMedia.Unlock();
3354 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3355 mData->mSession.mLockedMedia.Lock();
3356 }
3357 }
3358 }
3359
3360 return S_OK;
3361 }
3362
3363 /* bus/channel/device differ; we need a new attachment object,
3364 * but don't try to associate it again */
3365 associate = false;
3366 break;
3367 }
3368 }
3369
3370 /* go further only if the attachment is to be indirect */
3371 if (!fIndirect)
3372 break;
3373
3374 /* perform the so called smart attachment logic for indirect
3375 * attachments. Note that smart attachment is only applicable to base
3376 * hard disks. */
3377
3378 if (medium->i_getParent().isNull())
3379 {
3380 /* first, investigate the backup copy of the current hard disk
3381 * attachments to make it possible to re-attach existing diffs to
3382 * another device slot w/o losing their contents */
3383 if (mMediumAttachments.isBackedUp())
3384 {
3385 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3386
3387 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3388 uint32_t foundLevel = 0;
3389
3390 for (MediumAttachmentList::const_iterator
3391 it = oldAtts.begin();
3392 it != oldAtts.end();
3393 ++it)
3394 {
3395 uint32_t level = 0;
3396 MediumAttachment *pAttach = *it;
3397 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3398 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3399 if (pMedium.isNull())
3400 continue;
3401
3402 if (pMedium->i_getBase(&level) == medium)
3403 {
3404 /* skip the hard disk if its currently attached (we
3405 * cannot attach the same hard disk twice) */
3406 if (i_findAttachment(*mMediumAttachments.data(),
3407 pMedium))
3408 continue;
3409
3410 /* matched device, channel and bus (i.e. attached to the
3411 * same place) will win and immediately stop the search;
3412 * otherwise the attachment that has the youngest
3413 * descendant of medium will be used
3414 */
3415 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3416 {
3417 /* the simplest case: restore the whole attachment
3418 * and return, nothing else to do */
3419 mMediumAttachments->push_back(*it);
3420
3421 /* Reattach the medium to the VM. */
3422 if (fHotplug || fSilent)
3423 {
3424 mediumLock.release();
3425 treeLock.release();
3426 alock.release();
3427
3428 MediumLockList *pMediumLockList(new MediumLockList());
3429
3430 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3431 medium /* pToLockWrite */,
3432 false /* fMediumLockWriteAll */,
3433 NULL,
3434 *pMediumLockList);
3435 alock.acquire();
3436 if (FAILED(hrc))
3437 delete pMediumLockList;
3438 else
3439 {
3440 Assert(mData->mSession.mLockedMedia.IsLocked());
3441 mData->mSession.mLockedMedia.Unlock();
3442 alock.release();
3443 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3444 mData->mSession.mLockedMedia.Lock();
3445 alock.acquire();
3446 }
3447 alock.release();
3448
3449 if (SUCCEEDED(hrc))
3450 {
3451 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3452 /* Remove lock list in case of error. */
3453 if (FAILED(hrc))
3454 {
3455 mData->mSession.mLockedMedia.Unlock();
3456 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3457 mData->mSession.mLockedMedia.Lock();
3458 }
3459 }
3460 }
3461
3462 return S_OK;
3463 }
3464 else if ( foundIt == oldAtts.end()
3465 || level > foundLevel /* prefer younger */
3466 )
3467 {
3468 foundIt = it;
3469 foundLevel = level;
3470 }
3471 }
3472 }
3473
3474 if (foundIt != oldAtts.end())
3475 {
3476 /* use the previously attached hard disk */
3477 medium = (*foundIt)->i_getMedium();
3478 mediumCaller.attach(medium);
3479 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3480 mediumLock.attach(medium);
3481 /* not implicit, doesn't require association with this VM */
3482 fIndirect = false;
3483 associate = false;
3484 /* go right to the MediumAttachment creation */
3485 break;
3486 }
3487 }
3488
3489 /* must give up the medium lock and medium tree lock as below we
3490 * go over snapshots, which needs a lock with higher lock order. */
3491 mediumLock.release();
3492 treeLock.release();
3493
3494 /* then, search through snapshots for the best diff in the given
3495 * hard disk's chain to base the new diff on */
3496
3497 ComObjPtr<Medium> base;
3498 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3499 while (snap)
3500 {
3501 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3502
3503 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3504
3505 MediumAttachment *pAttachFound = NULL;
3506 uint32_t foundLevel = 0;
3507
3508 for (MediumAttachmentList::const_iterator
3509 it = snapAtts.begin();
3510 it != snapAtts.end();
3511 ++it)
3512 {
3513 MediumAttachment *pAttach = *it;
3514 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3515 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3516 if (pMedium.isNull())
3517 continue;
3518
3519 uint32_t level = 0;
3520 if (pMedium->i_getBase(&level) == medium)
3521 {
3522 /* matched device, channel and bus (i.e. attached to the
3523 * same place) will win and immediately stop the search;
3524 * otherwise the attachment that has the youngest
3525 * descendant of medium will be used
3526 */
3527 if ( pAttach->i_getDevice() == aDevice
3528 && pAttach->i_getPort() == aControllerPort
3529 && pAttach->i_getControllerName() == aName
3530 )
3531 {
3532 pAttachFound = pAttach;
3533 break;
3534 }
3535 else if ( !pAttachFound
3536 || level > foundLevel /* prefer younger */
3537 )
3538 {
3539 pAttachFound = pAttach;
3540 foundLevel = level;
3541 }
3542 }
3543 }
3544
3545 if (pAttachFound)
3546 {
3547 base = pAttachFound->i_getMedium();
3548 break;
3549 }
3550
3551 snap = snap->i_getParent();
3552 }
3553
3554 /* re-lock medium tree and the medium, as we need it below */
3555 treeLock.acquire();
3556 mediumLock.acquire();
3557
3558 /* found a suitable diff, use it as a base */
3559 if (!base.isNull())
3560 {
3561 medium = base;
3562 mediumCaller.attach(medium);
3563 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3564 mediumLock.attach(medium);
3565 }
3566 }
3567
3568 Utf8Str strFullSnapshotFolder;
3569 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3570
3571 ComObjPtr<Medium> diff;
3572 diff.createObject();
3573 // store this diff in the same registry as the parent
3574 Guid uuidRegistryParent;
3575 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3576 {
3577 // parent image has no registry: this can happen if we're attaching a new immutable
3578 // image that has not yet been attached (medium then points to the base and we're
3579 // creating the diff image for the immutable, and the parent is not yet registered);
3580 // put the parent in the machine registry then
3581 mediumLock.release();
3582 treeLock.release();
3583 alock.release();
3584 i_addMediumToRegistry(medium);
3585 alock.acquire();
3586 treeLock.acquire();
3587 mediumLock.acquire();
3588 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3589 }
3590 hrc = diff->init(mParent,
3591 medium->i_getPreferredDiffFormat(),
3592 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3593 uuidRegistryParent,
3594 DeviceType_HardDisk);
3595 if (FAILED(hrc)) return hrc;
3596
3597 /* Apply the normal locking logic to the entire chain. */
3598 MediumLockList *pMediumLockList(new MediumLockList());
3599 mediumLock.release();
3600 treeLock.release();
3601 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3602 diff /* pToLockWrite */,
3603 false /* fMediumLockWriteAll */,
3604 medium,
3605 *pMediumLockList);
3606 treeLock.acquire();
3607 mediumLock.acquire();
3608 if (SUCCEEDED(hrc))
3609 {
3610 mediumLock.release();
3611 treeLock.release();
3612 hrc = pMediumLockList->Lock();
3613 treeLock.acquire();
3614 mediumLock.acquire();
3615 if (FAILED(hrc))
3616 setError(hrc,
3617 tr("Could not lock medium when creating diff '%s'"),
3618 diff->i_getLocationFull().c_str());
3619 else
3620 {
3621 /* will release the lock before the potentially lengthy
3622 * operation, so protect with the special state */
3623 MachineState_T oldState = mData->mMachineState;
3624 i_setMachineState(MachineState_SettingUp);
3625
3626 mediumLock.release();
3627 treeLock.release();
3628 alock.release();
3629
3630 hrc = medium->i_createDiffStorage(diff,
3631 medium->i_getPreferredDiffVariant(),
3632 pMediumLockList,
3633 NULL /* aProgress */,
3634 true /* aWait */,
3635 false /* aNotify */);
3636
3637 alock.acquire();
3638 treeLock.acquire();
3639 mediumLock.acquire();
3640
3641 i_setMachineState(oldState);
3642 }
3643 }
3644
3645 /* Unlock the media and free the associated memory. */
3646 delete pMediumLockList;
3647
3648 if (FAILED(hrc)) return hrc;
3649
3650 /* use the created diff for the actual attachment */
3651 medium = diff;
3652 mediumCaller.attach(medium);
3653 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3654 mediumLock.attach(medium);
3655 }
3656 while (0);
3657
3658 ComObjPtr<MediumAttachment> attachment;
3659 attachment.createObject();
3660 hrc = attachment->init(this,
3661 medium,
3662 aName,
3663 aControllerPort,
3664 aDevice,
3665 aType,
3666 fIndirect,
3667 false /* fPassthrough */,
3668 false /* fTempEject */,
3669 false /* fNonRotational */,
3670 false /* fDiscard */,
3671 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3672 Utf8Str::Empty);
3673 if (FAILED(hrc)) return hrc;
3674
3675 if (associate && !medium.isNull())
3676 {
3677 // as the last step, associate the medium to the VM
3678 hrc = medium->i_addBackReference(mData->mUuid);
3679 // here we can fail because of Deleting, or being in process of creating a Diff
3680 if (FAILED(hrc)) return hrc;
3681
3682 mediumLock.release();
3683 treeLock.release();
3684 alock.release();
3685 i_addMediumToRegistry(medium);
3686 alock.acquire();
3687 treeLock.acquire();
3688 mediumLock.acquire();
3689 }
3690
3691 /* success: finally remember the attachment */
3692 i_setModified(IsModified_Storage);
3693 mMediumAttachments.backup();
3694 mMediumAttachments->push_back(attachment);
3695
3696 mediumLock.release();
3697 treeLock.release();
3698 alock.release();
3699
3700 if (fHotplug || fSilent)
3701 {
3702 if (!medium.isNull())
3703 {
3704 MediumLockList *pMediumLockList(new MediumLockList());
3705
3706 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3707 medium /* pToLockWrite */,
3708 false /* fMediumLockWriteAll */,
3709 NULL,
3710 *pMediumLockList);
3711 alock.acquire();
3712 if (FAILED(hrc))
3713 delete pMediumLockList;
3714 else
3715 {
3716 Assert(mData->mSession.mLockedMedia.IsLocked());
3717 mData->mSession.mLockedMedia.Unlock();
3718 alock.release();
3719 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3720 mData->mSession.mLockedMedia.Lock();
3721 alock.acquire();
3722 }
3723 alock.release();
3724 }
3725
3726 if (SUCCEEDED(hrc))
3727 {
3728 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3729 /* Remove lock list in case of error. */
3730 if (FAILED(hrc))
3731 {
3732 mData->mSession.mLockedMedia.Unlock();
3733 mData->mSession.mLockedMedia.Remove(attachment);
3734 mData->mSession.mLockedMedia.Lock();
3735 }
3736 }
3737 }
3738
3739 /* Save modified registries, but skip this machine as it's the caller's
3740 * job to save its settings like all other settings changes. */
3741 mParent->i_unmarkRegistryModified(i_getId());
3742 mParent->i_saveModifiedRegistries();
3743
3744 if (SUCCEEDED(hrc))
3745 {
3746 if (fIndirect && medium != aM)
3747 mParent->i_onMediumConfigChanged(medium);
3748 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3749 }
3750
3751 return hrc;
3752}
3753
3754HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3755 LONG aDevice)
3756{
3757 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3758
3759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3760
3761 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3762 if (FAILED(hrc)) return hrc;
3763
3764 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3765
3766 /* Check for an existing controller. */
3767 ComObjPtr<StorageController> ctl;
3768 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3769 if (FAILED(hrc)) return hrc;
3770
3771 StorageControllerType_T ctrlType;
3772 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3773 if (FAILED(hrc))
3774 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3775
3776 bool fSilent = false;
3777 Utf8Str strReconfig;
3778
3779 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3780 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3781 if ( mData->mMachineState == MachineState_Paused
3782 && strReconfig == "1")
3783 fSilent = true;
3784
3785 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3786 bool fHotplug = false;
3787 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3788 fHotplug = true;
3789
3790 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3791 return setError(VBOX_E_INVALID_VM_STATE,
3792 tr("Controller '%s' does not support hot-plugging"),
3793 aName.c_str());
3794
3795 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3796 aName,
3797 aControllerPort,
3798 aDevice);
3799 if (!pAttach)
3800 return setError(VBOX_E_OBJECT_NOT_FOUND,
3801 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3802 aDevice, aControllerPort, aName.c_str());
3803
3804 if (fHotplug && !pAttach->i_getHotPluggable())
3805 return setError(VBOX_E_NOT_SUPPORTED,
3806 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3807 aDevice, aControllerPort, aName.c_str());
3808
3809 /*
3810 * The VM has to detach the device before we delete any implicit diffs.
3811 * If this fails we can roll back without loosing data.
3812 */
3813 if (fHotplug || fSilent)
3814 {
3815 alock.release();
3816 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3817 alock.acquire();
3818 }
3819 if (FAILED(hrc)) return hrc;
3820
3821 /* If we are here everything went well and we can delete the implicit now. */
3822 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3823
3824 alock.release();
3825
3826 /* Save modified registries, but skip this machine as it's the caller's
3827 * job to save its settings like all other settings changes. */
3828 mParent->i_unmarkRegistryModified(i_getId());
3829 mParent->i_saveModifiedRegistries();
3830
3831 if (SUCCEEDED(hrc))
3832 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3833
3834 return hrc;
3835}
3836
3837HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3838 LONG aDevice, BOOL aPassthrough)
3839{
3840 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3841 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3842
3843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3844
3845 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3846 if (FAILED(hrc)) return hrc;
3847
3848 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3849
3850 /* Check for an existing controller. */
3851 ComObjPtr<StorageController> ctl;
3852 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3853 if (FAILED(hrc)) return hrc;
3854
3855 StorageControllerType_T ctrlType;
3856 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3857 if (FAILED(hrc))
3858 return setError(E_FAIL,
3859 tr("Could not get type of controller '%s'"),
3860 aName.c_str());
3861
3862 bool fSilent = false;
3863 Utf8Str strReconfig;
3864
3865 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3866 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3867 if ( mData->mMachineState == MachineState_Paused
3868 && strReconfig == "1")
3869 fSilent = true;
3870
3871 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3872 bool fHotplug = false;
3873 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3874 fHotplug = true;
3875
3876 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3877 return setError(VBOX_E_INVALID_VM_STATE,
3878 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3879 aName.c_str());
3880
3881 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3882 aName,
3883 aControllerPort,
3884 aDevice);
3885 if (!pAttach)
3886 return setError(VBOX_E_OBJECT_NOT_FOUND,
3887 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3888 aDevice, aControllerPort, aName.c_str());
3889
3890
3891 i_setModified(IsModified_Storage);
3892 mMediumAttachments.backup();
3893
3894 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3895
3896 if (pAttach->i_getType() != DeviceType_DVD)
3897 return setError(E_INVALIDARG,
3898 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3899 aDevice, aControllerPort, aName.c_str());
3900
3901 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3902
3903 pAttach->i_updatePassthrough(!!aPassthrough);
3904
3905 attLock.release();
3906 alock.release();
3907 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3908 if (SUCCEEDED(hrc) && fValueChanged)
3909 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3910
3911 return hrc;
3912}
3913
3914HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3915 LONG aDevice, BOOL aTemporaryEject)
3916{
3917
3918 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3919 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3920
3921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3922
3923 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3924 if (FAILED(hrc)) return hrc;
3925
3926 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3927 aName,
3928 aControllerPort,
3929 aDevice);
3930 if (!pAttach)
3931 return setError(VBOX_E_OBJECT_NOT_FOUND,
3932 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3933 aDevice, aControllerPort, aName.c_str());
3934
3935
3936 i_setModified(IsModified_Storage);
3937 mMediumAttachments.backup();
3938
3939 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3940
3941 if (pAttach->i_getType() != DeviceType_DVD)
3942 return setError(E_INVALIDARG,
3943 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3944 aDevice, aControllerPort, aName.c_str());
3945 pAttach->i_updateTempEject(!!aTemporaryEject);
3946
3947 return S_OK;
3948}
3949
3950HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3951 LONG aDevice, BOOL aNonRotational)
3952{
3953
3954 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3955 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3956
3957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3958
3959 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3960 if (FAILED(hrc)) return hrc;
3961
3962 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3963
3964 if (Global::IsOnlineOrTransient(mData->mMachineState))
3965 return setError(VBOX_E_INVALID_VM_STATE,
3966 tr("Invalid machine state: %s"),
3967 Global::stringifyMachineState(mData->mMachineState));
3968
3969 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3970 aName,
3971 aControllerPort,
3972 aDevice);
3973 if (!pAttach)
3974 return setError(VBOX_E_OBJECT_NOT_FOUND,
3975 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3976 aDevice, aControllerPort, aName.c_str());
3977
3978
3979 i_setModified(IsModified_Storage);
3980 mMediumAttachments.backup();
3981
3982 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3983
3984 if (pAttach->i_getType() != DeviceType_HardDisk)
3985 return setError(E_INVALIDARG,
3986 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"),
3987 aDevice, aControllerPort, aName.c_str());
3988 pAttach->i_updateNonRotational(!!aNonRotational);
3989
3990 return S_OK;
3991}
3992
3993HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
3994 LONG aDevice, BOOL aDiscard)
3995{
3996
3997 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
3998 aName.c_str(), aControllerPort, aDevice, aDiscard));
3999
4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4001
4002 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4003 if (FAILED(hrc)) return hrc;
4004
4005 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4006
4007 if (Global::IsOnlineOrTransient(mData->mMachineState))
4008 return setError(VBOX_E_INVALID_VM_STATE,
4009 tr("Invalid machine state: %s"),
4010 Global::stringifyMachineState(mData->mMachineState));
4011
4012 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4013 aName,
4014 aControllerPort,
4015 aDevice);
4016 if (!pAttach)
4017 return setError(VBOX_E_OBJECT_NOT_FOUND,
4018 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4019 aDevice, aControllerPort, aName.c_str());
4020
4021
4022 i_setModified(IsModified_Storage);
4023 mMediumAttachments.backup();
4024
4025 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4026
4027 if (pAttach->i_getType() != DeviceType_HardDisk)
4028 return setError(E_INVALIDARG,
4029 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"),
4030 aDevice, aControllerPort, aName.c_str());
4031 pAttach->i_updateDiscard(!!aDiscard);
4032
4033 return S_OK;
4034}
4035
4036HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4037 LONG aDevice, BOOL aHotPluggable)
4038{
4039 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4040 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4041
4042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4043
4044 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4045 if (FAILED(hrc)) return hrc;
4046
4047 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4048
4049 if (Global::IsOnlineOrTransient(mData->mMachineState))
4050 return setError(VBOX_E_INVALID_VM_STATE,
4051 tr("Invalid machine state: %s"),
4052 Global::stringifyMachineState(mData->mMachineState));
4053
4054 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4055 aName,
4056 aControllerPort,
4057 aDevice);
4058 if (!pAttach)
4059 return setError(VBOX_E_OBJECT_NOT_FOUND,
4060 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4061 aDevice, aControllerPort, aName.c_str());
4062
4063 /* Check for an existing controller. */
4064 ComObjPtr<StorageController> ctl;
4065 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4066 if (FAILED(hrc)) return hrc;
4067
4068 StorageControllerType_T ctrlType;
4069 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4070 if (FAILED(hrc))
4071 return setError(E_FAIL,
4072 tr("Could not get type of controller '%s'"),
4073 aName.c_str());
4074
4075 if (!i_isControllerHotplugCapable(ctrlType))
4076 return setError(VBOX_E_NOT_SUPPORTED,
4077 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4078 aName.c_str());
4079
4080 /* silently ignore attempts to modify the hot-plug status of USB devices */
4081 if (ctrlType == StorageControllerType_USB)
4082 return S_OK;
4083
4084 i_setModified(IsModified_Storage);
4085 mMediumAttachments.backup();
4086
4087 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4088
4089 if (pAttach->i_getType() == DeviceType_Floppy)
4090 return setError(E_INVALIDARG,
4091 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"),
4092 aDevice, aControllerPort, aName.c_str());
4093 pAttach->i_updateHotPluggable(!!aHotPluggable);
4094
4095 return S_OK;
4096}
4097
4098HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4099 LONG aDevice)
4100{
4101 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4102 aName.c_str(), aControllerPort, aDevice));
4103
4104 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4105}
4106
4107HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4108 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4109{
4110 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4111 aName.c_str(), aControllerPort, aDevice));
4112
4113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4114
4115 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4116 if (FAILED(hrc)) return hrc;
4117
4118 if (Global::IsOnlineOrTransient(mData->mMachineState))
4119 return setError(VBOX_E_INVALID_VM_STATE,
4120 tr("Invalid machine state: %s"),
4121 Global::stringifyMachineState(mData->mMachineState));
4122
4123 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4124 aName,
4125 aControllerPort,
4126 aDevice);
4127 if (!pAttach)
4128 return setError(VBOX_E_OBJECT_NOT_FOUND,
4129 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4130 aDevice, aControllerPort, aName.c_str());
4131
4132
4133 i_setModified(IsModified_Storage);
4134 mMediumAttachments.backup();
4135
4136 IBandwidthGroup *iB = aBandwidthGroup;
4137 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4138 if (aBandwidthGroup && group.isNull())
4139 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4140
4141 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4142
4143 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4144 if (strBandwidthGroupOld.isNotEmpty())
4145 {
4146 /* Get the bandwidth group object and release it - this must not fail. */
4147 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4148 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4149 Assert(SUCCEEDED(hrc));
4150
4151 pBandwidthGroupOld->i_release();
4152 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4153 }
4154
4155 if (!group.isNull())
4156 {
4157 group->i_reference();
4158 pAttach->i_updateBandwidthGroup(group->i_getName());
4159 }
4160
4161 return S_OK;
4162}
4163
4164HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4165 LONG aControllerPort,
4166 LONG aDevice,
4167 DeviceType_T aType)
4168{
4169 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4170 aName.c_str(), aControllerPort, aDevice, aType));
4171
4172 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4173}
4174
4175
4176HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4177 LONG aControllerPort,
4178 LONG aDevice,
4179 BOOL aForce)
4180{
4181 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4182 aName.c_str(), aControllerPort, aForce));
4183
4184 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4185}
4186
4187HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4188 LONG aControllerPort,
4189 LONG aDevice,
4190 const ComPtr<IMedium> &aMedium,
4191 BOOL aForce)
4192{
4193 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4194 aName.c_str(), aControllerPort, aDevice, aForce));
4195
4196 // request the host lock first, since might be calling Host methods for getting host drives;
4197 // next, protect the media tree all the while we're in here, as well as our member variables
4198 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4199 this->lockHandle(),
4200 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4201
4202 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4203 if (FAILED(hrc)) return hrc;
4204
4205 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4206 aName,
4207 aControllerPort,
4208 aDevice);
4209 if (pAttach.isNull())
4210 return setError(VBOX_E_OBJECT_NOT_FOUND,
4211 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4212 aDevice, aControllerPort, aName.c_str());
4213
4214 /* Remember previously mounted medium. The medium before taking the
4215 * backup is not necessarily the same thing. */
4216 ComObjPtr<Medium> oldmedium;
4217 oldmedium = pAttach->i_getMedium();
4218
4219 IMedium *iM = aMedium;
4220 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4221 if (aMedium && pMedium.isNull())
4222 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4223
4224 /* Check if potential medium is already mounted */
4225 if (pMedium == oldmedium)
4226 return S_OK;
4227
4228 AutoCaller mediumCaller(pMedium);
4229 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4230
4231 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4232 if (pMedium)
4233 {
4234 DeviceType_T mediumType = pAttach->i_getType();
4235 switch (mediumType)
4236 {
4237 case DeviceType_DVD:
4238 case DeviceType_Floppy:
4239 break;
4240
4241 default:
4242 return setError(VBOX_E_INVALID_OBJECT_STATE,
4243 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4244 aControllerPort,
4245 aDevice,
4246 aName.c_str());
4247 }
4248 }
4249
4250 i_setModified(IsModified_Storage);
4251 mMediumAttachments.backup();
4252
4253 {
4254 // The backup operation makes the pAttach reference point to the
4255 // old settings. Re-get the correct reference.
4256 pAttach = i_findAttachment(*mMediumAttachments.data(),
4257 aName,
4258 aControllerPort,
4259 aDevice);
4260 if (!oldmedium.isNull())
4261 oldmedium->i_removeBackReference(mData->mUuid);
4262 if (!pMedium.isNull())
4263 {
4264 pMedium->i_addBackReference(mData->mUuid);
4265
4266 mediumLock.release();
4267 multiLock.release();
4268 i_addMediumToRegistry(pMedium);
4269 multiLock.acquire();
4270 mediumLock.acquire();
4271 }
4272
4273 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4274 pAttach->i_updateMedium(pMedium);
4275 }
4276
4277 i_setModified(IsModified_Storage);
4278
4279 mediumLock.release();
4280 multiLock.release();
4281 hrc = i_onMediumChange(pAttach, aForce);
4282 multiLock.acquire();
4283 mediumLock.acquire();
4284
4285 /* On error roll back this change only. */
4286 if (FAILED(hrc))
4287 {
4288 if (!pMedium.isNull())
4289 pMedium->i_removeBackReference(mData->mUuid);
4290 pAttach = i_findAttachment(*mMediumAttachments.data(),
4291 aName,
4292 aControllerPort,
4293 aDevice);
4294 /* If the attachment is gone in the meantime, bail out. */
4295 if (pAttach.isNull())
4296 return hrc;
4297 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4298 if (!oldmedium.isNull())
4299 oldmedium->i_addBackReference(mData->mUuid);
4300 pAttach->i_updateMedium(oldmedium);
4301 }
4302
4303 mediumLock.release();
4304 multiLock.release();
4305
4306 /* Save modified registries, but skip this machine as it's the caller's
4307 * job to save its settings like all other settings changes. */
4308 mParent->i_unmarkRegistryModified(i_getId());
4309 mParent->i_saveModifiedRegistries();
4310
4311 return hrc;
4312}
4313HRESULT Machine::getMedium(const com::Utf8Str &aName,
4314 LONG aControllerPort,
4315 LONG aDevice,
4316 ComPtr<IMedium> &aMedium)
4317{
4318 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4319 aName.c_str(), aControllerPort, aDevice));
4320
4321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4322
4323 aMedium = NULL;
4324
4325 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4326 aName,
4327 aControllerPort,
4328 aDevice);
4329 if (pAttach.isNull())
4330 return setError(VBOX_E_OBJECT_NOT_FOUND,
4331 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4332 aDevice, aControllerPort, aName.c_str());
4333
4334 aMedium = pAttach->i_getMedium();
4335
4336 return S_OK;
4337}
4338
4339HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4340{
4341 if (aSlot < RT_ELEMENTS(mSerialPorts))
4342 {
4343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4344 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4345 return S_OK;
4346 }
4347 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4348}
4349
4350HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4351{
4352 if (aSlot < RT_ELEMENTS(mParallelPorts))
4353 {
4354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4355 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4356 return S_OK;
4357 }
4358 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4359}
4360
4361
4362HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4363{
4364 /* Do not assert if slot is out of range, just return the advertised
4365 status. testdriver/vbox.py triggers this in logVmInfo. */
4366 if (aSlot >= mNetworkAdapters.size())
4367 return setError(E_INVALIDARG,
4368 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4369 aSlot, mNetworkAdapters.size());
4370
4371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4374
4375 return S_OK;
4376}
4377
4378HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4379{
4380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4381
4382 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4383 size_t i = 0;
4384 for (settings::StringsMap::const_iterator
4385 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4386 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4387 ++it, ++i)
4388 aKeys[i] = it->first;
4389
4390 return S_OK;
4391}
4392
4393 /**
4394 * @note Locks this object for reading.
4395 */
4396HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4397 com::Utf8Str &aValue)
4398{
4399 /* start with nothing found */
4400 aValue = "";
4401
4402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4403
4404 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4405 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4406 // found:
4407 aValue = it->second; // source is a Utf8Str
4408
4409 /* return the result to caller (may be empty) */
4410 return S_OK;
4411}
4412
4413 /**
4414 * @note Locks mParent for writing + this object for writing.
4415 */
4416HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4417{
4418 /* Because control characters in aKey have caused problems in the settings
4419 * they are rejected unless the key should be deleted. */
4420 if (!aValue.isEmpty())
4421 {
4422 for (size_t i = 0; i < aKey.length(); ++i)
4423 {
4424 char ch = aKey[i];
4425 if (RTLocCIsCntrl(ch))
4426 return E_INVALIDARG;
4427 }
4428 }
4429
4430 Utf8Str strOldValue; // empty
4431
4432 // locking note: we only hold the read lock briefly to look up the old value,
4433 // then release it and call the onExtraCanChange callbacks. There is a small
4434 // chance of a race insofar as the callback might be called twice if two callers
4435 // change the same key at the same time, but that's a much better solution
4436 // than the deadlock we had here before. The actual changing of the extradata
4437 // is then performed under the write lock and race-free.
4438
4439 // look up the old value first; if nothing has changed then we need not do anything
4440 {
4441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4442
4443 // For snapshots don't even think about allowing changes, extradata
4444 // is global for a machine, so there is nothing snapshot specific.
4445 if (i_isSnapshotMachine())
4446 return setError(VBOX_E_INVALID_VM_STATE,
4447 tr("Cannot set extradata for a snapshot"));
4448
4449 // check if the right IMachine instance is used
4450 if (mData->mRegistered && !i_isSessionMachine())
4451 return setError(VBOX_E_INVALID_VM_STATE,
4452 tr("Cannot set extradata for an immutable machine"));
4453
4454 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4455 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4456 strOldValue = it->second;
4457 }
4458
4459 bool fChanged;
4460 if ((fChanged = (strOldValue != aValue)))
4461 {
4462 // ask for permission from all listeners outside the locks;
4463 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4464 // lock to copy the list of callbacks to invoke
4465 Bstr bstrError;
4466 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4467 {
4468 const char *sep = bstrError.isEmpty() ? "" : ": ";
4469 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4470 return setError(E_ACCESSDENIED,
4471 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4472 aKey.c_str(),
4473 aValue.c_str(),
4474 sep,
4475 bstrError.raw());
4476 }
4477
4478 // data is changing and change not vetoed: then write it out under the lock
4479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4480
4481 if (aValue.isEmpty())
4482 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4483 else
4484 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4485 // creates a new key if needed
4486
4487 bool fNeedsGlobalSaveSettings = false;
4488 // This saving of settings is tricky: there is no "old state" for the
4489 // extradata items at all (unlike all other settings), so the old/new
4490 // settings comparison would give a wrong result!
4491 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4492
4493 if (fNeedsGlobalSaveSettings)
4494 {
4495 // save the global settings; for that we should hold only the VirtualBox lock
4496 alock.release();
4497 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4498 mParent->i_saveSettings();
4499 }
4500 }
4501
4502 // fire notification outside the lock
4503 if (fChanged)
4504 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4505
4506 return S_OK;
4507}
4508
4509HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4510{
4511 aProgress = NULL;
4512 NOREF(aSettingsFilePath);
4513 ReturnComNotImplemented();
4514}
4515
4516HRESULT Machine::saveSettings()
4517{
4518 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4519
4520 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4521 if (FAILED(hrc)) return hrc;
4522
4523 /* the settings file path may never be null */
4524 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4525
4526 /* save all VM data excluding snapshots */
4527 bool fNeedsGlobalSaveSettings = false;
4528 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4529 mlock.release();
4530
4531 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4532 {
4533 // save the global settings; for that we should hold only the VirtualBox lock
4534 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4535 hrc = mParent->i_saveSettings();
4536 }
4537
4538 return hrc;
4539}
4540
4541
4542HRESULT Machine::discardSettings()
4543{
4544 /*
4545 * We need to take the machine list lock here as well as the machine one
4546 * or we'll get into trouble should any media stuff require rolling back.
4547 *
4548 * Details:
4549 *
4550 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4551 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4552 * 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]
4553 * 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
4554 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4555 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4556 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4557 * 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
4558 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4559 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4560 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4561 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4562 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4563 * 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]
4564 * 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] (*)
4565 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4566 * 0:005> k
4567 * # Child-SP RetAddr Call Site
4568 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4569 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4570 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4571 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4572 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4573 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4574 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4575 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4576 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4577 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4578 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4579 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4580 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4581 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4582 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4583 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4584 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4585 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4586 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4587 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4588 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4589 *
4590 */
4591 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4593
4594 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4595 if (FAILED(hrc)) return hrc;
4596
4597 /*
4598 * during this rollback, the session will be notified if data has
4599 * been actually changed
4600 */
4601 i_rollback(true /* aNotify */);
4602
4603 return S_OK;
4604}
4605
4606/** @note Locks objects! */
4607HRESULT Machine::unregister(AutoCaller &autoCaller,
4608 CleanupMode_T aCleanupMode,
4609 std::vector<ComPtr<IMedium> > &aMedia)
4610{
4611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 Guid id(i_getId());
4614
4615 if (mData->mSession.mState != SessionState_Unlocked)
4616 return setError(VBOX_E_INVALID_OBJECT_STATE,
4617 tr("Cannot unregister the machine '%s' while it is locked"),
4618 mUserData->s.strName.c_str());
4619
4620 // wait for state dependents to drop to zero
4621 i_ensureNoStateDependencies(alock);
4622
4623 if (!mData->mAccessible)
4624 {
4625 // inaccessible machines can only be unregistered; uninitialize ourselves
4626 // here because currently there may be no unregistered that are inaccessible
4627 // (this state combination is not supported). Note releasing the caller and
4628 // leaving the lock before calling uninit()
4629 alock.release();
4630 autoCaller.release();
4631
4632 uninit();
4633
4634 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4635 // calls VirtualBox::i_saveSettings()
4636
4637 return S_OK;
4638 }
4639
4640 HRESULT hrc = S_OK;
4641 mData->llFilesToDelete.clear();
4642
4643 if (!mSSData->strStateFilePath.isEmpty())
4644 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4645
4646 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4647 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4648 mData->llFilesToDelete.push_back(strNVRAMFile);
4649
4650 // This list collects the medium objects from all medium attachments
4651 // which we will detach from the machine and its snapshots, in a specific
4652 // order which allows for closing all media without getting "media in use"
4653 // errors, simply by going through the list from the front to the back:
4654 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4655 // and must be closed before the parent media from the snapshots, or closing the parents
4656 // will fail because they still have children);
4657 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4658 // the root ("first") snapshot of the machine.
4659 MediaList llMedia;
4660
4661 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4662 && mMediumAttachments->size()
4663 )
4664 {
4665 // we have media attachments: detach them all and add the Medium objects to our list
4666 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4667 }
4668
4669 if (mData->mFirstSnapshot)
4670 {
4671 // add the media from the medium attachments of the snapshots to
4672 // llMedia as well, after the "main" machine media;
4673 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4674 // snapshot machine, depth first.
4675
4676 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4677 MachineState_T oldState = mData->mMachineState;
4678 mData->mMachineState = MachineState_DeletingSnapshot;
4679
4680 // make a copy of the first snapshot reference so the refcount does not
4681 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4682 // (would hang due to the AutoCaller voodoo)
4683 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4684
4685 // GO!
4686 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4687
4688 mData->mMachineState = oldState;
4689 }
4690
4691 if (FAILED(hrc))
4692 {
4693 i_rollbackMedia();
4694 return hrc;
4695 }
4696
4697 // commit all the media changes made above
4698 i_commitMedia();
4699
4700 mData->mRegistered = false;
4701
4702 // machine lock no longer needed
4703 alock.release();
4704
4705 /* Make sure that the settings of the current VM are not saved, because
4706 * they are rather crippled at this point to meet the cleanup expectations
4707 * and there's no point destroying the VM config on disk just because. */
4708 mParent->i_unmarkRegistryModified(id);
4709
4710 // return media to caller
4711 aMedia.resize(llMedia.size());
4712 size_t i = 0;
4713 for (MediaList::const_iterator
4714 it = llMedia.begin();
4715 it != llMedia.end();
4716 ++it, ++i)
4717 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4718
4719 mParent->i_unregisterMachine(this, aCleanupMode, id);
4720 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4721
4722 return S_OK;
4723}
4724
4725/**
4726 * Task record for deleting a machine config.
4727 */
4728class Machine::DeleteConfigTask
4729 : public Machine::Task
4730{
4731public:
4732 DeleteConfigTask(Machine *m,
4733 Progress *p,
4734 const Utf8Str &t,
4735 const RTCList<ComPtr<IMedium> > &llMedia,
4736 const StringsList &llFilesToDelete)
4737 : Task(m, p, t),
4738 m_llMedia(llMedia),
4739 m_llFilesToDelete(llFilesToDelete)
4740 {}
4741
4742private:
4743 void handler()
4744 {
4745 try
4746 {
4747 m_pMachine->i_deleteConfigHandler(*this);
4748 }
4749 catch (...)
4750 {
4751 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4752 }
4753 }
4754
4755 RTCList<ComPtr<IMedium> > m_llMedia;
4756 StringsList m_llFilesToDelete;
4757
4758 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4759};
4760
4761/**
4762 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4763 * SessionMachine::taskHandler().
4764 *
4765 * @note Locks this object for writing.
4766 *
4767 * @param task
4768 */
4769void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4770{
4771 LogFlowThisFuncEnter();
4772
4773 AutoCaller autoCaller(this);
4774 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4775 if (FAILED(autoCaller.hrc()))
4776 {
4777 /* we might have been uninitialized because the session was accidentally
4778 * closed by the client, so don't assert */
4779 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4780 task.m_pProgress->i_notifyComplete(hrc);
4781 LogFlowThisFuncLeave();
4782 return;
4783 }
4784
4785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
4787 HRESULT hrc;
4788 try
4789 {
4790 ULONG uLogHistoryCount = 3;
4791 ComPtr<ISystemProperties> systemProperties;
4792 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4793 if (FAILED(hrc)) throw hrc;
4794
4795 if (!systemProperties.isNull())
4796 {
4797 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4798 if (FAILED(hrc)) throw hrc;
4799 }
4800
4801 MachineState_T oldState = mData->mMachineState;
4802 i_setMachineState(MachineState_SettingUp);
4803 alock.release();
4804 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4805 {
4806 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4807 {
4808 AutoCaller mac(pMedium);
4809 if (FAILED(mac.hrc())) throw mac.hrc();
4810 Utf8Str strLocation = pMedium->i_getLocationFull();
4811 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4812 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4813 if (FAILED(hrc)) throw hrc;
4814 }
4815 if (pMedium->i_isMediumFormatFile())
4816 {
4817 ComPtr<IProgress> pProgress2;
4818 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4819 if (FAILED(hrc)) throw hrc;
4820 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4821 if (FAILED(hrc)) throw hrc;
4822 }
4823
4824 /* Close the medium, deliberately without checking the return
4825 * code, and without leaving any trace in the error info, as
4826 * a failure here is a very minor issue, which shouldn't happen
4827 * as above we even managed to delete the medium. */
4828 {
4829 ErrorInfoKeeper eik;
4830 pMedium->Close();
4831 }
4832 }
4833 i_setMachineState(oldState);
4834 alock.acquire();
4835
4836 // delete the files pushed on the task list by Machine::Delete()
4837 // (this includes saved states of the machine and snapshots and
4838 // medium storage files from the IMedium list passed in, and the
4839 // machine XML file)
4840 for (StringsList::const_iterator
4841 it = task.m_llFilesToDelete.begin();
4842 it != task.m_llFilesToDelete.end();
4843 ++it)
4844 {
4845 const Utf8Str &strFile = *it;
4846 LogFunc(("Deleting file %s\n", strFile.c_str()));
4847 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4848 if (FAILED(hrc)) throw hrc;
4849 i_deleteFile(strFile);
4850 }
4851
4852 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4853 if (FAILED(hrc)) throw hrc;
4854
4855 /* delete the settings only when the file actually exists */
4856 if (mData->pMachineConfigFile->fileExists())
4857 {
4858 /* Delete any backup or uncommitted XML files. Ignore failures.
4859 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4860 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4861 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4862 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4863 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4864 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4865
4866 /* delete the Logs folder, nothing important should be left
4867 * there (we don't check for errors because the user might have
4868 * some private files there that we don't want to delete) */
4869 Utf8Str logFolder;
4870 getLogFolder(logFolder);
4871 Assert(logFolder.length());
4872 if (RTDirExists(logFolder.c_str()))
4873 {
4874 /* Delete all VBox.log[.N] files from the Logs folder
4875 * (this must be in sync with the rotation logic in
4876 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4877 * files that may have been created by the GUI. */
4878 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4879 i_deleteFile(log, true /* fIgnoreFailures */);
4880 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4881 i_deleteFile(log, true /* fIgnoreFailures */);
4882 for (ULONG i = uLogHistoryCount; i > 0; i--)
4883 {
4884 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4885 i_deleteFile(log, true /* fIgnoreFailures */);
4886 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4887 i_deleteFile(log, true /* fIgnoreFailures */);
4888 }
4889 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4890 i_deleteFile(log, true /* fIgnoreFailures */);
4891#if defined(RT_OS_WINDOWS)
4892 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4893 i_deleteFile(log, true /* fIgnoreFailures */);
4894 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4895 i_deleteFile(log, true /* fIgnoreFailures */);
4896#endif
4897
4898 RTDirRemove(logFolder.c_str());
4899 }
4900
4901 /* delete the Snapshots folder, nothing important should be left
4902 * there (we don't check for errors because the user might have
4903 * some private files there that we don't want to delete) */
4904 Utf8Str strFullSnapshotFolder;
4905 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4906 Assert(!strFullSnapshotFolder.isEmpty());
4907 if (RTDirExists(strFullSnapshotFolder.c_str()))
4908 RTDirRemove(strFullSnapshotFolder.c_str());
4909
4910 // delete the directory that contains the settings file, but only
4911 // if it matches the VM name
4912 Utf8Str settingsDir;
4913 if (i_isInOwnDir(&settingsDir))
4914 RTDirRemove(settingsDir.c_str());
4915 }
4916
4917 alock.release();
4918
4919 mParent->i_saveModifiedRegistries();
4920 }
4921 catch (HRESULT hrcXcpt)
4922 {
4923 hrc = hrcXcpt;
4924 }
4925
4926 task.m_pProgress->i_notifyComplete(hrc);
4927
4928 LogFlowThisFuncLeave();
4929}
4930
4931HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4932{
4933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4934
4935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4936 if (FAILED(hrc)) return hrc;
4937
4938 if (mData->mRegistered)
4939 return setError(VBOX_E_INVALID_VM_STATE,
4940 tr("Cannot delete settings of a registered machine"));
4941
4942 // collect files to delete
4943 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4944 // machine config file
4945 if (mData->pMachineConfigFile->fileExists())
4946 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4947 // backup of machine config file
4948 Utf8Str strTmp(mData->m_strConfigFileFull);
4949 strTmp.append("-prev");
4950 if (RTFileExists(strTmp.c_str()))
4951 llFilesToDelete.push_back(strTmp);
4952
4953 RTCList<ComPtr<IMedium> > llMedia;
4954 for (size_t i = 0; i < aMedia.size(); ++i)
4955 {
4956 IMedium *pIMedium(aMedia[i]);
4957 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4958 if (pMedium.isNull())
4959 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4960 SafeArray<BSTR> ids;
4961 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4962 if (FAILED(hrc)) return hrc;
4963 /* At this point the medium should not have any back references
4964 * anymore. If it has it is attached to another VM and *must* not
4965 * deleted. */
4966 if (ids.size() < 1)
4967 llMedia.append(pMedium);
4968 }
4969
4970 ComObjPtr<Progress> pProgress;
4971 pProgress.createObject();
4972 hrc = pProgress->init(i_getVirtualBox(),
4973 static_cast<IMachine*>(this) /* aInitiator */,
4974 tr("Deleting files"),
4975 true /* fCancellable */,
4976 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4977 tr("Collecting file inventory"));
4978 if (FAILED(hrc))
4979 return hrc;
4980
4981 /* create and start the task on a separate thread (note that it will not
4982 * start working until we release alock) */
4983 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
4984 hrc = pTask->createThread();
4985 pTask = NULL;
4986 if (FAILED(hrc))
4987 return hrc;
4988
4989 pProgress.queryInterfaceTo(aProgress.asOutParam());
4990
4991 LogFlowFuncLeave();
4992
4993 return S_OK;
4994}
4995
4996HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
4997{
4998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4999
5000 ComObjPtr<Snapshot> pSnapshot;
5001 HRESULT hrc;
5002
5003 if (aNameOrId.isEmpty())
5004 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5005 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5006 else
5007 {
5008 Guid uuid(aNameOrId);
5009 if (uuid.isValid())
5010 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5011 else
5012 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5013 }
5014 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5015
5016 return hrc;
5017}
5018
5019HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5020 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5021{
5022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5023
5024 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5025 if (FAILED(hrc)) return hrc;
5026
5027 ComObjPtr<SharedFolder> sharedFolder;
5028 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5029 if (SUCCEEDED(hrc))
5030 return setError(VBOX_E_OBJECT_IN_USE,
5031 tr("Shared folder named '%s' already exists"),
5032 aName.c_str());
5033
5034 sharedFolder.createObject();
5035 hrc = sharedFolder->init(i_getMachine(),
5036 aName,
5037 aHostPath,
5038 !!aWritable,
5039 !!aAutomount,
5040 aAutoMountPoint,
5041 true /* fFailOnError */);
5042 if (FAILED(hrc)) return hrc;
5043
5044 i_setModified(IsModified_SharedFolders);
5045 mHWData.backup();
5046 mHWData->mSharedFolders.push_back(sharedFolder);
5047
5048 /* inform the direct session if any */
5049 alock.release();
5050 i_onSharedFolderChange();
5051
5052 return S_OK;
5053}
5054
5055HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5056{
5057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5058
5059 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5060 if (FAILED(hrc)) return hrc;
5061
5062 ComObjPtr<SharedFolder> sharedFolder;
5063 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5064 if (FAILED(hrc)) return hrc;
5065
5066 i_setModified(IsModified_SharedFolders);
5067 mHWData.backup();
5068 mHWData->mSharedFolders.remove(sharedFolder);
5069
5070 /* inform the direct session if any */
5071 alock.release();
5072 i_onSharedFolderChange();
5073
5074 return S_OK;
5075}
5076
5077HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5078{
5079 /* start with No */
5080 *aCanShow = FALSE;
5081
5082 ComPtr<IInternalSessionControl> directControl;
5083 {
5084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5085
5086 if (mData->mSession.mState != SessionState_Locked)
5087 return setError(VBOX_E_INVALID_VM_STATE,
5088 tr("Machine is not locked for session (session state: %s)"),
5089 Global::stringifySessionState(mData->mSession.mState));
5090
5091 if (mData->mSession.mLockType == LockType_VM)
5092 directControl = mData->mSession.mDirectControl;
5093 }
5094
5095 /* ignore calls made after #OnSessionEnd() is called */
5096 if (!directControl)
5097 return S_OK;
5098
5099 LONG64 dummy;
5100 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5101}
5102
5103HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5104{
5105 ComPtr<IInternalSessionControl> directControl;
5106 {
5107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5108
5109 if (mData->mSession.mState != SessionState_Locked)
5110 return setError(E_FAIL,
5111 tr("Machine is not locked for session (session state: %s)"),
5112 Global::stringifySessionState(mData->mSession.mState));
5113
5114 if (mData->mSession.mLockType == LockType_VM)
5115 directControl = mData->mSession.mDirectControl;
5116 }
5117
5118 /* ignore calls made after #OnSessionEnd() is called */
5119 if (!directControl)
5120 return S_OK;
5121
5122 BOOL dummy;
5123 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5124}
5125
5126#ifdef VBOX_WITH_GUEST_PROPS
5127/**
5128 * Look up a guest property in VBoxSVC's internal structures.
5129 */
5130HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5131 com::Utf8Str &aValue,
5132 LONG64 *aTimestamp,
5133 com::Utf8Str &aFlags) const
5134{
5135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5136
5137 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5138 if (it != mHWData->mGuestProperties.end())
5139 {
5140 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5141 aValue = it->second.strValue;
5142 *aTimestamp = it->second.mTimestamp;
5143 GuestPropWriteFlags(it->second.mFlags, szFlags);
5144 aFlags = Utf8Str(szFlags);
5145 }
5146
5147 return S_OK;
5148}
5149
5150/**
5151 * Query the VM that a guest property belongs to for the property.
5152 * @returns E_ACCESSDENIED if the VM process is not available or not
5153 * currently handling queries and the lookup should then be done in
5154 * VBoxSVC.
5155 */
5156HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5157 com::Utf8Str &aValue,
5158 LONG64 *aTimestamp,
5159 com::Utf8Str &aFlags) const
5160{
5161 HRESULT hrc = S_OK;
5162 Bstr bstrValue;
5163 Bstr bstrFlags;
5164
5165 ComPtr<IInternalSessionControl> directControl;
5166 {
5167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5168 if (mData->mSession.mLockType == LockType_VM)
5169 directControl = mData->mSession.mDirectControl;
5170 }
5171
5172 /* ignore calls made after #OnSessionEnd() is called */
5173 if (!directControl)
5174 hrc = E_ACCESSDENIED;
5175 else
5176 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5177 0 /* accessMode */,
5178 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5179
5180 aValue = bstrValue;
5181 aFlags = bstrFlags;
5182
5183 return hrc;
5184}
5185#endif // VBOX_WITH_GUEST_PROPS
5186
5187HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5188 com::Utf8Str &aValue,
5189 LONG64 *aTimestamp,
5190 com::Utf8Str &aFlags)
5191{
5192#ifndef VBOX_WITH_GUEST_PROPS
5193 ReturnComNotImplemented();
5194#else // VBOX_WITH_GUEST_PROPS
5195
5196 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5197
5198 if (hrc == E_ACCESSDENIED)
5199 /* The VM is not running or the service is not (yet) accessible */
5200 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5201 return hrc;
5202#endif // VBOX_WITH_GUEST_PROPS
5203}
5204
5205HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5206{
5207 LONG64 dummyTimestamp;
5208 com::Utf8Str dummyFlags;
5209 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5210
5211}
5212HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5213{
5214 com::Utf8Str dummyFlags;
5215 com::Utf8Str dummyValue;
5216 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5217}
5218
5219#ifdef VBOX_WITH_GUEST_PROPS
5220/**
5221 * Set a guest property in VBoxSVC's internal structures.
5222 */
5223HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5224 const com::Utf8Str &aFlags, bool fDelete)
5225{
5226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5227 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5228 if (FAILED(hrc)) return hrc;
5229
5230 try
5231 {
5232 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5233 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5234 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5235
5236 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5237 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5238
5239 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5240 if (it == mHWData->mGuestProperties.end())
5241 {
5242 if (!fDelete)
5243 {
5244 i_setModified(IsModified_MachineData);
5245 mHWData.backupEx();
5246
5247 RTTIMESPEC time;
5248 HWData::GuestProperty prop;
5249 prop.strValue = aValue;
5250 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5251 prop.mFlags = fFlags;
5252 mHWData->mGuestProperties[aName] = prop;
5253 }
5254 }
5255 else
5256 {
5257 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5258 {
5259 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5260 }
5261 else
5262 {
5263 i_setModified(IsModified_MachineData);
5264 mHWData.backupEx();
5265
5266 /* The backupEx() operation invalidates our iterator,
5267 * so get a new one. */
5268 it = mHWData->mGuestProperties.find(aName);
5269 Assert(it != mHWData->mGuestProperties.end());
5270
5271 if (!fDelete)
5272 {
5273 RTTIMESPEC time;
5274 it->second.strValue = aValue;
5275 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5276 it->second.mFlags = fFlags;
5277 }
5278 else
5279 mHWData->mGuestProperties.erase(it);
5280 }
5281 }
5282
5283 if (SUCCEEDED(hrc))
5284 {
5285 alock.release();
5286
5287 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5288 }
5289 }
5290 catch (std::bad_alloc &)
5291 {
5292 hrc = E_OUTOFMEMORY;
5293 }
5294
5295 return hrc;
5296}
5297
5298/**
5299 * Set a property on the VM that that property belongs to.
5300 * @returns E_ACCESSDENIED if the VM process is not available or not
5301 * currently handling queries and the setting should then be done in
5302 * VBoxSVC.
5303 */
5304HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5305 const com::Utf8Str &aFlags, bool fDelete)
5306{
5307 HRESULT hrc;
5308
5309 try
5310 {
5311 ComPtr<IInternalSessionControl> directControl;
5312 {
5313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5314 if (mData->mSession.mLockType == LockType_VM)
5315 directControl = mData->mSession.mDirectControl;
5316 }
5317
5318 Bstr dummy1; /* will not be changed (setter) */
5319 Bstr dummy2; /* will not be changed (setter) */
5320 LONG64 dummy64;
5321 if (!directControl)
5322 hrc = E_ACCESSDENIED;
5323 else
5324 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5325 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5326 fDelete ? 2 : 1 /* accessMode */,
5327 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5328 }
5329 catch (std::bad_alloc &)
5330 {
5331 hrc = E_OUTOFMEMORY;
5332 }
5333
5334 return hrc;
5335}
5336#endif // VBOX_WITH_GUEST_PROPS
5337
5338HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5339 const com::Utf8Str &aFlags)
5340{
5341#ifndef VBOX_WITH_GUEST_PROPS
5342 ReturnComNotImplemented();
5343#else // VBOX_WITH_GUEST_PROPS
5344
5345 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5346 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5347
5348 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5349 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5350
5351 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5352 if (hrc == E_ACCESSDENIED)
5353 /* The VM is not running or the service is not (yet) accessible */
5354 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5355 return hrc;
5356#endif // VBOX_WITH_GUEST_PROPS
5357}
5358
5359HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5360{
5361 return setGuestProperty(aProperty, aValue, "");
5362}
5363
5364HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5365{
5366#ifndef VBOX_WITH_GUEST_PROPS
5367 ReturnComNotImplemented();
5368#else // VBOX_WITH_GUEST_PROPS
5369 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5370 if (hrc == E_ACCESSDENIED)
5371 /* The VM is not running or the service is not (yet) accessible */
5372 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5373 return hrc;
5374#endif // VBOX_WITH_GUEST_PROPS
5375}
5376
5377#ifdef VBOX_WITH_GUEST_PROPS
5378/**
5379 * Enumerate the guest properties in VBoxSVC's internal structures.
5380 */
5381HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5382 std::vector<com::Utf8Str> &aNames,
5383 std::vector<com::Utf8Str> &aValues,
5384 std::vector<LONG64> &aTimestamps,
5385 std::vector<com::Utf8Str> &aFlags)
5386{
5387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5388 Utf8Str strPatterns(aPatterns);
5389
5390 /*
5391 * Look for matching patterns and build up a list.
5392 */
5393 HWData::GuestPropertyMap propMap;
5394 for (HWData::GuestPropertyMap::const_iterator
5395 it = mHWData->mGuestProperties.begin();
5396 it != mHWData->mGuestProperties.end();
5397 ++it)
5398 {
5399 if ( strPatterns.isEmpty()
5400 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5401 RTSTR_MAX,
5402 it->first.c_str(),
5403 RTSTR_MAX,
5404 NULL)
5405 )
5406 propMap.insert(*it);
5407 }
5408
5409 alock.release();
5410
5411 /*
5412 * And build up the arrays for returning the property information.
5413 */
5414 size_t cEntries = propMap.size();
5415
5416 aNames.resize(cEntries);
5417 aValues.resize(cEntries);
5418 aTimestamps.resize(cEntries);
5419 aFlags.resize(cEntries);
5420
5421 size_t i = 0;
5422 for (HWData::GuestPropertyMap::const_iterator
5423 it = propMap.begin();
5424 it != propMap.end();
5425 ++it, ++i)
5426 {
5427 aNames[i] = it->first;
5428 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5429 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5430
5431 aValues[i] = it->second.strValue;
5432 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5433 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5434
5435 aTimestamps[i] = it->second.mTimestamp;
5436
5437 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5438 GuestPropWriteFlags(it->second.mFlags, szFlags);
5439 aFlags[i] = szFlags;
5440 }
5441
5442 return S_OK;
5443}
5444
5445/**
5446 * Enumerate the properties managed by a VM.
5447 * @returns E_ACCESSDENIED if the VM process is not available or not
5448 * currently handling queries and the setting should then be done in
5449 * VBoxSVC.
5450 */
5451HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5452 std::vector<com::Utf8Str> &aNames,
5453 std::vector<com::Utf8Str> &aValues,
5454 std::vector<LONG64> &aTimestamps,
5455 std::vector<com::Utf8Str> &aFlags)
5456{
5457 HRESULT hrc;
5458 ComPtr<IInternalSessionControl> directControl;
5459 {
5460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5461 if (mData->mSession.mLockType == LockType_VM)
5462 directControl = mData->mSession.mDirectControl;
5463 }
5464
5465 com::SafeArray<BSTR> bNames;
5466 com::SafeArray<BSTR> bValues;
5467 com::SafeArray<LONG64> bTimestamps;
5468 com::SafeArray<BSTR> bFlags;
5469
5470 if (!directControl)
5471 hrc = E_ACCESSDENIED;
5472 else
5473 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5474 ComSafeArrayAsOutParam(bNames),
5475 ComSafeArrayAsOutParam(bValues),
5476 ComSafeArrayAsOutParam(bTimestamps),
5477 ComSafeArrayAsOutParam(bFlags));
5478 size_t i;
5479 aNames.resize(bNames.size());
5480 for (i = 0; i < bNames.size(); ++i)
5481 aNames[i] = Utf8Str(bNames[i]);
5482 aValues.resize(bValues.size());
5483 for (i = 0; i < bValues.size(); ++i)
5484 aValues[i] = Utf8Str(bValues[i]);
5485 aTimestamps.resize(bTimestamps.size());
5486 for (i = 0; i < bTimestamps.size(); ++i)
5487 aTimestamps[i] = bTimestamps[i];
5488 aFlags.resize(bFlags.size());
5489 for (i = 0; i < bFlags.size(); ++i)
5490 aFlags[i] = Utf8Str(bFlags[i]);
5491
5492 return hrc;
5493}
5494#endif // VBOX_WITH_GUEST_PROPS
5495HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5496 std::vector<com::Utf8Str> &aNames,
5497 std::vector<com::Utf8Str> &aValues,
5498 std::vector<LONG64> &aTimestamps,
5499 std::vector<com::Utf8Str> &aFlags)
5500{
5501#ifndef VBOX_WITH_GUEST_PROPS
5502 ReturnComNotImplemented();
5503#else // VBOX_WITH_GUEST_PROPS
5504
5505 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5506
5507 if (hrc == E_ACCESSDENIED)
5508 /* The VM is not running or the service is not (yet) accessible */
5509 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5510 return hrc;
5511#endif // VBOX_WITH_GUEST_PROPS
5512}
5513
5514HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5515 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5516{
5517 MediumAttachmentList atts;
5518
5519 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5520 if (FAILED(hrc)) return hrc;
5521
5522 aMediumAttachments.resize(atts.size());
5523 size_t i = 0;
5524 for (MediumAttachmentList::const_iterator
5525 it = atts.begin();
5526 it != atts.end();
5527 ++it, ++i)
5528 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5529
5530 return S_OK;
5531}
5532
5533HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5534 LONG aControllerPort,
5535 LONG aDevice,
5536 ComPtr<IMediumAttachment> &aAttachment)
5537{
5538 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5539 aName.c_str(), aControllerPort, aDevice));
5540
5541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5542
5543 aAttachment = NULL;
5544
5545 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5546 aName,
5547 aControllerPort,
5548 aDevice);
5549 if (pAttach.isNull())
5550 return setError(VBOX_E_OBJECT_NOT_FOUND,
5551 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5552 aDevice, aControllerPort, aName.c_str());
5553
5554 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5555
5556 return S_OK;
5557}
5558
5559
5560HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5561 StorageBus_T aConnectionType,
5562 ComPtr<IStorageController> &aController)
5563{
5564 if ( (aConnectionType <= StorageBus_Null)
5565 || (aConnectionType > StorageBus_VirtioSCSI))
5566 return setError(E_INVALIDARG,
5567 tr("Invalid connection type: %d"),
5568 aConnectionType);
5569
5570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5571
5572 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5573 if (FAILED(hrc)) return hrc;
5574
5575 /* try to find one with the name first. */
5576 ComObjPtr<StorageController> ctrl;
5577
5578 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5579 if (SUCCEEDED(hrc))
5580 return setError(VBOX_E_OBJECT_IN_USE,
5581 tr("Storage controller named '%s' already exists"),
5582 aName.c_str());
5583
5584 ctrl.createObject();
5585
5586 /* get a new instance number for the storage controller */
5587 ULONG ulInstance = 0;
5588 bool fBootable = true;
5589 for (StorageControllerList::const_iterator
5590 it = mStorageControllers->begin();
5591 it != mStorageControllers->end();
5592 ++it)
5593 {
5594 if ((*it)->i_getStorageBus() == aConnectionType)
5595 {
5596 ULONG ulCurInst = (*it)->i_getInstance();
5597
5598 if (ulCurInst >= ulInstance)
5599 ulInstance = ulCurInst + 1;
5600
5601 /* Only one controller of each type can be marked as bootable. */
5602 if ((*it)->i_getBootable())
5603 fBootable = false;
5604 }
5605 }
5606
5607 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5608 if (FAILED(hrc)) return hrc;
5609
5610 i_setModified(IsModified_Storage);
5611 mStorageControllers.backup();
5612 mStorageControllers->push_back(ctrl);
5613
5614 ctrl.queryInterfaceTo(aController.asOutParam());
5615
5616 /* inform the direct session if any */
5617 alock.release();
5618 i_onStorageControllerChange(i_getId(), aName);
5619
5620 return S_OK;
5621}
5622
5623HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5624 ComPtr<IStorageController> &aStorageController)
5625{
5626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5627
5628 ComObjPtr<StorageController> ctrl;
5629
5630 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5631 if (SUCCEEDED(hrc))
5632 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5633
5634 return hrc;
5635}
5636
5637HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5638 ULONG aInstance,
5639 ComPtr<IStorageController> &aStorageController)
5640{
5641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5642
5643 for (StorageControllerList::const_iterator
5644 it = mStorageControllers->begin();
5645 it != mStorageControllers->end();
5646 ++it)
5647 {
5648 if ( (*it)->i_getStorageBus() == aConnectionType
5649 && (*it)->i_getInstance() == aInstance)
5650 {
5651 (*it).queryInterfaceTo(aStorageController.asOutParam());
5652 return S_OK;
5653 }
5654 }
5655
5656 return setError(VBOX_E_OBJECT_NOT_FOUND,
5657 tr("Could not find a storage controller with instance number '%lu'"),
5658 aInstance);
5659}
5660
5661HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5662{
5663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5664
5665 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5666 if (FAILED(hrc)) return hrc;
5667
5668 ComObjPtr<StorageController> ctrl;
5669
5670 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5671 if (SUCCEEDED(hrc))
5672 {
5673 /* Ensure that only one controller of each type is marked as bootable. */
5674 if (aBootable == TRUE)
5675 {
5676 for (StorageControllerList::const_iterator
5677 it = mStorageControllers->begin();
5678 it != mStorageControllers->end();
5679 ++it)
5680 {
5681 ComObjPtr<StorageController> aCtrl = (*it);
5682
5683 if ( (aCtrl->i_getName() != aName)
5684 && aCtrl->i_getBootable() == TRUE
5685 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5686 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5687 {
5688 aCtrl->i_setBootable(FALSE);
5689 break;
5690 }
5691 }
5692 }
5693
5694 if (SUCCEEDED(hrc))
5695 {
5696 ctrl->i_setBootable(aBootable);
5697 i_setModified(IsModified_Storage);
5698 }
5699 }
5700
5701 if (SUCCEEDED(hrc))
5702 {
5703 /* inform the direct session if any */
5704 alock.release();
5705 i_onStorageControllerChange(i_getId(), aName);
5706 }
5707
5708 return hrc;
5709}
5710
5711HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5712{
5713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5714
5715 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5716 if (FAILED(hrc)) return hrc;
5717
5718 ComObjPtr<StorageController> ctrl;
5719 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5720 if (FAILED(hrc)) return hrc;
5721
5722 MediumAttachmentList llDetachedAttachments;
5723 {
5724 /* find all attached devices to the appropriate storage controller and detach them all */
5725 // make a temporary list because detachDevice invalidates iterators into
5726 // mMediumAttachments
5727 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5728
5729 for (MediumAttachmentList::const_iterator
5730 it = llAttachments2.begin();
5731 it != llAttachments2.end();
5732 ++it)
5733 {
5734 MediumAttachment *pAttachTemp = *it;
5735
5736 AutoCaller localAutoCaller(pAttachTemp);
5737 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
5738
5739 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5740
5741 if (pAttachTemp->i_getControllerName() == aName)
5742 {
5743 llDetachedAttachments.push_back(pAttachTemp);
5744 hrc = i_detachDevice(pAttachTemp, alock, NULL);
5745 if (FAILED(hrc)) return hrc;
5746 }
5747 }
5748 }
5749
5750 /* send event about detached devices before removing parent controller */
5751 for (MediumAttachmentList::const_iterator
5752 it = llDetachedAttachments.begin();
5753 it != llDetachedAttachments.end();
5754 ++it)
5755 {
5756 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5757 }
5758
5759 /* We can remove it now. */
5760 i_setModified(IsModified_Storage);
5761 mStorageControllers.backup();
5762
5763 ctrl->i_unshare();
5764
5765 mStorageControllers->remove(ctrl);
5766
5767 /* inform the direct session if any */
5768 alock.release();
5769 i_onStorageControllerChange(i_getId(), aName);
5770
5771 return S_OK;
5772}
5773
5774HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5775 ComPtr<IUSBController> &aController)
5776{
5777 if ( (aType <= USBControllerType_Null)
5778 || (aType >= USBControllerType_Last))
5779 return setError(E_INVALIDARG,
5780 tr("Invalid USB controller type: %d"),
5781 aType);
5782
5783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5784
5785 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5786 if (FAILED(hrc)) return hrc;
5787
5788 /* try to find one with the same type first. */
5789 ComObjPtr<USBController> ctrl;
5790
5791 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5792 if (SUCCEEDED(hrc))
5793 return setError(VBOX_E_OBJECT_IN_USE,
5794 tr("USB controller named '%s' already exists"),
5795 aName.c_str());
5796
5797 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5798 ChipsetType_T enmChipsetType;
5799 hrc = mPlatform->getChipsetType(&enmChipsetType);
5800 if (FAILED(hrc))
5801 return hrc;
5802
5803 ULONG maxInstances;
5804 hrc = mPlatformProperties->GetMaxInstancesOfUSBControllerType(enmChipsetType, aType, &maxInstances);
5805 if (FAILED(hrc))
5806 return hrc;
5807
5808 ULONG cInstances = i_getUSBControllerCountByType(aType);
5809 if (cInstances >= maxInstances)
5810 return setError(E_INVALIDARG,
5811 tr("Too many USB controllers of this type"));
5812
5813 ctrl.createObject();
5814
5815 hrc = ctrl->init(this, aName, aType);
5816 if (FAILED(hrc)) return hrc;
5817
5818 i_setModified(IsModified_USB);
5819 mUSBControllers.backup();
5820 mUSBControllers->push_back(ctrl);
5821
5822 ctrl.queryInterfaceTo(aController.asOutParam());
5823
5824 /* inform the direct session if any */
5825 alock.release();
5826 i_onUSBControllerChange();
5827
5828 return S_OK;
5829}
5830
5831HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
5832{
5833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5834
5835 ComObjPtr<USBController> ctrl;
5836
5837 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5838 if (SUCCEEDED(hrc))
5839 ctrl.queryInterfaceTo(aController.asOutParam());
5840
5841 return hrc;
5842}
5843
5844HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
5845 ULONG *aControllers)
5846{
5847 if ( (aType <= USBControllerType_Null)
5848 || (aType >= USBControllerType_Last))
5849 return setError(E_INVALIDARG,
5850 tr("Invalid USB controller type: %d"),
5851 aType);
5852
5853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5854
5855 ComObjPtr<USBController> ctrl;
5856
5857 *aControllers = i_getUSBControllerCountByType(aType);
5858
5859 return S_OK;
5860}
5861
5862HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
5863{
5864
5865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5866
5867 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5868 if (FAILED(hrc)) return hrc;
5869
5870 ComObjPtr<USBController> ctrl;
5871 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5872 if (FAILED(hrc)) return hrc;
5873
5874 i_setModified(IsModified_USB);
5875 mUSBControllers.backup();
5876
5877 ctrl->i_unshare();
5878
5879 mUSBControllers->remove(ctrl);
5880
5881 /* inform the direct session if any */
5882 alock.release();
5883 i_onUSBControllerChange();
5884
5885 return S_OK;
5886}
5887
5888HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
5889 ULONG *aOriginX,
5890 ULONG *aOriginY,
5891 ULONG *aWidth,
5892 ULONG *aHeight,
5893 BOOL *aEnabled)
5894{
5895 uint32_t u32OriginX= 0;
5896 uint32_t u32OriginY= 0;
5897 uint32_t u32Width = 0;
5898 uint32_t u32Height = 0;
5899 uint16_t u16Flags = 0;
5900
5901#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5902 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5903#else
5904 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5905#endif
5906 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
5907 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5908 if (RT_FAILURE(vrc))
5909 {
5910#ifdef RT_OS_WINDOWS
5911 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5912 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5913 * So just assign fEnable to TRUE again.
5914 * The right fix would be to change GUI API wrappers to make sure that parameters
5915 * are changed only if API succeeds.
5916 */
5917 *aEnabled = TRUE;
5918#endif
5919 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5920 tr("Saved guest size is not available (%Rrc)"),
5921 vrc);
5922 }
5923
5924 *aOriginX = u32OriginX;
5925 *aOriginY = u32OriginY;
5926 *aWidth = u32Width;
5927 *aHeight = u32Height;
5928 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5929
5930 return S_OK;
5931}
5932
5933HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
5934 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
5935{
5936 if (aScreenId != 0)
5937 return E_NOTIMPL;
5938
5939 if ( aBitmapFormat != BitmapFormat_BGR0
5940 && aBitmapFormat != BitmapFormat_BGRA
5941 && aBitmapFormat != BitmapFormat_RGBA
5942 && aBitmapFormat != BitmapFormat_PNG)
5943 return setError(E_NOTIMPL,
5944 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
5945
5946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5947
5948 uint8_t *pu8Data = NULL;
5949 uint32_t cbData = 0;
5950 uint32_t u32Width = 0;
5951 uint32_t u32Height = 0;
5952
5953#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5954 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5955#else
5956 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5957#endif
5958 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
5959 &pu8Data, &cbData, &u32Width, &u32Height);
5960 if (RT_FAILURE(vrc))
5961 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5962 tr("Saved thumbnail data is not available (%Rrc)"),
5963 vrc);
5964
5965 HRESULT hrc = S_OK;
5966
5967 *aWidth = u32Width;
5968 *aHeight = u32Height;
5969
5970 if (cbData > 0)
5971 {
5972 /* Convert pixels to the format expected by the API caller. */
5973 if (aBitmapFormat == BitmapFormat_BGR0)
5974 {
5975 /* [0] B, [1] G, [2] R, [3] 0. */
5976 aData.resize(cbData);
5977 memcpy(&aData.front(), pu8Data, cbData);
5978 }
5979 else if (aBitmapFormat == BitmapFormat_BGRA)
5980 {
5981 /* [0] B, [1] G, [2] R, [3] A. */
5982 aData.resize(cbData);
5983 for (uint32_t i = 0; i < cbData; i += 4)
5984 {
5985 aData[i] = pu8Data[i];
5986 aData[i + 1] = pu8Data[i + 1];
5987 aData[i + 2] = pu8Data[i + 2];
5988 aData[i + 3] = 0xff;
5989 }
5990 }
5991 else if (aBitmapFormat == BitmapFormat_RGBA)
5992 {
5993 /* [0] R, [1] G, [2] B, [3] A. */
5994 aData.resize(cbData);
5995 for (uint32_t i = 0; i < cbData; i += 4)
5996 {
5997 aData[i] = pu8Data[i + 2];
5998 aData[i + 1] = pu8Data[i + 1];
5999 aData[i + 2] = pu8Data[i];
6000 aData[i + 3] = 0xff;
6001 }
6002 }
6003 else if (aBitmapFormat == BitmapFormat_PNG)
6004 {
6005 uint8_t *pu8PNG = NULL;
6006 uint32_t cbPNG = 0;
6007 uint32_t cxPNG = 0;
6008 uint32_t cyPNG = 0;
6009
6010 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6011
6012 if (RT_SUCCESS(vrc))
6013 {
6014 aData.resize(cbPNG);
6015 if (cbPNG)
6016 memcpy(&aData.front(), pu8PNG, cbPNG);
6017 }
6018 else
6019 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6020
6021 RTMemFree(pu8PNG);
6022 }
6023 }
6024
6025 freeSavedDisplayScreenshot(pu8Data);
6026
6027 return hrc;
6028}
6029
6030HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6031 ULONG *aWidth,
6032 ULONG *aHeight,
6033 std::vector<BitmapFormat_T> &aBitmapFormats)
6034{
6035 if (aScreenId != 0)
6036 return E_NOTIMPL;
6037
6038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6039
6040 uint8_t *pu8Data = NULL;
6041 uint32_t cbData = 0;
6042 uint32_t u32Width = 0;
6043 uint32_t u32Height = 0;
6044
6045#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6046 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6047#else
6048 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6049#endif
6050 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6051 &pu8Data, &cbData, &u32Width, &u32Height);
6052
6053 if (RT_FAILURE(vrc))
6054 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6055 tr("Saved screenshot data is not available (%Rrc)"),
6056 vrc);
6057
6058 *aWidth = u32Width;
6059 *aHeight = u32Height;
6060 aBitmapFormats.resize(1);
6061 aBitmapFormats[0] = BitmapFormat_PNG;
6062
6063 freeSavedDisplayScreenshot(pu8Data);
6064
6065 return S_OK;
6066}
6067
6068HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6069 BitmapFormat_T aBitmapFormat,
6070 ULONG *aWidth,
6071 ULONG *aHeight,
6072 std::vector<BYTE> &aData)
6073{
6074 if (aScreenId != 0)
6075 return E_NOTIMPL;
6076
6077 if (aBitmapFormat != BitmapFormat_PNG)
6078 return E_NOTIMPL;
6079
6080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6081
6082 uint8_t *pu8Data = NULL;
6083 uint32_t cbData = 0;
6084 uint32_t u32Width = 0;
6085 uint32_t u32Height = 0;
6086
6087#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6088 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6089#else
6090 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6091#endif
6092 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6093 &pu8Data, &cbData, &u32Width, &u32Height);
6094
6095 if (RT_FAILURE(vrc))
6096 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6097 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6098 vrc);
6099
6100 *aWidth = u32Width;
6101 *aHeight = u32Height;
6102
6103 aData.resize(cbData);
6104 if (cbData)
6105 memcpy(&aData.front(), pu8Data, cbData);
6106
6107 freeSavedDisplayScreenshot(pu8Data);
6108
6109 return S_OK;
6110}
6111
6112HRESULT Machine::hotPlugCPU(ULONG aCpu)
6113{
6114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6115
6116 if (!mHWData->mCPUHotPlugEnabled)
6117 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6118
6119 if (aCpu >= mHWData->mCPUCount)
6120 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6121
6122 if (mHWData->mCPUAttached[aCpu])
6123 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6124
6125 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6126 if (FAILED(hrc)) return hrc;
6127
6128 alock.release();
6129 hrc = i_onCPUChange(aCpu, false);
6130 alock.acquire();
6131 if (FAILED(hrc)) return hrc;
6132
6133 i_setModified(IsModified_MachineData);
6134 mHWData.backup();
6135 mHWData->mCPUAttached[aCpu] = true;
6136
6137 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6138 if (Global::IsOnline(mData->mMachineState))
6139 i_saveSettings(NULL, alock);
6140
6141 return S_OK;
6142}
6143
6144HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6145{
6146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6147
6148 if (!mHWData->mCPUHotPlugEnabled)
6149 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6150
6151 if (aCpu >= SchemaDefs::MaxCPUCount)
6152 return setError(E_INVALIDARG,
6153 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6154 SchemaDefs::MaxCPUCount);
6155
6156 if (!mHWData->mCPUAttached[aCpu])
6157 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6158
6159 /* CPU 0 can't be detached */
6160 if (aCpu == 0)
6161 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6162
6163 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6164 if (FAILED(hrc)) return hrc;
6165
6166 alock.release();
6167 hrc = i_onCPUChange(aCpu, true);
6168 alock.acquire();
6169 if (FAILED(hrc)) return hrc;
6170
6171 i_setModified(IsModified_MachineData);
6172 mHWData.backup();
6173 mHWData->mCPUAttached[aCpu] = false;
6174
6175 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6176 if (Global::IsOnline(mData->mMachineState))
6177 i_saveSettings(NULL, alock);
6178
6179 return S_OK;
6180}
6181
6182HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6183{
6184 *aAttached = false;
6185
6186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6187
6188 /* If hotplug is enabled the CPU is always enabled. */
6189 if (!mHWData->mCPUHotPlugEnabled)
6190 {
6191 if (aCpu < mHWData->mCPUCount)
6192 *aAttached = true;
6193 }
6194 else
6195 {
6196 if (aCpu < SchemaDefs::MaxCPUCount)
6197 *aAttached = mHWData->mCPUAttached[aCpu];
6198 }
6199
6200 return S_OK;
6201}
6202
6203HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6204{
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 Utf8Str log = i_getLogFilename(aIdx);
6208 if (!RTFileExists(log.c_str()))
6209 log.setNull();
6210 aFilename = log;
6211
6212 return S_OK;
6213}
6214
6215HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6216{
6217 if (aSize < 0)
6218 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6219
6220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 HRESULT hrc = S_OK;
6223 Utf8Str log = i_getLogFilename(aIdx);
6224
6225 /* do not unnecessarily hold the lock while doing something which does
6226 * not need the lock and potentially takes a long time. */
6227 alock.release();
6228
6229 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6230 * keeps the SOAP reply size under 1M for the webservice (we're using
6231 * base64 encoded strings for binary data for years now, avoiding the
6232 * expansion of each byte array element to approx. 25 bytes of XML. */
6233 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6234 aData.resize(cbData);
6235
6236 int vrc = VINF_SUCCESS;
6237 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6238
6239#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6240 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6241 {
6242 PCVBOXCRYPTOIF pCryptoIf = NULL;
6243 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6244 if (SUCCEEDED(hrc))
6245 {
6246 alock.acquire();
6247
6248 SecretKey *pKey = NULL;
6249 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6250 alock.release();
6251
6252 if (RT_SUCCESS(vrc))
6253 {
6254 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6255 if (RT_SUCCESS(vrc))
6256 {
6257 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6258 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6259 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6260 if (RT_SUCCESS(vrc))
6261 {
6262 RTVfsIoStrmRelease(hVfsIosLog);
6263 hVfsIosLog = hVfsIosLogDec;
6264 }
6265 }
6266
6267 pKey->release();
6268 }
6269
6270 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6271 }
6272 }
6273 else
6274 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6275#else
6276 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6277#endif
6278 if (RT_SUCCESS(vrc))
6279 {
6280 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6281 cbData ? &aData.front() : NULL, cbData,
6282 true /*fBlocking*/, &cbData);
6283 if (RT_SUCCESS(vrc))
6284 aData.resize(cbData);
6285 else
6286 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6287
6288 RTVfsIoStrmRelease(hVfsIosLog);
6289 }
6290 else
6291 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6292
6293 if (FAILED(hrc))
6294 aData.resize(0);
6295
6296 return hrc;
6297}
6298
6299
6300/**
6301 * Currently this method doesn't attach device to the running VM,
6302 * just makes sure it's plugged on next VM start.
6303 */
6304HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6305{
6306 // lock scope
6307 {
6308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6309
6310 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6311 if (FAILED(hrc)) return hrc;
6312
6313 ChipsetType_T aChipset = ChipsetType_PIIX3;
6314 hrc = mPlatform->COMGETTER(ChipsetType)(&aChipset);
6315 if (FAILED(hrc)) return hrc;
6316
6317 if (aChipset != ChipsetType_ICH9) /** @todo BUGBUG ASSUMES x86! */
6318 {
6319 return setError(E_INVALIDARG,
6320 tr("Host PCI attachment only supported with ICH9 chipset"));
6321 }
6322
6323 // check if device with this host PCI address already attached
6324 for (HWData::PCIDeviceAssignmentList::const_iterator
6325 it = mHWData->mPCIDeviceAssignments.begin();
6326 it != mHWData->mPCIDeviceAssignments.end();
6327 ++it)
6328 {
6329 LONG iHostAddress = -1;
6330 ComPtr<PCIDeviceAttachment> pAttach;
6331 pAttach = *it;
6332 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6333 if (iHostAddress == aHostAddress)
6334 return setError(E_INVALIDARG,
6335 tr("Device with host PCI address already attached to this VM"));
6336 }
6337
6338 ComObjPtr<PCIDeviceAttachment> pda;
6339 char name[32];
6340
6341 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6342 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6343 pda.createObject();
6344 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6345 i_setModified(IsModified_MachineData);
6346 mHWData.backup();
6347 mHWData->mPCIDeviceAssignments.push_back(pda);
6348 }
6349
6350 return S_OK;
6351}
6352
6353/**
6354 * Currently this method doesn't detach device from the running VM,
6355 * just makes sure it's not plugged on next VM start.
6356 */
6357HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6358{
6359 ComObjPtr<PCIDeviceAttachment> pAttach;
6360 bool fRemoved = false;
6361 HRESULT hrc;
6362
6363 // lock scope
6364 {
6365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6366
6367 hrc = i_checkStateDependency(MutableStateDep);
6368 if (FAILED(hrc)) return hrc;
6369
6370 for (HWData::PCIDeviceAssignmentList::const_iterator
6371 it = mHWData->mPCIDeviceAssignments.begin();
6372 it != mHWData->mPCIDeviceAssignments.end();
6373 ++it)
6374 {
6375 LONG iHostAddress = -1;
6376 pAttach = *it;
6377 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6378 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6379 {
6380 i_setModified(IsModified_MachineData);
6381 mHWData.backup();
6382 mHWData->mPCIDeviceAssignments.remove(pAttach);
6383 fRemoved = true;
6384 break;
6385 }
6386 }
6387 }
6388
6389
6390 /* Fire event outside of the lock */
6391 if (fRemoved)
6392 {
6393 Assert(!pAttach.isNull());
6394 ComPtr<IEventSource> es;
6395 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
6396 Assert(SUCCEEDED(hrc));
6397 Bstr mid;
6398 hrc = this->COMGETTER(Id)(mid.asOutParam());
6399 Assert(SUCCEEDED(hrc));
6400 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6401 }
6402
6403 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6404 tr("No host PCI device %08x attached"),
6405 aHostAddress
6406 );
6407}
6408
6409HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6410{
6411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6412
6413 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6414 size_t i = 0;
6415 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6416 it = mHWData->mPCIDeviceAssignments.begin();
6417 it != mHWData->mPCIDeviceAssignments.end();
6418 ++it, ++i)
6419 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6420
6421 return S_OK;
6422}
6423
6424HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6425{
6426 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6427
6428 return S_OK;
6429}
6430
6431HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6432{
6433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6436
6437 return S_OK;
6438}
6439
6440HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6441{
6442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6443 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6444 if (SUCCEEDED(hrc))
6445 {
6446 hrc = mHWData.backupEx();
6447 if (SUCCEEDED(hrc))
6448 {
6449 i_setModified(IsModified_MachineData);
6450 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6451 }
6452 }
6453 return hrc;
6454}
6455
6456HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6457{
6458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6459 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6460 return S_OK;
6461}
6462
6463HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6464{
6465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6466 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6467 if (SUCCEEDED(hrc))
6468 {
6469 hrc = mHWData.backupEx();
6470 if (SUCCEEDED(hrc))
6471 {
6472 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6473 if (SUCCEEDED(hrc))
6474 i_setModified(IsModified_MachineData);
6475 }
6476 }
6477 return hrc;
6478}
6479
6480HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6481{
6482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6483
6484 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6485
6486 return S_OK;
6487}
6488
6489HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6490{
6491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6492 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6493 if (SUCCEEDED(hrc))
6494 {
6495 hrc = mHWData.backupEx();
6496 if (SUCCEEDED(hrc))
6497 {
6498 i_setModified(IsModified_MachineData);
6499 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6500 }
6501 }
6502 return hrc;
6503}
6504
6505HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6506{
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6510
6511 return S_OK;
6512}
6513
6514HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6515{
6516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6517
6518 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6519 if ( SUCCEEDED(hrc)
6520 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6521 {
6522 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6523 int vrc;
6524
6525 if (aAutostartEnabled)
6526 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6527 else
6528 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6529
6530 if (RT_SUCCESS(vrc))
6531 {
6532 hrc = mHWData.backupEx();
6533 if (SUCCEEDED(hrc))
6534 {
6535 i_setModified(IsModified_MachineData);
6536 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6537 }
6538 }
6539 else if (vrc == VERR_NOT_SUPPORTED)
6540 hrc = setError(VBOX_E_NOT_SUPPORTED,
6541 tr("The VM autostart feature is not supported on this platform"));
6542 else if (vrc == VERR_PATH_NOT_FOUND)
6543 hrc = setError(E_FAIL,
6544 tr("The path to the autostart database is not set"));
6545 else
6546 hrc = setError(E_UNEXPECTED,
6547 aAutostartEnabled ?
6548 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6549 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6550 mUserData->s.strName.c_str(), vrc);
6551 }
6552 return hrc;
6553}
6554
6555HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6556{
6557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6558
6559 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6560
6561 return S_OK;
6562}
6563
6564HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6565{
6566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6567 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6568 if (SUCCEEDED(hrc))
6569 {
6570 hrc = mHWData.backupEx();
6571 if (SUCCEEDED(hrc))
6572 {
6573 i_setModified(IsModified_MachineData);
6574 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6575 }
6576 }
6577 return hrc;
6578}
6579
6580HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6581{
6582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6583
6584 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6585
6586 return S_OK;
6587}
6588
6589HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6590{
6591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6592 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6593 if ( SUCCEEDED(hrc)
6594 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6595 {
6596 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6597 int vrc;
6598
6599 if (aAutostopType != AutostopType_Disabled)
6600 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6601 else
6602 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6603
6604 if (RT_SUCCESS(vrc))
6605 {
6606 hrc = mHWData.backupEx();
6607 if (SUCCEEDED(hrc))
6608 {
6609 i_setModified(IsModified_MachineData);
6610 mHWData->mAutostart.enmAutostopType = aAutostopType;
6611 }
6612 }
6613 else if (vrc == VERR_NOT_SUPPORTED)
6614 hrc = setError(VBOX_E_NOT_SUPPORTED,
6615 tr("The VM autostop feature is not supported on this platform"));
6616 else if (vrc == VERR_PATH_NOT_FOUND)
6617 hrc = setError(E_FAIL,
6618 tr("The path to the autostart database is not set"));
6619 else
6620 hrc = setError(E_UNEXPECTED,
6621 aAutostopType != AutostopType_Disabled ?
6622 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6623 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6624 mUserData->s.strName.c_str(), vrc);
6625 }
6626 return hrc;
6627}
6628
6629HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6630{
6631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6632
6633 aDefaultFrontend = mHWData->mDefaultFrontend;
6634
6635 return S_OK;
6636}
6637
6638HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6639{
6640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6641 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6642 if (SUCCEEDED(hrc))
6643 {
6644 hrc = mHWData.backupEx();
6645 if (SUCCEEDED(hrc))
6646 {
6647 i_setModified(IsModified_MachineData);
6648 mHWData->mDefaultFrontend = aDefaultFrontend;
6649 }
6650 }
6651 return hrc;
6652}
6653
6654HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6655{
6656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6657 size_t cbIcon = mUserData->s.ovIcon.size();
6658 aIcon.resize(cbIcon);
6659 if (cbIcon)
6660 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6661 return S_OK;
6662}
6663
6664HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6665{
6666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6667 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6668 if (SUCCEEDED(hrc))
6669 {
6670 i_setModified(IsModified_MachineData);
6671 mUserData.backup();
6672 size_t cbIcon = aIcon.size();
6673 mUserData->s.ovIcon.resize(cbIcon);
6674 if (cbIcon)
6675 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6676 }
6677 return hrc;
6678}
6679
6680HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6681{
6682#ifdef VBOX_WITH_USB
6683 *aUSBProxyAvailable = true;
6684#else
6685 *aUSBProxyAvailable = false;
6686#endif
6687 return S_OK;
6688}
6689
6690HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6691{
6692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6693
6694 *aVMProcessPriority = mUserData->s.enmVMPriority;
6695
6696 return S_OK;
6697}
6698
6699HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6700{
6701 RT_NOREF(aVMProcessPriority);
6702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6703 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6704 if (SUCCEEDED(hrc))
6705 {
6706 hrc = mUserData.backupEx();
6707 if (SUCCEEDED(hrc))
6708 {
6709 i_setModified(IsModified_MachineData);
6710 mUserData->s.enmVMPriority = aVMProcessPriority;
6711 }
6712 }
6713 alock.release();
6714 if (SUCCEEDED(hrc))
6715 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6716 return hrc;
6717}
6718
6719HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6720 ComPtr<IProgress> &aProgress)
6721{
6722 ComObjPtr<Progress> pP;
6723 Progress *ppP = pP;
6724 IProgress *iP = static_cast<IProgress *>(ppP);
6725 IProgress **pProgress = &iP;
6726
6727 IMachine *pTarget = aTarget;
6728
6729 /* Convert the options. */
6730 RTCList<CloneOptions_T> optList;
6731 if (aOptions.size())
6732 for (size_t i = 0; i < aOptions.size(); ++i)
6733 optList.append(aOptions[i]);
6734
6735 if (optList.contains(CloneOptions_Link))
6736 {
6737 if (!i_isSnapshotMachine())
6738 return setError(E_INVALIDARG,
6739 tr("Linked clone can only be created from a snapshot"));
6740 if (aMode != CloneMode_MachineState)
6741 return setError(E_INVALIDARG,
6742 tr("Linked clone can only be created for a single machine state"));
6743 }
6744 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6745
6746 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6747
6748 HRESULT hrc = pWorker->start(pProgress);
6749
6750 pP = static_cast<Progress *>(*pProgress);
6751 pP.queryInterfaceTo(aProgress.asOutParam());
6752
6753 return hrc;
6754
6755}
6756
6757HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6758 const com::Utf8Str &aType,
6759 ComPtr<IProgress> &aProgress)
6760{
6761 LogFlowThisFuncEnter();
6762
6763 ComObjPtr<Progress> ptrProgress;
6764 HRESULT hrc = ptrProgress.createObject();
6765 if (SUCCEEDED(hrc))
6766 {
6767 com::Utf8Str strDefaultPath;
6768 if (aTargetPath.isEmpty())
6769 i_calculateFullPath(".", strDefaultPath);
6770
6771 /* Initialize our worker task */
6772 MachineMoveVM *pTask = NULL;
6773 try
6774 {
6775 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6776 }
6777 catch (std::bad_alloc &)
6778 {
6779 return E_OUTOFMEMORY;
6780 }
6781
6782 hrc = pTask->init();//no exceptions are thrown
6783
6784 if (SUCCEEDED(hrc))
6785 {
6786 hrc = pTask->createThread();
6787 pTask = NULL; /* Consumed by createThread(). */
6788 if (SUCCEEDED(hrc))
6789 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6790 else
6791 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6792 }
6793 else
6794 delete pTask;
6795 }
6796
6797 LogFlowThisFuncLeave();
6798 return hrc;
6799
6800}
6801
6802HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6803{
6804 NOREF(aProgress);
6805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6806
6807 // This check should always fail.
6808 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6809 if (FAILED(hrc)) return hrc;
6810
6811 AssertFailedReturn(E_NOTIMPL);
6812}
6813
6814HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6815{
6816 NOREF(aSavedStateFile);
6817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6818
6819 // This check should always fail.
6820 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6821 if (FAILED(hrc)) return hrc;
6822
6823 AssertFailedReturn(E_NOTIMPL);
6824}
6825
6826HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6827{
6828 NOREF(aFRemoveFile);
6829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6830
6831 // This check should always fail.
6832 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6833 if (FAILED(hrc)) return hrc;
6834
6835 AssertFailedReturn(E_NOTIMPL);
6836}
6837
6838// public methods for internal purposes
6839/////////////////////////////////////////////////////////////////////////////
6840
6841/**
6842 * Adds the given IsModified_* flag to the dirty flags of the machine.
6843 * This must be called either during i_loadSettings or under the machine write lock.
6844 * @param fl Flag
6845 * @param fAllowStateModification If state modifications are allowed.
6846 */
6847void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6848{
6849 mData->flModifications |= fl;
6850 if (fAllowStateModification && i_isStateModificationAllowed())
6851 mData->mCurrentStateModified = true;
6852}
6853
6854/**
6855 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6856 * care of the write locking.
6857 *
6858 * @param fModification The flag to add.
6859 * @param fAllowStateModification If state modifications are allowed.
6860 */
6861void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6862{
6863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 i_setModified(fModification, fAllowStateModification);
6865}
6866
6867/**
6868 * Saves the registry entry of this machine to the given configuration node.
6869 *
6870 * @param data Machine registry data.
6871 *
6872 * @note locks this object for reading.
6873 */
6874HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6875{
6876 AutoLimitedCaller autoCaller(this);
6877 AssertComRCReturnRC(autoCaller.hrc());
6878
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 data.uuid = mData->mUuid;
6882 data.strSettingsFile = mData->m_strConfigFile;
6883
6884 return S_OK;
6885}
6886
6887/**
6888 * Calculates the absolute path of the given path taking the directory of the
6889 * machine settings file as the current directory.
6890 *
6891 * @param strPath Path to calculate the absolute path for.
6892 * @param aResult Where to put the result (used only on success, can be the
6893 * same Utf8Str instance as passed in @a aPath).
6894 * @return IPRT result.
6895 *
6896 * @note Locks this object for reading.
6897 */
6898int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6899{
6900 AutoCaller autoCaller(this);
6901 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
6902
6903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6904
6905 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6906
6907 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6908
6909 strSettingsDir.stripFilename();
6910 char szFolder[RTPATH_MAX];
6911 size_t cbFolder = sizeof(szFolder);
6912 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
6913 if (RT_SUCCESS(vrc))
6914 aResult = szFolder;
6915
6916 return vrc;
6917}
6918
6919/**
6920 * Copies strSource to strTarget, making it relative to the machine folder
6921 * if it is a subdirectory thereof, or simply copying it otherwise.
6922 *
6923 * @param strSource Path to evaluate and copy.
6924 * @param strTarget Buffer to receive target path.
6925 *
6926 * @note Locks this object for reading.
6927 */
6928void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6929 Utf8Str &strTarget)
6930{
6931 AutoCaller autoCaller(this);
6932 AssertComRCReturn(autoCaller.hrc(), (void)0);
6933
6934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6935
6936 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6937 // use strTarget as a temporary buffer to hold the machine settings dir
6938 strTarget = mData->m_strConfigFileFull;
6939 strTarget.stripFilename();
6940 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6941 {
6942 // is relative: then append what's left
6943 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6944 // for empty paths (only possible for subdirs) use "." to avoid
6945 // triggering default settings for not present config attributes.
6946 if (strTarget.isEmpty())
6947 strTarget = ".";
6948 }
6949 else
6950 // is not relative: then overwrite
6951 strTarget = strSource;
6952}
6953
6954/**
6955 * Returns the full path to the machine's log folder in the
6956 * \a aLogFolder argument.
6957 */
6958void Machine::i_getLogFolder(Utf8Str &aLogFolder)
6959{
6960 AutoCaller autoCaller(this);
6961 AssertComRCReturnVoid(autoCaller.hrc());
6962
6963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 char szTmp[RTPATH_MAX];
6966 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6967 if (RT_SUCCESS(vrc))
6968 {
6969 if (szTmp[0] && !mUserData.isNull())
6970 {
6971 char szTmp2[RTPATH_MAX];
6972 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6973 if (RT_SUCCESS(vrc))
6974 aLogFolder.printf("%s%c%s",
6975 szTmp2,
6976 RTPATH_DELIMITER,
6977 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6978 }
6979 else
6980 vrc = VERR_PATH_IS_RELATIVE;
6981 }
6982
6983 if (RT_FAILURE(vrc))
6984 {
6985 // fallback if VBOX_USER_LOGHOME is not set or invalid
6986 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6987 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6988 aLogFolder.append(RTPATH_DELIMITER);
6989 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6990 }
6991}
6992
6993/**
6994 * Returns the full path to the machine's log file for an given index.
6995 */
6996Utf8Str Machine::i_getLogFilename(ULONG idx)
6997{
6998 Utf8Str logFolder;
6999 getLogFolder(logFolder);
7000 Assert(logFolder.length());
7001
7002 Utf8Str log;
7003 if (idx == 0)
7004 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7005#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7006 else if (idx == 1)
7007 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7008 else
7009 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7010#else
7011 else
7012 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7013#endif
7014 return log;
7015}
7016
7017/**
7018 * Returns the full path to the machine's hardened log file.
7019 */
7020Utf8Str Machine::i_getHardeningLogFilename(void)
7021{
7022 Utf8Str strFilename;
7023 getLogFolder(strFilename);
7024 Assert(strFilename.length());
7025 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7026 return strFilename;
7027}
7028
7029/**
7030 * Returns the default NVRAM filename based on the location of the VM config.
7031 * Note that this is a relative path.
7032 */
7033Utf8Str Machine::i_getDefaultNVRAMFilename()
7034{
7035 AutoCaller autoCaller(this);
7036 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7037
7038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7039
7040 if (i_isSnapshotMachine())
7041 return Utf8Str::Empty;
7042
7043 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7044 strNVRAMFilePath.stripPath();
7045 strNVRAMFilePath.stripSuffix();
7046 strNVRAMFilePath += ".nvram";
7047
7048 return strNVRAMFilePath;
7049}
7050
7051/**
7052 * Returns the NVRAM filename for a new snapshot. This intentionally works
7053 * similarly to the saved state file naming. Note that this is usually
7054 * a relative path, unless the snapshot folder is absolute.
7055 */
7056Utf8Str Machine::i_getSnapshotNVRAMFilename()
7057{
7058 AutoCaller autoCaller(this);
7059 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7060
7061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7062
7063 RTTIMESPEC ts;
7064 RTTimeNow(&ts);
7065 RTTIME time;
7066 RTTimeExplode(&time, &ts);
7067
7068 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7069 strNVRAMFilePath += RTPATH_DELIMITER;
7070 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7071 time.i32Year, time.u8Month, time.u8MonthDay,
7072 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7073
7074 return strNVRAMFilePath;
7075}
7076
7077/**
7078 * Returns the version of the settings file.
7079 */
7080SettingsVersion_T Machine::i_getSettingsVersion(void)
7081{
7082 AutoCaller autoCaller(this);
7083 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7084
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 return mData->pMachineConfigFile->getSettingsVersion();
7088}
7089
7090/**
7091 * Composes a unique saved state filename based on the current system time. The filename is
7092 * granular to the second so this will work so long as no more than one snapshot is taken on
7093 * a machine per second.
7094 *
7095 * Before version 4.1, we used this formula for saved state files:
7096 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7097 * which no longer works because saved state files can now be shared between the saved state of the
7098 * "saved" machine and an online snapshot, and the following would cause problems:
7099 * 1) save machine
7100 * 2) create online snapshot from that machine state --> reusing saved state file
7101 * 3) save machine again --> filename would be reused, breaking the online snapshot
7102 *
7103 * So instead we now use a timestamp.
7104 *
7105 * @param strStateFilePath
7106 */
7107
7108void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7109{
7110 AutoCaller autoCaller(this);
7111 AssertComRCReturnVoid(autoCaller.hrc());
7112
7113 {
7114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7115 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7116 }
7117
7118 RTTIMESPEC ts;
7119 RTTimeNow(&ts);
7120 RTTIME time;
7121 RTTimeExplode(&time, &ts);
7122
7123 strStateFilePath += RTPATH_DELIMITER;
7124 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7125 time.i32Year, time.u8Month, time.u8MonthDay,
7126 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7127}
7128
7129/**
7130 * Returns whether at least one USB controller is present for the VM.
7131 */
7132bool Machine::i_isUSBControllerPresent()
7133{
7134 AutoCaller autoCaller(this);
7135 AssertComRCReturn(autoCaller.hrc(), false);
7136
7137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7138
7139 return (mUSBControllers->size() > 0);
7140}
7141
7142
7143/**
7144 * @note Locks this object for writing, calls the client process
7145 * (inside the lock).
7146 */
7147HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7148 const Utf8Str &strFrontend,
7149 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7150 ProgressProxy *aProgress)
7151{
7152 LogFlowThisFuncEnter();
7153
7154 AssertReturn(aControl, E_FAIL);
7155 AssertReturn(aProgress, E_FAIL);
7156 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7157
7158 AutoCaller autoCaller(this);
7159 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7160
7161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7162
7163 if (!mData->mRegistered)
7164 return setError(E_UNEXPECTED,
7165 tr("The machine '%s' is not registered"),
7166 mUserData->s.strName.c_str());
7167
7168 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7169
7170 /* The process started when launching a VM with separate UI/VM processes is always
7171 * the UI process, i.e. needs special handling as it won't claim the session. */
7172 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7173
7174 if (fSeparate)
7175 {
7176 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7177 return setError(VBOX_E_INVALID_OBJECT_STATE,
7178 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7179 mUserData->s.strName.c_str());
7180 }
7181 else
7182 {
7183 if ( mData->mSession.mState == SessionState_Locked
7184 || mData->mSession.mState == SessionState_Spawning
7185 || mData->mSession.mState == SessionState_Unlocking)
7186 return setError(VBOX_E_INVALID_OBJECT_STATE,
7187 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7188 mUserData->s.strName.c_str());
7189
7190 /* may not be busy */
7191 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7192 }
7193
7194 /* Hardening logging */
7195#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7196 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7197 {
7198 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7199 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7200 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7201 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7202 {
7203 Utf8Str strStartupLogDir = strHardeningLogFile;
7204 strStartupLogDir.stripFilename();
7205 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7206 file without stripping the file. */
7207 }
7208 strSupHardeningLogArg.append(strHardeningLogFile);
7209
7210 /* Remove legacy log filename to avoid confusion. */
7211 Utf8Str strOldStartupLogFile;
7212 getLogFolder(strOldStartupLogFile);
7213 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7214 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7215 }
7216#else
7217 Utf8Str strSupHardeningLogArg;
7218#endif
7219
7220 Utf8Str strAppOverride;
7221#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7222 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7223#endif
7224
7225 bool fUseVBoxSDS = false;
7226 Utf8Str strCanonicalName;
7227 if (false)
7228 { }
7229#ifdef VBOX_WITH_QTGUI
7230 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7231 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7232 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7233 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7234 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7235 {
7236 strCanonicalName = "GUI/Qt";
7237 fUseVBoxSDS = true;
7238 }
7239#endif
7240#ifdef VBOX_WITH_VBOXSDL
7241 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7242 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7243 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7244 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7245 {
7246 strCanonicalName = "GUI/SDL";
7247 fUseVBoxSDS = true;
7248 }
7249#endif
7250#ifdef VBOX_WITH_HEADLESS
7251 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7252 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7253 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7254 {
7255 strCanonicalName = "headless";
7256 }
7257#endif
7258 else
7259 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7260
7261 Utf8Str idStr = mData->mUuid.toString();
7262 Utf8Str const &strMachineName = mUserData->s.strName;
7263 RTPROCESS pid = NIL_RTPROCESS;
7264
7265#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7266 RT_NOREF(fUseVBoxSDS);
7267#else
7268 DWORD idCallerSession = ~(DWORD)0;
7269 if (fUseVBoxSDS)
7270 {
7271 /*
7272 * The VBoxSDS should be used for process launching the VM with
7273 * GUI only if the caller and the VBoxSDS are in different Windows
7274 * sessions and the caller in the interactive one.
7275 */
7276 fUseVBoxSDS = false;
7277
7278 /* Get windows session of the current process. The process token used
7279 due to several reasons:
7280 1. The token is absent for the current thread except someone set it
7281 for us.
7282 2. Needs to get the id of the session where the process is started.
7283 We only need to do this once, though. */
7284 static DWORD s_idCurrentSession = ~(DWORD)0;
7285 DWORD idCurrentSession = s_idCurrentSession;
7286 if (idCurrentSession == ~(DWORD)0)
7287 {
7288 HANDLE hCurrentProcessToken = NULL;
7289 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7290 {
7291 DWORD cbIgn = 0;
7292 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7293 s_idCurrentSession = idCurrentSession;
7294 else
7295 {
7296 idCurrentSession = ~(DWORD)0;
7297 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7298 }
7299 CloseHandle(hCurrentProcessToken);
7300 }
7301 else
7302 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7303 }
7304
7305 /* get the caller's session */
7306 HRESULT hrc = CoImpersonateClient();
7307 if (SUCCEEDED(hrc))
7308 {
7309 HANDLE hCallerThreadToken;
7310 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7311 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7312 &hCallerThreadToken))
7313 {
7314 SetLastError(NO_ERROR);
7315 DWORD cbIgn = 0;
7316 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7317 {
7318 /* Only need to use SDS if the session ID differs: */
7319 if (idCurrentSession != idCallerSession)
7320 {
7321 fUseVBoxSDS = false;
7322
7323 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7324 DWORD cbTokenGroups = 0;
7325 PTOKEN_GROUPS pTokenGroups = NULL;
7326 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7327 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7328 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7329 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7330 {
7331 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7332 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7333 PSID pInteractiveSid = NULL;
7334 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7335 {
7336 /* Iterate over the groups looking for the interactive SID: */
7337 fUseVBoxSDS = false;
7338 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7339 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7340 {
7341 fUseVBoxSDS = true;
7342 break;
7343 }
7344 FreeSid(pInteractiveSid);
7345 }
7346 }
7347 else
7348 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7349 RTMemTmpFree(pTokenGroups);
7350 }
7351 }
7352 else
7353 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7354 CloseHandle(hCallerThreadToken);
7355 }
7356 else
7357 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7358 CoRevertToSelf();
7359 }
7360 else
7361 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7362 }
7363 if (fUseVBoxSDS)
7364 {
7365 /* connect to VBoxSDS */
7366 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7367 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7368 if (FAILED(hrc))
7369 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7370 strMachineName.c_str());
7371
7372 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7373 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7374 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7375 service to access the files. */
7376 hrc = CoSetProxyBlanket(pVBoxSDS,
7377 RPC_C_AUTHN_DEFAULT,
7378 RPC_C_AUTHZ_DEFAULT,
7379 COLE_DEFAULT_PRINCIPAL,
7380 RPC_C_AUTHN_LEVEL_DEFAULT,
7381 RPC_C_IMP_LEVEL_IMPERSONATE,
7382 NULL,
7383 EOAC_DEFAULT);
7384 if (FAILED(hrc))
7385 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7386
7387 size_t const cEnvVars = aEnvironmentChanges.size();
7388 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7389 for (size_t i = 0; i < cEnvVars; i++)
7390 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7391
7392 ULONG uPid = 0;
7393 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7394 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7395 idCallerSession, &uPid);
7396 if (FAILED(hrc))
7397 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7398 pid = (RTPROCESS)uPid;
7399 }
7400 else
7401#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7402 {
7403 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7404 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7405 if (RT_FAILURE(vrc))
7406 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7407 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7408 }
7409
7410 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7411 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7412
7413 if (!fSeparate)
7414 {
7415 /*
7416 * Note that we don't release the lock here before calling the client,
7417 * because it doesn't need to call us back if called with a NULL argument.
7418 * Releasing the lock here is dangerous because we didn't prepare the
7419 * launch data yet, but the client we've just started may happen to be
7420 * too fast and call LockMachine() that will fail (because of PID, etc.),
7421 * so that the Machine will never get out of the Spawning session state.
7422 */
7423
7424 /* inform the session that it will be a remote one */
7425 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7426#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7427 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7428#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7429 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7430#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7431 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
7432
7433 if (FAILED(hrc))
7434 {
7435 /* restore the session state */
7436 mData->mSession.mState = SessionState_Unlocked;
7437 alock.release();
7438 mParent->i_addProcessToReap(pid);
7439 /* The failure may occur w/o any error info (from RPC), so provide one */
7440 return setError(VBOX_E_VM_ERROR,
7441 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
7442 }
7443
7444 /* attach launch data to the machine */
7445 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7446 mData->mSession.mRemoteControls.push_back(aControl);
7447 mData->mSession.mProgress = aProgress;
7448 mData->mSession.mPID = pid;
7449 mData->mSession.mState = SessionState_Spawning;
7450 Assert(strCanonicalName.isNotEmpty());
7451 mData->mSession.mName = strCanonicalName;
7452 }
7453 else
7454 {
7455 /* For separate UI process we declare the launch as completed instantly, as the
7456 * actual headless VM start may or may not come. No point in remembering anything
7457 * yet, as what matters for us is when the headless VM gets started. */
7458 aProgress->i_notifyComplete(S_OK);
7459 }
7460
7461 alock.release();
7462 mParent->i_addProcessToReap(pid);
7463
7464 LogFlowThisFuncLeave();
7465 return S_OK;
7466}
7467
7468/**
7469 * Returns @c true if the given session machine instance has an open direct
7470 * session (and optionally also for direct sessions which are closing) and
7471 * returns the session control machine instance if so.
7472 *
7473 * Note that when the method returns @c false, the arguments remain unchanged.
7474 *
7475 * @param aMachine Session machine object.
7476 * @param aControl Direct session control object (optional).
7477 * @param aRequireVM If true then only allow VM sessions.
7478 * @param aAllowClosing If true then additionally a session which is currently
7479 * being closed will also be allowed.
7480 *
7481 * @note locks this object for reading.
7482 */
7483bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7484 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7485 bool aRequireVM /*= false*/,
7486 bool aAllowClosing /*= false*/)
7487{
7488 AutoLimitedCaller autoCaller(this);
7489 AssertComRCReturn(autoCaller.hrc(), false);
7490
7491 /* just return false for inaccessible machines */
7492 if (getObjectState().getState() != ObjectState::Ready)
7493 return false;
7494
7495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7496
7497 if ( ( mData->mSession.mState == SessionState_Locked
7498 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7499 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7500 )
7501 {
7502 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7503
7504 aMachine = mData->mSession.mMachine;
7505
7506 if (aControl != NULL)
7507 *aControl = mData->mSession.mDirectControl;
7508
7509 return true;
7510 }
7511
7512 return false;
7513}
7514
7515/**
7516 * Returns @c true if the given machine has an spawning direct session.
7517 *
7518 * @note locks this object for reading.
7519 */
7520bool Machine::i_isSessionSpawning()
7521{
7522 AutoLimitedCaller autoCaller(this);
7523 AssertComRCReturn(autoCaller.hrc(), false);
7524
7525 /* just return false for inaccessible machines */
7526 if (getObjectState().getState() != ObjectState::Ready)
7527 return false;
7528
7529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7530
7531 if (mData->mSession.mState == SessionState_Spawning)
7532 return true;
7533
7534 return false;
7535}
7536
7537/**
7538 * Called from the client watcher thread to check for unexpected client process
7539 * death during Session_Spawning state (e.g. before it successfully opened a
7540 * direct session).
7541 *
7542 * On Win32 and on OS/2, this method is called only when we've got the
7543 * direct client's process termination notification, so it always returns @c
7544 * true.
7545 *
7546 * On other platforms, this method returns @c true if the client process is
7547 * terminated and @c false if it's still alive.
7548 *
7549 * @note Locks this object for writing.
7550 */
7551bool Machine::i_checkForSpawnFailure()
7552{
7553 AutoCaller autoCaller(this);
7554 if (!autoCaller.isOk())
7555 {
7556 /* nothing to do */
7557 LogFlowThisFunc(("Already uninitialized!\n"));
7558 return true;
7559 }
7560
7561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7562
7563 if (mData->mSession.mState != SessionState_Spawning)
7564 {
7565 /* nothing to do */
7566 LogFlowThisFunc(("Not spawning any more!\n"));
7567 return true;
7568 }
7569
7570 /* PID not yet initialized, skip check. */
7571 if (mData->mSession.mPID == NIL_RTPROCESS)
7572 return false;
7573
7574 HRESULT hrc = S_OK;
7575 RTPROCSTATUS status;
7576 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7577 if (vrc != VERR_PROCESS_RUNNING)
7578 {
7579 Utf8Str strExtraInfo;
7580
7581#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7582 /* If the startup logfile exists and is of non-zero length, tell the
7583 user to look there for more details to encourage them to attach it
7584 when reporting startup issues. */
7585 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7586 uint64_t cbStartupLogFile = 0;
7587 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7588 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7589 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7590#endif
7591
7592 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7593 hrc = setError(E_FAIL,
7594 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7595 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7596 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7597 hrc = setError(E_FAIL,
7598 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7599 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7600 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7601 hrc = setError(E_FAIL,
7602 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7603 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7604 else
7605 hrc = setErrorBoth(E_FAIL, vrc,
7606 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7607 i_getName().c_str(), vrc, strExtraInfo.c_str());
7608 }
7609
7610 if (FAILED(hrc))
7611 {
7612 /* Close the remote session, remove the remote control from the list
7613 * and reset session state to Closed (@note keep the code in sync with
7614 * the relevant part in LockMachine()). */
7615
7616 Assert(mData->mSession.mRemoteControls.size() == 1);
7617 if (mData->mSession.mRemoteControls.size() == 1)
7618 {
7619 ErrorInfoKeeper eik;
7620 mData->mSession.mRemoteControls.front()->Uninitialize();
7621 }
7622
7623 mData->mSession.mRemoteControls.clear();
7624 mData->mSession.mState = SessionState_Unlocked;
7625
7626 /* finalize the progress after setting the state */
7627 if (!mData->mSession.mProgress.isNull())
7628 {
7629 mData->mSession.mProgress->notifyComplete(hrc);
7630 mData->mSession.mProgress.setNull();
7631 }
7632
7633 mData->mSession.mPID = NIL_RTPROCESS;
7634
7635 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7636 return true;
7637 }
7638
7639 return false;
7640}
7641
7642/**
7643 * Checks whether the machine can be registered. If so, commits and saves
7644 * all settings.
7645 *
7646 * @note Must be called from mParent's write lock. Locks this object and
7647 * children for writing.
7648 */
7649HRESULT Machine::i_prepareRegister()
7650{
7651 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7652
7653 AutoLimitedCaller autoCaller(this);
7654 AssertComRCReturnRC(autoCaller.hrc());
7655
7656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7657
7658 /* wait for state dependents to drop to zero */
7659 i_ensureNoStateDependencies(alock);
7660
7661 if (!mData->mAccessible)
7662 return setError(VBOX_E_INVALID_OBJECT_STATE,
7663 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7664 mUserData->s.strName.c_str(),
7665 mData->mUuid.toString().c_str());
7666
7667 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7668
7669 if (mData->mRegistered)
7670 return setError(VBOX_E_INVALID_OBJECT_STATE,
7671 tr("The machine '%s' with UUID {%s} is already registered"),
7672 mUserData->s.strName.c_str(),
7673 mData->mUuid.toString().c_str());
7674
7675 HRESULT hrc = S_OK;
7676
7677 // Ensure the settings are saved. If we are going to be registered and
7678 // no config file exists yet, create it by calling i_saveSettings() too.
7679 if ( (mData->flModifications)
7680 || (!mData->pMachineConfigFile->fileExists())
7681 )
7682 {
7683 hrc = i_saveSettings(NULL, alock);
7684 // no need to check whether VirtualBox.xml needs saving too since
7685 // we can't have a machine XML file rename pending
7686 if (FAILED(hrc)) return hrc;
7687 }
7688
7689 /* more config checking goes here */
7690
7691 if (SUCCEEDED(hrc))
7692 {
7693 /* we may have had implicit modifications we want to fix on success */
7694 i_commit();
7695
7696 mData->mRegistered = true;
7697 }
7698 else
7699 {
7700 /* we may have had implicit modifications we want to cancel on failure*/
7701 i_rollback(false /* aNotify */);
7702 }
7703
7704 return hrc;
7705}
7706
7707/**
7708 * Increases the number of objects dependent on the machine state or on the
7709 * registered state. Guarantees that these two states will not change at least
7710 * until #i_releaseStateDependency() is called.
7711 *
7712 * Depending on the @a aDepType value, additional state checks may be made.
7713 * These checks will set extended error info on failure. See
7714 * #i_checkStateDependency() for more info.
7715 *
7716 * If this method returns a failure, the dependency is not added and the caller
7717 * is not allowed to rely on any particular machine state or registration state
7718 * value and may return the failed result code to the upper level.
7719 *
7720 * @param aDepType Dependency type to add.
7721 * @param aState Current machine state (NULL if not interested).
7722 * @param aRegistered Current registered state (NULL if not interested).
7723 *
7724 * @note Locks this object for writing.
7725 */
7726HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7727 MachineState_T *aState /* = NULL */,
7728 BOOL *aRegistered /* = NULL */)
7729{
7730 AutoCaller autoCaller(this);
7731 AssertComRCReturnRC(autoCaller.hrc());
7732
7733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7734
7735 HRESULT hrc = i_checkStateDependency(aDepType);
7736 if (FAILED(hrc)) return hrc;
7737
7738 {
7739 if (mData->mMachineStateChangePending != 0)
7740 {
7741 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7742 * drop to zero so don't add more. It may make sense to wait a bit
7743 * and retry before reporting an error (since the pending state
7744 * transition should be really quick) but let's just assert for
7745 * now to see if it ever happens on practice. */
7746
7747 AssertFailed();
7748
7749 return setError(E_ACCESSDENIED,
7750 tr("Machine state change is in progress. Please retry the operation later."));
7751 }
7752
7753 ++mData->mMachineStateDeps;
7754 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7755 }
7756
7757 if (aState)
7758 *aState = mData->mMachineState;
7759 if (aRegistered)
7760 *aRegistered = mData->mRegistered;
7761
7762 return S_OK;
7763}
7764
7765/**
7766 * Decreases the number of objects dependent on the machine state.
7767 * Must always complete the #i_addStateDependency() call after the state
7768 * dependency is no more necessary.
7769 */
7770void Machine::i_releaseStateDependency()
7771{
7772 AutoCaller autoCaller(this);
7773 AssertComRCReturnVoid(autoCaller.hrc());
7774
7775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7776
7777 /* releaseStateDependency() w/o addStateDependency()? */
7778 AssertReturnVoid(mData->mMachineStateDeps != 0);
7779 -- mData->mMachineStateDeps;
7780
7781 if (mData->mMachineStateDeps == 0)
7782 {
7783 /* inform i_ensureNoStateDependencies() that there are no more deps */
7784 if (mData->mMachineStateChangePending != 0)
7785 {
7786 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7787 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7788 }
7789 }
7790}
7791
7792Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7793{
7794 /* start with nothing found */
7795 Utf8Str strResult("");
7796
7797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7798
7799 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7800 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7801 // found:
7802 strResult = it->second; // source is a Utf8Str
7803
7804 return strResult;
7805}
7806
7807FirmwareType_T Machine::i_getFirmwareType() const
7808{
7809 return mFirmwareSettings->i_getFirmwareType();
7810}
7811
7812// protected methods
7813/////////////////////////////////////////////////////////////////////////////
7814
7815/**
7816 * Performs machine state checks based on the @a aDepType value. If a check
7817 * fails, this method will set extended error info, otherwise it will return
7818 * S_OK. It is supposed, that on failure, the caller will immediately return
7819 * the return value of this method to the upper level.
7820 *
7821 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7822 *
7823 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7824 * current state of this machine object allows to change settings of the
7825 * machine (i.e. the machine is not registered, or registered but not running
7826 * and not saved). It is useful to call this method from Machine setters
7827 * before performing any change.
7828 *
7829 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7830 * as for MutableStateDep except that if the machine is saved, S_OK is also
7831 * returned. This is useful in setters which allow changing machine
7832 * properties when it is in the saved state.
7833 *
7834 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7835 * if the current state of this machine object allows to change runtime
7836 * changeable settings of the machine (i.e. the machine is not registered, or
7837 * registered but either running or not running and not saved). It is useful
7838 * to call this method from Machine setters before performing any changes to
7839 * runtime changeable settings.
7840 *
7841 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7842 * the same as for MutableOrRunningStateDep except that if the machine is
7843 * saved, S_OK is also returned. This is useful in setters which allow
7844 * changing runtime and saved state changeable machine properties.
7845 *
7846 * @param aDepType Dependency type to check.
7847 *
7848 * @note Non Machine based classes should use #i_addStateDependency() and
7849 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7850 * template.
7851 *
7852 * @note This method must be called from under this object's read or write
7853 * lock.
7854 */
7855HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7856{
7857 switch (aDepType)
7858 {
7859 case AnyStateDep:
7860 {
7861 break;
7862 }
7863 case MutableStateDep:
7864 {
7865 if ( mData->mRegistered
7866 && ( !i_isSessionMachine()
7867 || ( mData->mMachineState != MachineState_Aborted
7868 && mData->mMachineState != MachineState_Teleported
7869 && mData->mMachineState != MachineState_PoweredOff
7870 )
7871 )
7872 )
7873 return setError(VBOX_E_INVALID_VM_STATE,
7874 tr("The machine is not mutable (state is %s)"),
7875 Global::stringifyMachineState(mData->mMachineState));
7876 break;
7877 }
7878 case MutableOrSavedStateDep:
7879 {
7880 if ( mData->mRegistered
7881 && ( !i_isSessionMachine()
7882 || ( mData->mMachineState != MachineState_Aborted
7883 && mData->mMachineState != MachineState_Teleported
7884 && mData->mMachineState != MachineState_Saved
7885 && mData->mMachineState != MachineState_AbortedSaved
7886 && mData->mMachineState != MachineState_PoweredOff
7887 )
7888 )
7889 )
7890 return setError(VBOX_E_INVALID_VM_STATE,
7891 tr("The machine is not mutable or saved (state is %s)"),
7892 Global::stringifyMachineState(mData->mMachineState));
7893 break;
7894 }
7895 case MutableOrRunningStateDep:
7896 {
7897 if ( mData->mRegistered
7898 && ( !i_isSessionMachine()
7899 || ( mData->mMachineState != MachineState_Aborted
7900 && mData->mMachineState != MachineState_Teleported
7901 && mData->mMachineState != MachineState_PoweredOff
7902 && !Global::IsOnline(mData->mMachineState)
7903 )
7904 )
7905 )
7906 return setError(VBOX_E_INVALID_VM_STATE,
7907 tr("The machine is not mutable or running (state is %s)"),
7908 Global::stringifyMachineState(mData->mMachineState));
7909 break;
7910 }
7911 case MutableOrSavedOrRunningStateDep:
7912 {
7913 if ( mData->mRegistered
7914 && ( !i_isSessionMachine()
7915 || ( mData->mMachineState != MachineState_Aborted
7916 && mData->mMachineState != MachineState_Teleported
7917 && mData->mMachineState != MachineState_Saved
7918 && mData->mMachineState != MachineState_AbortedSaved
7919 && mData->mMachineState != MachineState_PoweredOff
7920 && !Global::IsOnline(mData->mMachineState)
7921 )
7922 )
7923 )
7924 return setError(VBOX_E_INVALID_VM_STATE,
7925 tr("The machine is not mutable, saved or running (state is %s)"),
7926 Global::stringifyMachineState(mData->mMachineState));
7927 break;
7928 }
7929 }
7930
7931 return S_OK;
7932}
7933
7934/**
7935 * Helper to initialize all associated child objects and allocate data
7936 * structures.
7937 *
7938 * This method must be called as a part of the object's initialization procedure
7939 * (usually done in the #init() method).
7940 *
7941 * @note Must be called only from #init() or from #i_registeredInit().
7942 */
7943HRESULT Machine::initDataAndChildObjects()
7944{
7945 AutoCaller autoCaller(this);
7946 AssertComRCReturnRC(autoCaller.hrc());
7947 AssertReturn( getObjectState().getState() == ObjectState::InInit
7948 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7949
7950 AssertReturn(!mData->mAccessible, E_FAIL);
7951
7952 /* allocate data structures */
7953 mSSData.allocate();
7954 mUserData.allocate();
7955 mHWData.allocate();
7956 mMediumAttachments.allocate();
7957 mStorageControllers.allocate();
7958 mUSBControllers.allocate();
7959
7960 /* create the platform + platform properties objects for this machine */
7961 HRESULT hrc = unconst(mPlatform).createObject();
7962 ComAssertComRCRetRC(hrc);
7963 hrc = mPlatform->init(this);
7964 ComAssertComRCRetRC(hrc);
7965 hrc = unconst(mPlatformProperties).createObject();
7966 ComAssertComRCRetRC(hrc);
7967 hrc = mPlatformProperties->init(mParent);
7968 ComAssertComRCRetRC(hrc);
7969
7970 /* initialize mOSTypeId */
7971 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7972
7973 /* create associated firmware settings object */
7974 unconst(mFirmwareSettings).createObject();
7975 mFirmwareSettings->init(this);
7976
7977 /* create associated recording settings object */
7978 unconst(mRecordingSettings).createObject();
7979 mRecordingSettings->init(this);
7980
7981 /* create associated trusted platform module object */
7982 unconst(mTrustedPlatformModule).createObject();
7983 mTrustedPlatformModule->init(this);
7984
7985 /* create associated NVRAM store object */
7986 unconst(mNvramStore).createObject();
7987 mNvramStore->init(this);
7988
7989 /* create the graphics adapter object (always present) */
7990 unconst(mGraphicsAdapter).createObject();
7991 mGraphicsAdapter->init(this);
7992
7993 /* create an associated VRDE object (default is disabled) */
7994 unconst(mVRDEServer).createObject();
7995 mVRDEServer->init(this);
7996
7997 /* create associated serial port objects */
7998 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
7999 {
8000 unconst(mSerialPorts[slot]).createObject();
8001 mSerialPorts[slot]->init(this, slot);
8002 }
8003
8004 /* create associated parallel port objects */
8005 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8006 {
8007 unconst(mParallelPorts[slot]).createObject();
8008 mParallelPorts[slot]->init(this, slot);
8009 }
8010
8011 /* create the audio settings object */
8012 unconst(mAudioSettings).createObject();
8013 mAudioSettings->init(this);
8014
8015 /* create the USB device filters object (always present) */
8016 unconst(mUSBDeviceFilters).createObject();
8017 mUSBDeviceFilters->init(this);
8018
8019 /* create associated network adapter objects */
8020 ChipsetType_T enmChipsetType;
8021 hrc = mPlatform->getChipsetType(&enmChipsetType);
8022 ComAssertComRC(hrc);
8023
8024 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType));
8025 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8026 {
8027 unconst(mNetworkAdapters[slot]).createObject();
8028 mNetworkAdapters[slot]->init(this, slot);
8029 }
8030
8031 /* create the bandwidth control */
8032 unconst(mBandwidthControl).createObject();
8033 mBandwidthControl->init(this);
8034
8035 /* create the guest debug control object */
8036 unconst(mGuestDebugControl).createObject();
8037 mGuestDebugControl->init(this);
8038
8039 return hrc;
8040}
8041
8042/**
8043 * Helper to uninitialize all associated child objects and to free all data
8044 * structures.
8045 *
8046 * This method must be called as a part of the object's uninitialization
8047 * procedure (usually done in the #uninit() method).
8048 *
8049 * @note Must be called only from #uninit() or from #i_registeredInit().
8050 */
8051void Machine::uninitDataAndChildObjects()
8052{
8053 AutoCaller autoCaller(this);
8054 AssertComRCReturnVoid(autoCaller.hrc());
8055 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8056 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8057 || getObjectState().getState() == ObjectState::InUninit
8058 || getObjectState().getState() == ObjectState::Limited);
8059
8060 /* tell all our other child objects we've been uninitialized */
8061 if (mGuestDebugControl)
8062 {
8063 mGuestDebugControl->uninit();
8064 unconst(mGuestDebugControl).setNull();
8065 }
8066
8067 if (mBandwidthControl)
8068 {
8069 mBandwidthControl->uninit();
8070 unconst(mBandwidthControl).setNull();
8071 }
8072
8073 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8074 {
8075 if (mNetworkAdapters[slot])
8076 {
8077 mNetworkAdapters[slot]->uninit();
8078 unconst(mNetworkAdapters[slot]).setNull();
8079 }
8080 }
8081
8082 if (mUSBDeviceFilters)
8083 {
8084 mUSBDeviceFilters->uninit();
8085 unconst(mUSBDeviceFilters).setNull();
8086 }
8087
8088 if (mAudioSettings)
8089 {
8090 mAudioSettings->uninit();
8091 unconst(mAudioSettings).setNull();
8092 }
8093
8094 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8095 {
8096 if (mParallelPorts[slot])
8097 {
8098 mParallelPorts[slot]->uninit();
8099 unconst(mParallelPorts[slot]).setNull();
8100 }
8101 }
8102
8103 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8104 {
8105 if (mSerialPorts[slot])
8106 {
8107 mSerialPorts[slot]->uninit();
8108 unconst(mSerialPorts[slot]).setNull();
8109 }
8110 }
8111
8112 if (mVRDEServer)
8113 {
8114 mVRDEServer->uninit();
8115 unconst(mVRDEServer).setNull();
8116 }
8117
8118 if (mGraphicsAdapter)
8119 {
8120 mGraphicsAdapter->uninit();
8121 unconst(mGraphicsAdapter).setNull();
8122 }
8123
8124 if (mPlatform)
8125 {
8126 mPlatform->uninit();
8127 unconst(mPlatform).setNull();
8128 }
8129
8130 if (mPlatformProperties)
8131 {
8132 mPlatformProperties->uninit();
8133 unconst(mPlatformProperties).setNull();
8134 }
8135
8136 if (mFirmwareSettings)
8137 {
8138 mFirmwareSettings->uninit();
8139 unconst(mFirmwareSettings).setNull();
8140 }
8141
8142 if (mRecordingSettings)
8143 {
8144 mRecordingSettings->uninit();
8145 unconst(mRecordingSettings).setNull();
8146 }
8147
8148 if (mTrustedPlatformModule)
8149 {
8150 mTrustedPlatformModule->uninit();
8151 unconst(mTrustedPlatformModule).setNull();
8152 }
8153
8154 if (mNvramStore)
8155 {
8156 mNvramStore->uninit();
8157 unconst(mNvramStore).setNull();
8158 }
8159
8160 /* Deassociate media (only when a real Machine or a SnapshotMachine
8161 * instance is uninitialized; SessionMachine instances refer to real
8162 * Machine media). This is necessary for a clean re-initialization of
8163 * the VM after successfully re-checking the accessibility state. Note
8164 * that in case of normal Machine or SnapshotMachine uninitialization (as
8165 * a result of unregistering or deleting the snapshot), outdated media
8166 * attachments will already be uninitialized and deleted, so this
8167 * code will not affect them. */
8168 if ( !mMediumAttachments.isNull()
8169 && !i_isSessionMachine()
8170 )
8171 {
8172 for (MediumAttachmentList::const_iterator
8173 it = mMediumAttachments->begin();
8174 it != mMediumAttachments->end();
8175 ++it)
8176 {
8177 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8178 if (pMedium.isNull())
8179 continue;
8180 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8181 AssertComRC(hrc);
8182 }
8183 }
8184
8185 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8186 {
8187 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8188 if (mData->mFirstSnapshot)
8189 {
8190 // Snapshots tree is protected by machine write lock.
8191 // Otherwise we assert in Snapshot::uninit()
8192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8193 mData->mFirstSnapshot->uninit();
8194 mData->mFirstSnapshot.setNull();
8195 }
8196
8197 mData->mCurrentSnapshot.setNull();
8198 }
8199
8200 /* free data structures (the essential mData structure is not freed here
8201 * since it may be still in use) */
8202 mMediumAttachments.free();
8203 mStorageControllers.free();
8204 mUSBControllers.free();
8205 mHWData.free();
8206 mUserData.free();
8207 mSSData.free();
8208}
8209
8210/**
8211 * Returns a pointer to the Machine object for this machine that acts like a
8212 * parent for complex machine data objects such as shared folders, etc.
8213 *
8214 * For primary Machine objects and for SnapshotMachine objects, returns this
8215 * object's pointer itself. For SessionMachine objects, returns the peer
8216 * (primary) machine pointer.
8217 */
8218Machine *Machine::i_getMachine()
8219{
8220 if (i_isSessionMachine())
8221 return (Machine*)mPeer;
8222 return this;
8223}
8224
8225/**
8226 * Makes sure that there are no machine state dependents. If necessary, waits
8227 * for the number of dependents to drop to zero.
8228 *
8229 * Make sure this method is called from under this object's write lock to
8230 * guarantee that no new dependents may be added when this method returns
8231 * control to the caller.
8232 *
8233 * @note Receives a lock to this object for writing. The lock will be released
8234 * while waiting (if necessary).
8235 *
8236 * @warning To be used only in methods that change the machine state!
8237 */
8238void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8239{
8240 AssertReturnVoid(isWriteLockOnCurrentThread());
8241
8242 /* Wait for all state dependents if necessary */
8243 if (mData->mMachineStateDeps != 0)
8244 {
8245 /* lazy semaphore creation */
8246 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8247 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8248
8249 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8250 mData->mMachineStateDeps));
8251
8252 ++mData->mMachineStateChangePending;
8253
8254 /* reset the semaphore before waiting, the last dependent will signal
8255 * it */
8256 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8257
8258 alock.release();
8259
8260 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8261
8262 alock.acquire();
8263
8264 -- mData->mMachineStateChangePending;
8265 }
8266}
8267
8268/**
8269 * Changes the machine state and informs callbacks.
8270 *
8271 * This method is not intended to fail so it either returns S_OK or asserts (and
8272 * returns a failure).
8273 *
8274 * @note Locks this object for writing.
8275 */
8276HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8277{
8278 LogFlowThisFuncEnter();
8279 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8280 Assert(aMachineState != MachineState_Null);
8281
8282 AutoCaller autoCaller(this);
8283 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8284
8285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8286
8287 /* wait for state dependents to drop to zero */
8288 i_ensureNoStateDependencies(alock);
8289
8290 MachineState_T const enmOldState = mData->mMachineState;
8291 if (enmOldState != aMachineState)
8292 {
8293 mData->mMachineState = aMachineState;
8294 RTTimeNow(&mData->mLastStateChange);
8295
8296#ifdef VBOX_WITH_DTRACE_R3_MAIN
8297 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8298#endif
8299 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8300 }
8301
8302 LogFlowThisFuncLeave();
8303 return S_OK;
8304}
8305
8306/**
8307 * Searches for a shared folder with the given logical name
8308 * in the collection of shared folders.
8309 *
8310 * @param aName logical name of the shared folder
8311 * @param aSharedFolder where to return the found object
8312 * @param aSetError whether to set the error info if the folder is
8313 * not found
8314 * @return
8315 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8316 *
8317 * @note
8318 * must be called from under the object's lock!
8319 */
8320HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8321 ComObjPtr<SharedFolder> &aSharedFolder,
8322 bool aSetError /* = false */)
8323{
8324 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8325 for (HWData::SharedFolderList::const_iterator
8326 it = mHWData->mSharedFolders.begin();
8327 it != mHWData->mSharedFolders.end();
8328 ++it)
8329 {
8330 SharedFolder *pSF = *it;
8331 AutoCaller autoCaller(pSF);
8332 if (pSF->i_getName() == aName)
8333 {
8334 aSharedFolder = pSF;
8335 hrc = S_OK;
8336 break;
8337 }
8338 }
8339
8340 if (aSetError && FAILED(hrc))
8341 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8342
8343 return hrc;
8344}
8345
8346/**
8347 * Initializes all machine instance data from the given settings structures
8348 * from XML. The exception is the machine UUID which needs special handling
8349 * depending on the caller's use case, so the caller needs to set that herself.
8350 *
8351 * This gets called in several contexts during machine initialization:
8352 *
8353 * -- When machine XML exists on disk already and needs to be loaded into memory,
8354 * for example, from #i_registeredInit() to load all registered machines on
8355 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8356 * attached to the machine should be part of some media registry already.
8357 *
8358 * -- During OVF import, when a machine config has been constructed from an
8359 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8360 * ensure that the media listed as attachments in the config (which have
8361 * been imported from the OVF) receive the correct registry ID.
8362 *
8363 * -- During VM cloning.
8364 *
8365 * @param config Machine settings from XML.
8366 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8367 * for each attached medium in the config.
8368 * @return
8369 */
8370HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8371 const Guid *puuidRegistry)
8372{
8373 // copy name, description, OS type, teleporter, UTC etc.
8374 mUserData->s = config.machineUserData;
8375
8376 // look up the object by Id to check it is valid
8377 ComObjPtr<GuestOSType> pGuestOSType;
8378 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8379 if (!pGuestOSType.isNull())
8380 mUserData->s.strOsType = pGuestOSType->i_id();
8381
8382#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8383 // stateFile encryption (optional)
8384 mSSData->strStateKeyId = config.strStateKeyId;
8385 mSSData->strStateKeyStore = config.strStateKeyStore;
8386 mData->mstrLogKeyId = config.strLogKeyId;
8387 mData->mstrLogKeyStore = config.strLogKeyStore;
8388#endif
8389
8390 // stateFile (optional)
8391 if (config.strStateFile.isEmpty())
8392 mSSData->strStateFilePath.setNull();
8393 else
8394 {
8395 Utf8Str stateFilePathFull(config.strStateFile);
8396 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8397 if (RT_FAILURE(vrc))
8398 return setErrorBoth(E_FAIL, vrc,
8399 tr("Invalid saved state file path '%s' (%Rrc)"),
8400 config.strStateFile.c_str(),
8401 vrc);
8402 mSSData->strStateFilePath = stateFilePathFull;
8403 }
8404
8405 // snapshot folder needs special processing so set it again
8406 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8407 if (FAILED(hrc)) return hrc;
8408
8409 /* Copy the extra data items (config may or may not be the same as
8410 * mData->pMachineConfigFile) if necessary. When loading the XML files
8411 * from disk they are the same, but not for OVF import. */
8412 if (mData->pMachineConfigFile != &config)
8413 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8414
8415 /* currentStateModified (optional, default is true) */
8416 mData->mCurrentStateModified = config.fCurrentStateModified;
8417
8418 mData->mLastStateChange = config.timeLastStateChange;
8419
8420 /*
8421 * note: all mUserData members must be assigned prior this point because
8422 * we need to commit changes in order to let mUserData be shared by all
8423 * snapshot machine instances.
8424 */
8425 mUserData.commitCopy();
8426
8427 // machine registry, if present (must be loaded before snapshots)
8428 if (config.canHaveOwnMediaRegistry())
8429 {
8430 // determine machine folder
8431 Utf8Str strMachineFolder = i_getSettingsFileFull();
8432 strMachineFolder.stripFilename();
8433 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8434 config.mediaRegistry,
8435 strMachineFolder);
8436 if (FAILED(hrc)) return hrc;
8437 }
8438
8439 /* Snapshot node (optional) */
8440 size_t cRootSnapshots;
8441 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8442 {
8443 // there must be only one root snapshot
8444 Assert(cRootSnapshots == 1);
8445 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8446
8447 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
8448 if (FAILED(hrc)) return hrc;
8449 }
8450
8451 // hardware data
8452 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
8453 config.recordingSettings);
8454 if (FAILED(hrc)) return hrc;
8455
8456 /*
8457 * NOTE: the assignment below must be the last thing to do,
8458 * otherwise it will be not possible to change the settings
8459 * somewhere in the code above because all setters will be
8460 * blocked by i_checkStateDependency(MutableStateDep).
8461 */
8462
8463 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8464 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8465 {
8466 /* no need to use i_setMachineState() during init() */
8467 mData->mMachineState = MachineState_AbortedSaved;
8468 }
8469 else if (config.fAborted)
8470 {
8471 mSSData->strStateFilePath.setNull();
8472
8473 /* no need to use i_setMachineState() during init() */
8474 mData->mMachineState = MachineState_Aborted;
8475 }
8476 else if (!mSSData->strStateFilePath.isEmpty())
8477 {
8478 /* no need to use i_setMachineState() during init() */
8479 mData->mMachineState = MachineState_Saved;
8480 }
8481
8482 // after loading settings, we are no longer different from the XML on disk
8483 mData->flModifications = 0;
8484
8485 return S_OK;
8486}
8487
8488/**
8489 * Loads all snapshots starting from the given settings.
8490 *
8491 * @param data snapshot settings.
8492 * @param aCurSnapshotId Current snapshot ID from the settings file.
8493 */
8494HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8495 const Guid &aCurSnapshotId)
8496{
8497 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8498 AssertReturn(!i_isSessionMachine(), E_FAIL);
8499
8500 HRESULT hrc = S_OK;
8501
8502 std::list<const settings::Snapshot *> llSettingsTodo;
8503 llSettingsTodo.push_back(&data);
8504 std::list<Snapshot *> llParentsTodo;
8505 llParentsTodo.push_back(NULL);
8506
8507 while (llSettingsTodo.size() > 0)
8508 {
8509 const settings::Snapshot *current = llSettingsTodo.front();
8510 llSettingsTodo.pop_front();
8511 Snapshot *pParent = llParentsTodo.front();
8512 llParentsTodo.pop_front();
8513
8514 Utf8Str strStateFile;
8515 if (!current->strStateFile.isEmpty())
8516 {
8517 /* optional */
8518 strStateFile = current->strStateFile;
8519 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8520 if (RT_FAILURE(vrc))
8521 {
8522 setErrorBoth(E_FAIL, vrc,
8523 tr("Invalid saved state file path '%s' (%Rrc)"),
8524 strStateFile.c_str(), vrc);
8525 }
8526 }
8527
8528 /* create a snapshot machine object */
8529 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8530 pSnapshotMachine.createObject();
8531 hrc = pSnapshotMachine->initFromSettings(this,
8532 current->hardware,
8533 &current->debugging,
8534 &current->autostart,
8535 current->recordingSettings,
8536 current->uuid.ref(),
8537 strStateFile);
8538 if (FAILED(hrc)) break;
8539
8540 /* create a snapshot object */
8541 ComObjPtr<Snapshot> pSnapshot;
8542 pSnapshot.createObject();
8543 /* initialize the snapshot */
8544 hrc = pSnapshot->init(mParent, // VirtualBox object
8545 current->uuid,
8546 current->strName,
8547 current->strDescription,
8548 current->timestamp,
8549 pSnapshotMachine,
8550 pParent);
8551 if (FAILED(hrc)) break;
8552
8553 /* memorize the first snapshot if necessary */
8554 if (!mData->mFirstSnapshot)
8555 {
8556 Assert(pParent == NULL);
8557 mData->mFirstSnapshot = pSnapshot;
8558 }
8559
8560 /* memorize the current snapshot when appropriate */
8561 if ( !mData->mCurrentSnapshot
8562 && pSnapshot->i_getId() == aCurSnapshotId
8563 )
8564 mData->mCurrentSnapshot = pSnapshot;
8565
8566 /* create all children */
8567 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8568 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8569 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8570 {
8571 llSettingsTodo.push_back(&*it);
8572 llParentsTodo.push_back(pSnapshot);
8573 }
8574 }
8575
8576 return hrc;
8577}
8578
8579/**
8580 * Loads settings into mHWData.
8581 *
8582 * @param puuidRegistry Registry ID.
8583 * @param puuidSnapshot Snapshot ID
8584 * @param data Reference to the hardware settings.
8585 * @param pDbg Pointer to the debugging settings.
8586 * @param pAutostart Pointer to the autostart settings
8587 * @param recording Reference to recording settings.
8588 */
8589HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8590 const Guid *puuidSnapshot,
8591 const settings::Hardware &data,
8592 const settings::Debugging *pDbg,
8593 const settings::Autostart *pAutostart,
8594 const settings::RecordingSettings &recording)
8595{
8596 AssertReturn(!i_isSessionMachine(), E_FAIL);
8597
8598 HRESULT hrc = S_OK;
8599
8600 try
8601 {
8602 ComObjPtr<GuestOSType> pGuestOSType;
8603 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8604
8605 /* The hardware version attribute (optional). */
8606 mHWData->mHWVersion = data.strVersion;
8607 mHWData->mHardwareUUID = data.uuid;
8608
8609 mHWData->mCPUCount = data.cCPUs;
8610 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8611 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8612 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8613 mHWData->mCpuProfile = data.strCpuProfile;
8614
8615 // cpu
8616 if (mHWData->mCPUHotPlugEnabled)
8617 {
8618 for (settings::CpuList::const_iterator
8619 it = data.llCpus.begin();
8620 it != data.llCpus.end();
8621 ++it)
8622 {
8623 const settings::Cpu &cpu = *it;
8624
8625 mHWData->mCPUAttached[cpu.ulId] = true;
8626 }
8627 }
8628
8629 mHWData->mMemorySize = data.ulMemorySizeMB;
8630 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8631
8632 // boot order
8633 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8634 {
8635 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8636 if (it == data.mapBootOrder.end())
8637 mHWData->mBootOrder[i] = DeviceType_Null;
8638 else
8639 mHWData->mBootOrder[i] = it->second;
8640 }
8641
8642 mHWData->mPointingHIDType = data.pointingHIDType;
8643 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8644 mHWData->mParavirtProvider = data.paravirtProvider;
8645 mHWData->mParavirtDebug = data.strParavirtDebug;
8646 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8647
8648 /* GraphicsAdapter */
8649 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8650 if (FAILED(hrc)) return hrc;
8651
8652 /* VRDEServer */
8653 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8654 if (FAILED(hrc)) return hrc;
8655
8656 /* Platform */
8657 hrc = mPlatform->i_loadSettings(data.platformSettings);
8658 if (FAILED(hrc)) return hrc;
8659
8660 i_platformPropertiesUpdate();
8661
8662 /* Firmware */
8663 hrc = mFirmwareSettings->i_loadSettings(data.firmwareSettings);
8664 if (FAILED(hrc)) return hrc;
8665
8666 /* Recording */
8667 hrc = mRecordingSettings->i_loadSettings(recording);
8668 if (FAILED(hrc)) return hrc;
8669
8670 /* Trusted Platform Module */
8671 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8672 if (FAILED(hrc)) return hrc;
8673
8674 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
8675 if (FAILED(hrc)) return hrc;
8676
8677 // Bandwidth control (must come before network adapters)
8678 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
8679 if (FAILED(hrc)) return hrc;
8680
8681 /* USB controllers */
8682 for (settings::USBControllerList::const_iterator
8683 it = data.usbSettings.llUSBControllers.begin();
8684 it != data.usbSettings.llUSBControllers.end();
8685 ++it)
8686 {
8687 const settings::USBController &settingsCtrl = *it;
8688 ComObjPtr<USBController> newCtrl;
8689
8690 newCtrl.createObject();
8691 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8692 mUSBControllers->push_back(newCtrl);
8693 }
8694
8695 /* USB device filters */
8696 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8697 if (FAILED(hrc)) return hrc;
8698
8699 // network adapters (establish array size first and apply defaults, to
8700 // ensure reading the same settings as we saved, since the list skips
8701 // adapters having defaults)
8702 size_t const newCount = PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType);
8703 size_t const oldCount = mNetworkAdapters.size();
8704 if (newCount > oldCount)
8705 {
8706 mNetworkAdapters.resize(newCount);
8707 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8708 {
8709 unconst(mNetworkAdapters[slot]).createObject();
8710 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8711 }
8712 }
8713 else if (newCount < oldCount)
8714 mNetworkAdapters.resize(newCount);
8715 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8716 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8717 for (settings::NetworkAdaptersList::const_iterator
8718 it = data.llNetworkAdapters.begin();
8719 it != data.llNetworkAdapters.end();
8720 ++it)
8721 {
8722 const settings::NetworkAdapter &nic = *it;
8723
8724 /* slot uniqueness is guaranteed by XML Schema */
8725 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8726 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8727 if (FAILED(hrc)) return hrc;
8728 }
8729
8730 // serial ports (establish defaults first, to ensure reading the same
8731 // settings as we saved, since the list skips ports having defaults)
8732 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8733 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8734 for (settings::SerialPortsList::const_iterator
8735 it = data.llSerialPorts.begin();
8736 it != data.llSerialPorts.end();
8737 ++it)
8738 {
8739 const settings::SerialPort &s = *it;
8740
8741 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8742 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8743 if (FAILED(hrc)) return hrc;
8744 }
8745
8746 // parallel ports (establish defaults first, to ensure reading the same
8747 // settings as we saved, since the list skips ports having defaults)
8748 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8749 mParallelPorts[i]->i_applyDefaults();
8750 for (settings::ParallelPortsList::const_iterator
8751 it = data.llParallelPorts.begin();
8752 it != data.llParallelPorts.end();
8753 ++it)
8754 {
8755 const settings::ParallelPort &p = *it;
8756
8757 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8758 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8759 if (FAILED(hrc)) return hrc;
8760 }
8761
8762 /* Audio settings */
8763 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
8764 if (FAILED(hrc)) return hrc;
8765
8766 /* storage controllers */
8767 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
8768 if (FAILED(hrc)) return hrc;
8769
8770 /* Shared folders */
8771 for (settings::SharedFoldersList::const_iterator
8772 it = data.llSharedFolders.begin();
8773 it != data.llSharedFolders.end();
8774 ++it)
8775 {
8776 const settings::SharedFolder &sf = *it;
8777
8778 ComObjPtr<SharedFolder> sharedFolder;
8779 /* Check for double entries. Not allowed! */
8780 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8781 if (SUCCEEDED(hrc))
8782 return setError(VBOX_E_OBJECT_IN_USE,
8783 tr("Shared folder named '%s' already exists"),
8784 sf.strName.c_str());
8785
8786 /* Create the new shared folder. Don't break on error. This will be
8787 * reported when the machine starts. */
8788 sharedFolder.createObject();
8789 hrc = sharedFolder->init(i_getMachine(),
8790 sf.strName,
8791 sf.strHostPath,
8792 RT_BOOL(sf.fWritable),
8793 RT_BOOL(sf.fAutoMount),
8794 sf.strAutoMountPoint,
8795 false /* fFailOnError */);
8796 if (FAILED(hrc)) return hrc;
8797 mHWData->mSharedFolders.push_back(sharedFolder);
8798 }
8799
8800 // Clipboard
8801 mHWData->mClipboardMode = data.clipboardMode;
8802 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8803
8804 // drag'n'drop
8805 mHWData->mDnDMode = data.dndMode;
8806
8807 // guest settings
8808 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8809
8810 // IO settings
8811 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8812 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8813
8814 // Host PCI devices
8815 for (settings::HostPCIDeviceAttachmentList::const_iterator
8816 it = data.pciAttachments.begin();
8817 it != data.pciAttachments.end();
8818 ++it)
8819 {
8820 const settings::HostPCIDeviceAttachment &hpda = *it;
8821 ComObjPtr<PCIDeviceAttachment> pda;
8822
8823 pda.createObject();
8824 pda->i_loadSettings(this, hpda);
8825 mHWData->mPCIDeviceAssignments.push_back(pda);
8826 }
8827
8828 /*
8829 * (The following isn't really real hardware, but it lives in HWData
8830 * for reasons of convenience.)
8831 */
8832
8833#ifdef VBOX_WITH_GUEST_PROPS
8834 /* Guest properties (optional) */
8835
8836 /* Only load transient guest properties for configs which have saved
8837 * state, because there shouldn't be any for powered off VMs. The same
8838 * logic applies for snapshots, as offline snapshots shouldn't have
8839 * any such properties. They confuse the code in various places.
8840 * Note: can't rely on the machine state, as it isn't set yet. */
8841 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8842 /* apologies for the hacky unconst() usage, but this needs hacking
8843 * actually inconsistent settings into consistency, otherwise there
8844 * will be some corner cases where the inconsistency survives
8845 * surprisingly long without getting fixed, especially for snapshots
8846 * as there are no config changes. */
8847 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8848 for (settings::GuestPropertiesList::iterator
8849 it = llGuestProperties.begin();
8850 it != llGuestProperties.end();
8851 /*nothing*/)
8852 {
8853 const settings::GuestProperty &prop = *it;
8854 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8855 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8856 if ( fSkipTransientGuestProperties
8857 && ( fFlags & GUEST_PROP_F_TRANSIENT
8858 || fFlags & GUEST_PROP_F_TRANSRESET))
8859 {
8860 it = llGuestProperties.erase(it);
8861 continue;
8862 }
8863 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8864 mHWData->mGuestProperties[prop.strName] = property;
8865 ++it;
8866 }
8867#endif /* VBOX_WITH_GUEST_PROPS defined */
8868
8869 hrc = i_loadDebugging(pDbg);
8870 if (FAILED(hrc))
8871 return hrc;
8872
8873 mHWData->mAutostart = *pAutostart;
8874
8875 /* default frontend */
8876 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8877 }
8878 catch (std::bad_alloc &)
8879 {
8880 return E_OUTOFMEMORY;
8881 }
8882
8883 AssertComRC(hrc);
8884 return hrc;
8885}
8886
8887/**
8888 * Called from i_loadHardware() to load the debugging settings of the
8889 * machine.
8890 *
8891 * @param pDbg Pointer to the settings.
8892 */
8893HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8894{
8895 mHWData->mDebugging = *pDbg;
8896 /* no more processing currently required, this will probably change. */
8897
8898 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
8899 if (FAILED(hrc)) return hrc;
8900
8901 return S_OK;
8902}
8903
8904/**
8905 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8906 *
8907 * @param data storage settings.
8908 * @param puuidRegistry media registry ID to set media to or NULL;
8909 * see Machine::i_loadMachineDataFromSettings()
8910 * @param puuidSnapshot snapshot ID
8911 * @return
8912 */
8913HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8914 const Guid *puuidRegistry,
8915 const Guid *puuidSnapshot)
8916{
8917 AssertReturn(!i_isSessionMachine(), E_FAIL);
8918
8919 HRESULT hrc = S_OK;
8920
8921 for (settings::StorageControllersList::const_iterator
8922 it = data.llStorageControllers.begin();
8923 it != data.llStorageControllers.end();
8924 ++it)
8925 {
8926 const settings::StorageController &ctlData = *it;
8927
8928 ComObjPtr<StorageController> pCtl;
8929 /* Try to find one with the name first. */
8930 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8931 if (SUCCEEDED(hrc))
8932 return setError(VBOX_E_OBJECT_IN_USE,
8933 tr("Storage controller named '%s' already exists"),
8934 ctlData.strName.c_str());
8935
8936 pCtl.createObject();
8937 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
8938 if (FAILED(hrc)) return hrc;
8939
8940 mStorageControllers->push_back(pCtl);
8941
8942 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8943 if (FAILED(hrc)) return hrc;
8944
8945 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8946 if (FAILED(hrc)) return hrc;
8947
8948 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8949 if (FAILED(hrc)) return hrc;
8950
8951 /* Load the attached devices now. */
8952 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
8953 if (FAILED(hrc)) return hrc;
8954 }
8955
8956 return S_OK;
8957}
8958
8959/**
8960 * Called from i_loadStorageControllers for a controller's devices.
8961 *
8962 * @param aStorageController
8963 * @param data
8964 * @param puuidRegistry media registry ID to set media to or NULL; see
8965 * Machine::i_loadMachineDataFromSettings()
8966 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
8967 * @return
8968 */
8969HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8970 const settings::StorageController &data,
8971 const Guid *puuidRegistry,
8972 const Guid *puuidSnapshot)
8973{
8974 HRESULT hrc = S_OK;
8975
8976 /* paranoia: detect duplicate attachments */
8977 for (settings::AttachedDevicesList::const_iterator
8978 it = data.llAttachedDevices.begin();
8979 it != data.llAttachedDevices.end();
8980 ++it)
8981 {
8982 const settings::AttachedDevice &ad = *it;
8983
8984 for (settings::AttachedDevicesList::const_iterator it2 = it;
8985 it2 != data.llAttachedDevices.end();
8986 ++it2)
8987 {
8988 if (it == it2)
8989 continue;
8990
8991 const settings::AttachedDevice &ad2 = *it2;
8992
8993 if ( ad.lPort == ad2.lPort
8994 && ad.lDevice == ad2.lDevice)
8995 {
8996 return setError(E_FAIL,
8997 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8998 aStorageController->i_getName().c_str(),
8999 ad.lPort,
9000 ad.lDevice,
9001 mUserData->s.strName.c_str());
9002 }
9003 }
9004 }
9005
9006 for (settings::AttachedDevicesList::const_iterator
9007 it = data.llAttachedDevices.begin();
9008 it != data.llAttachedDevices.end();
9009 ++it)
9010 {
9011 const settings::AttachedDevice &dev = *it;
9012 ComObjPtr<Medium> medium;
9013
9014 switch (dev.deviceType)
9015 {
9016 case DeviceType_Floppy:
9017 case DeviceType_DVD:
9018 if (dev.strHostDriveSrc.isNotEmpty())
9019 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9020 false /* fRefresh */, medium);
9021 else
9022 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9023 dev.uuid,
9024 false /* fRefresh */,
9025 false /* aSetError */,
9026 medium);
9027 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9028 // This is not an error. The host drive or UUID might have vanished, so just go
9029 // ahead without this removeable medium attachment
9030 hrc = S_OK;
9031 break;
9032
9033 case DeviceType_HardDisk:
9034 {
9035 /* find a hard disk by UUID */
9036 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9037 if (FAILED(hrc))
9038 {
9039 if (i_isSnapshotMachine())
9040 {
9041 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9042 // so the user knows that the bad disk is in a snapshot somewhere
9043 com::ErrorInfo info;
9044 return setError(E_FAIL,
9045 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9046 puuidSnapshot->raw(),
9047 info.getText().raw());
9048 }
9049 return hrc;
9050 }
9051
9052 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9053
9054 if (medium->i_getType() == MediumType_Immutable)
9055 {
9056 if (i_isSnapshotMachine())
9057 return setError(E_FAIL,
9058 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9059 "of the virtual machine '%s' ('%s')"),
9060 medium->i_getLocationFull().c_str(),
9061 dev.uuid.raw(),
9062 puuidSnapshot->raw(),
9063 mUserData->s.strName.c_str(),
9064 mData->m_strConfigFileFull.c_str());
9065
9066 return setError(E_FAIL,
9067 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9068 medium->i_getLocationFull().c_str(),
9069 dev.uuid.raw(),
9070 mUserData->s.strName.c_str(),
9071 mData->m_strConfigFileFull.c_str());
9072 }
9073
9074 if (medium->i_getType() == MediumType_MultiAttach)
9075 {
9076 if (i_isSnapshotMachine())
9077 return setError(E_FAIL,
9078 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9079 "of the virtual machine '%s' ('%s')"),
9080 medium->i_getLocationFull().c_str(),
9081 dev.uuid.raw(),
9082 puuidSnapshot->raw(),
9083 mUserData->s.strName.c_str(),
9084 mData->m_strConfigFileFull.c_str());
9085
9086 return setError(E_FAIL,
9087 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9088 medium->i_getLocationFull().c_str(),
9089 dev.uuid.raw(),
9090 mUserData->s.strName.c_str(),
9091 mData->m_strConfigFileFull.c_str());
9092 }
9093
9094 if ( !i_isSnapshotMachine()
9095 && medium->i_getChildren().size() != 0
9096 )
9097 return setError(E_FAIL,
9098 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9099 "because it has %d differencing child hard disks"),
9100 medium->i_getLocationFull().c_str(),
9101 dev.uuid.raw(),
9102 mUserData->s.strName.c_str(),
9103 mData->m_strConfigFileFull.c_str(),
9104 medium->i_getChildren().size());
9105
9106 if (i_findAttachment(*mMediumAttachments.data(),
9107 medium))
9108 return setError(E_FAIL,
9109 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9110 medium->i_getLocationFull().c_str(),
9111 dev.uuid.raw(),
9112 mUserData->s.strName.c_str(),
9113 mData->m_strConfigFileFull.c_str());
9114
9115 break;
9116 }
9117
9118 default:
9119 return setError(E_FAIL,
9120 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9121 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9122 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9123 }
9124
9125 if (FAILED(hrc))
9126 break;
9127
9128 /* Bandwidth groups are loaded at this point. */
9129 ComObjPtr<BandwidthGroup> pBwGroup;
9130
9131 if (!dev.strBwGroup.isEmpty())
9132 {
9133 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9134 if (FAILED(hrc))
9135 return setError(E_FAIL,
9136 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9137 medium->i_getLocationFull().c_str(),
9138 dev.strBwGroup.c_str(),
9139 mUserData->s.strName.c_str(),
9140 mData->m_strConfigFileFull.c_str());
9141 pBwGroup->i_reference();
9142 }
9143
9144 const Utf8Str controllerName = aStorageController->i_getName();
9145 ComObjPtr<MediumAttachment> pAttachment;
9146 pAttachment.createObject();
9147 hrc = pAttachment->init(this,
9148 medium,
9149 controllerName,
9150 dev.lPort,
9151 dev.lDevice,
9152 dev.deviceType,
9153 false,
9154 dev.fPassThrough,
9155 dev.fTempEject,
9156 dev.fNonRotational,
9157 dev.fDiscard,
9158 dev.fHotPluggable,
9159 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9160 if (FAILED(hrc)) break;
9161
9162 /* associate the medium with this machine and snapshot */
9163 if (!medium.isNull())
9164 {
9165 AutoCaller medCaller(medium);
9166 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9167 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9168
9169 if (i_isSnapshotMachine())
9170 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9171 else
9172 hrc = medium->i_addBackReference(mData->mUuid);
9173 /* If the medium->addBackReference fails it sets an appropriate
9174 * error message, so no need to do any guesswork here. */
9175
9176 if (puuidRegistry)
9177 // caller wants registry ID to be set on all attached media (OVF import case)
9178 medium->i_addRegistry(*puuidRegistry);
9179 }
9180
9181 if (FAILED(hrc))
9182 break;
9183
9184 /* back up mMediumAttachments to let registeredInit() properly rollback
9185 * on failure (= limited accessibility) */
9186 i_setModified(IsModified_Storage);
9187 mMediumAttachments.backup();
9188 mMediumAttachments->push_back(pAttachment);
9189 }
9190
9191 return hrc;
9192}
9193
9194/**
9195 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9196 *
9197 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9198 * @param aSnapshot where to return the found snapshot
9199 * @param aSetError true to set extended error info on failure
9200 */
9201HRESULT Machine::i_findSnapshotById(const Guid &aId,
9202 ComObjPtr<Snapshot> &aSnapshot,
9203 bool aSetError /* = false */)
9204{
9205 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9206
9207 if (!mData->mFirstSnapshot)
9208 {
9209 if (aSetError)
9210 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9211 return E_FAIL;
9212 }
9213
9214 if (aId.isZero())
9215 aSnapshot = mData->mFirstSnapshot;
9216 else
9217 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9218
9219 if (!aSnapshot)
9220 {
9221 if (aSetError)
9222 return setError(E_FAIL,
9223 tr("Could not find a snapshot with UUID {%s}"),
9224 aId.toString().c_str());
9225 return E_FAIL;
9226 }
9227
9228 return S_OK;
9229}
9230
9231/**
9232 * Returns the snapshot with the given name or fails of no such snapshot.
9233 *
9234 * @param strName snapshot name to find
9235 * @param aSnapshot where to return the found snapshot
9236 * @param aSetError true to set extended error info on failure
9237 */
9238HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9239 ComObjPtr<Snapshot> &aSnapshot,
9240 bool aSetError /* = false */)
9241{
9242 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9243
9244 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9245
9246 if (!mData->mFirstSnapshot)
9247 {
9248 if (aSetError)
9249 return setError(VBOX_E_OBJECT_NOT_FOUND,
9250 tr("This machine does not have any snapshots"));
9251 return VBOX_E_OBJECT_NOT_FOUND;
9252 }
9253
9254 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9255
9256 if (!aSnapshot)
9257 {
9258 if (aSetError)
9259 return setError(VBOX_E_OBJECT_NOT_FOUND,
9260 tr("Could not find a snapshot named '%s'"), strName.c_str());
9261 return VBOX_E_OBJECT_NOT_FOUND;
9262 }
9263
9264 return S_OK;
9265}
9266
9267/**
9268 * Returns a storage controller object with the given name.
9269 *
9270 * @param aName storage controller name to find
9271 * @param aStorageController where to return the found storage controller
9272 * @param aSetError true to set extended error info on failure
9273 */
9274HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9275 ComObjPtr<StorageController> &aStorageController,
9276 bool aSetError /* = false */)
9277{
9278 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9279
9280 for (StorageControllerList::const_iterator
9281 it = mStorageControllers->begin();
9282 it != mStorageControllers->end();
9283 ++it)
9284 {
9285 if ((*it)->i_getName() == aName)
9286 {
9287 aStorageController = (*it);
9288 return S_OK;
9289 }
9290 }
9291
9292 if (aSetError)
9293 return setError(VBOX_E_OBJECT_NOT_FOUND,
9294 tr("Could not find a storage controller named '%s'"),
9295 aName.c_str());
9296 return VBOX_E_OBJECT_NOT_FOUND;
9297}
9298
9299/**
9300 * Returns a USB controller object with the given name.
9301 *
9302 * @param aName USB controller name to find
9303 * @param aUSBController where to return the found USB controller
9304 * @param aSetError true to set extended error info on failure
9305 */
9306HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9307 ComObjPtr<USBController> &aUSBController,
9308 bool aSetError /* = false */)
9309{
9310 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9311
9312 for (USBControllerList::const_iterator
9313 it = mUSBControllers->begin();
9314 it != mUSBControllers->end();
9315 ++it)
9316 {
9317 if ((*it)->i_getName() == aName)
9318 {
9319 aUSBController = (*it);
9320 return S_OK;
9321 }
9322 }
9323
9324 if (aSetError)
9325 return setError(VBOX_E_OBJECT_NOT_FOUND,
9326 tr("Could not find a storage controller named '%s'"),
9327 aName.c_str());
9328 return VBOX_E_OBJECT_NOT_FOUND;
9329}
9330
9331/**
9332 * Returns the number of USB controller instance of the given type.
9333 *
9334 * @param enmType USB controller type.
9335 */
9336ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9337{
9338 ULONG cCtrls = 0;
9339
9340 for (USBControllerList::const_iterator
9341 it = mUSBControllers->begin();
9342 it != mUSBControllers->end();
9343 ++it)
9344 {
9345 if ((*it)->i_getControllerType() == enmType)
9346 cCtrls++;
9347 }
9348
9349 return cCtrls;
9350}
9351
9352HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9353 MediumAttachmentList &atts)
9354{
9355 AutoCaller autoCaller(this);
9356 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
9357
9358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9359
9360 for (MediumAttachmentList::const_iterator
9361 it = mMediumAttachments->begin();
9362 it != mMediumAttachments->end();
9363 ++it)
9364 {
9365 const ComObjPtr<MediumAttachment> &pAtt = *it;
9366 // should never happen, but deal with NULL pointers in the list.
9367 AssertContinue(!pAtt.isNull());
9368
9369 // getControllerName() needs caller+read lock
9370 AutoCaller autoAttCaller(pAtt);
9371 if (FAILED(autoAttCaller.hrc()))
9372 {
9373 atts.clear();
9374 return autoAttCaller.hrc();
9375 }
9376 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9377
9378 if (pAtt->i_getControllerName() == aName)
9379 atts.push_back(pAtt);
9380 }
9381
9382 return S_OK;
9383}
9384
9385
9386/**
9387 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9388 * file if the machine name was changed and about creating a new settings file
9389 * if this is a new machine.
9390 *
9391 * @note Must be never called directly but only from #saveSettings().
9392 */
9393HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9394 bool *pfSettingsFileIsNew)
9395{
9396 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9397
9398 HRESULT hrc = S_OK;
9399
9400 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9401 /// @todo need to handle primary group change, too
9402
9403 /* attempt to rename the settings file if machine name is changed */
9404 if ( mUserData->s.fNameSync
9405 && mUserData.isBackedUp()
9406 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9407 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9408 )
9409 {
9410 bool dirRenamed = false;
9411 bool fileRenamed = false;
9412
9413 Utf8Str configFile, newConfigFile;
9414 Utf8Str configFilePrev, newConfigFilePrev;
9415 Utf8Str NVRAMFile, newNVRAMFile;
9416 Utf8Str configDir, newConfigDir;
9417
9418 do
9419 {
9420 int vrc = VINF_SUCCESS;
9421
9422 Utf8Str name = mUserData.backedUpData()->s.strName;
9423 Utf8Str newName = mUserData->s.strName;
9424 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9425 if (group == "/")
9426 group.setNull();
9427 Utf8Str newGroup = mUserData->s.llGroups.front();
9428 if (newGroup == "/")
9429 newGroup.setNull();
9430
9431 configFile = mData->m_strConfigFileFull;
9432
9433 /* first, rename the directory if it matches the group and machine name */
9434 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9435 /** @todo hack, make somehow use of ComposeMachineFilename */
9436 if (mUserData->s.fDirectoryIncludesUUID)
9437 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9438 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9439 /** @todo hack, make somehow use of ComposeMachineFilename */
9440 if (mUserData->s.fDirectoryIncludesUUID)
9441 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9442 configDir = configFile;
9443 configDir.stripFilename();
9444 newConfigDir = configDir;
9445 if ( configDir.length() >= groupPlusName.length()
9446 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9447 groupPlusName.c_str()))
9448 {
9449 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9450 Utf8Str newConfigBaseDir(newConfigDir);
9451 newConfigDir.append(newGroupPlusName);
9452 /* consistency: use \ if appropriate on the platform */
9453 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9454 /* new dir and old dir cannot be equal here because of 'if'
9455 * above and because name != newName */
9456 Assert(configDir != newConfigDir);
9457 if (!fSettingsFileIsNew)
9458 {
9459 /* perform real rename only if the machine is not new */
9460 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9461 if ( vrc == VERR_FILE_NOT_FOUND
9462 || vrc == VERR_PATH_NOT_FOUND)
9463 {
9464 /* create the parent directory, then retry renaming */
9465 Utf8Str parent(newConfigDir);
9466 parent.stripFilename();
9467 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9468 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9469 }
9470 if (RT_FAILURE(vrc))
9471 {
9472 hrc = setErrorBoth(E_FAIL, vrc,
9473 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9474 configDir.c_str(),
9475 newConfigDir.c_str(),
9476 vrc);
9477 break;
9478 }
9479 /* delete subdirectories which are no longer needed */
9480 Utf8Str dir(configDir);
9481 dir.stripFilename();
9482 while (dir != newConfigBaseDir && dir != ".")
9483 {
9484 vrc = RTDirRemove(dir.c_str());
9485 if (RT_FAILURE(vrc))
9486 break;
9487 dir.stripFilename();
9488 }
9489 dirRenamed = true;
9490 }
9491 }
9492
9493 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9494
9495 /* then try to rename the settings file itself */
9496 if (newConfigFile != configFile)
9497 {
9498 /* get the path to old settings file in renamed directory */
9499 Assert(mData->m_strConfigFileFull == configFile);
9500 configFile.printf("%s%c%s",
9501 newConfigDir.c_str(),
9502 RTPATH_DELIMITER,
9503 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9504 if (!fSettingsFileIsNew)
9505 {
9506 /* perform real rename only if the machine is not new */
9507 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9508 if (RT_FAILURE(vrc))
9509 {
9510 hrc = setErrorBoth(E_FAIL, vrc,
9511 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9512 configFile.c_str(),
9513 newConfigFile.c_str(),
9514 vrc);
9515 break;
9516 }
9517 fileRenamed = true;
9518 configFilePrev = configFile;
9519 configFilePrev += "-prev";
9520 newConfigFilePrev = newConfigFile;
9521 newConfigFilePrev += "-prev";
9522 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9523 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9524 if (NVRAMFile.isNotEmpty())
9525 {
9526 // in the NVRAM file path, replace the old directory with the new directory
9527 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9528 {
9529 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9530 NVRAMFile = newConfigDir + strNVRAMFile;
9531 }
9532 newNVRAMFile = newConfigFile;
9533 newNVRAMFile.stripSuffix();
9534 newNVRAMFile += ".nvram";
9535 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9536 }
9537 }
9538 }
9539
9540 // update m_strConfigFileFull amd mConfigFile
9541 mData->m_strConfigFileFull = newConfigFile;
9542 // compute the relative path too
9543 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9544
9545 // store the old and new so that VirtualBox::i_saveSettings() can update
9546 // the media registry
9547 if ( mData->mRegistered
9548 && (configDir != newConfigDir || configFile != newConfigFile))
9549 {
9550 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9551
9552 if (pfNeedsGlobalSaveSettings)
9553 *pfNeedsGlobalSaveSettings = true;
9554 }
9555
9556 // in the saved state file path, replace the old directory with the new directory
9557 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9558 {
9559 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9560 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9561 }
9562 if (newNVRAMFile.isNotEmpty())
9563 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9564
9565 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9566 if (mData->mFirstSnapshot)
9567 {
9568 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9569 newConfigDir.c_str());
9570 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9571 newConfigDir.c_str());
9572 }
9573 }
9574 while (0);
9575
9576 if (FAILED(hrc))
9577 {
9578 /* silently try to rename everything back */
9579 if (fileRenamed)
9580 {
9581 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9582 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9583 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9584 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9585 }
9586 if (dirRenamed)
9587 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9588 }
9589
9590 if (FAILED(hrc)) return hrc;
9591 }
9592
9593 if (fSettingsFileIsNew)
9594 {
9595 /* create a virgin config file */
9596 int vrc = VINF_SUCCESS;
9597
9598 /* ensure the settings directory exists */
9599 Utf8Str path(mData->m_strConfigFileFull);
9600 path.stripFilename();
9601 if (!RTDirExists(path.c_str()))
9602 {
9603 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9604 if (RT_FAILURE(vrc))
9605 {
9606 return setErrorBoth(E_FAIL, vrc,
9607 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9608 path.c_str(),
9609 vrc);
9610 }
9611 }
9612
9613 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9614 path = mData->m_strConfigFileFull;
9615 RTFILE f = NIL_RTFILE;
9616 vrc = RTFileOpen(&f, path.c_str(),
9617 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9618 if (RT_FAILURE(vrc))
9619 return setErrorBoth(E_FAIL, vrc,
9620 tr("Could not create the settings file '%s' (%Rrc)"),
9621 path.c_str(),
9622 vrc);
9623 RTFileClose(f);
9624 }
9625 if (pfSettingsFileIsNew)
9626 *pfSettingsFileIsNew = fSettingsFileIsNew;
9627
9628 return hrc;
9629}
9630
9631/**
9632 * Saves and commits machine data, user data and hardware data.
9633 *
9634 * Note that on failure, the data remains uncommitted.
9635 *
9636 * @a aFlags may combine the following flags:
9637 *
9638 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9639 * Used when saving settings after an operation that makes them 100%
9640 * correspond to the settings from the current snapshot.
9641 * - SaveS_Force: settings will be saved without doing a deep compare of the
9642 * settings structures. This is used when this is called because snapshots
9643 * have changed to avoid the overhead of the deep compare.
9644 *
9645 * @note Must be called from under this object's write lock. Locks children for
9646 * writing.
9647 *
9648 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9649 * initialized to false and that will be set to true by this function if
9650 * the caller must invoke VirtualBox::i_saveSettings() because the global
9651 * settings have changed. This will happen if a machine rename has been
9652 * saved and the global machine and media registries will therefore need
9653 * updating.
9654 * @param alock Reference to the lock for this machine object.
9655 * @param aFlags Flags.
9656 */
9657HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9658 AutoWriteLock &alock,
9659 int aFlags /*= 0*/)
9660{
9661 LogFlowThisFuncEnter();
9662
9663 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9664
9665 /* make sure child objects are unable to modify the settings while we are
9666 * saving them */
9667 i_ensureNoStateDependencies(alock);
9668
9669 AssertReturn(!i_isSnapshotMachine(),
9670 E_FAIL);
9671
9672 if (!mData->mAccessible)
9673 return setError(VBOX_E_INVALID_VM_STATE,
9674 tr("The machine is not accessible, so cannot save settings"));
9675
9676 HRESULT hrc = S_OK;
9677 PCVBOXCRYPTOIF pCryptoIf = NULL;
9678 const char *pszPassword = NULL;
9679 SecretKey *pKey = NULL;
9680
9681#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9682 if (mData->mstrKeyId.isNotEmpty())
9683 {
9684 /* VM is going to be encrypted. */
9685 alock.release(); /** @todo Revise the locking. */
9686 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
9687 alock.acquire();
9688 if (FAILED(hrc)) return hrc; /* Error is set. */
9689
9690 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
9691 if (RT_SUCCESS(vrc))
9692 pszPassword = (const char *)pKey->getKeyBuffer();
9693 else
9694 {
9695 mParent->i_releaseCryptoIf(pCryptoIf);
9696 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
9697 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
9698 mData->mstrKeyId.c_str(), vrc);
9699 }
9700 }
9701#else
9702 RT_NOREF(pKey);
9703#endif
9704
9705 bool fNeedsWrite = false;
9706 bool fSettingsFileIsNew = false;
9707
9708 /* First, prepare to save settings. It will care about renaming the
9709 * settings directory and file if the machine name was changed and about
9710 * creating a new settings file if this is a new machine. */
9711 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
9712 if (FAILED(hrc))
9713 {
9714#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9715 if (pCryptoIf)
9716 {
9717 alock.release(); /** @todo Revise the locking. */
9718 mParent->i_releaseCryptoIf(pCryptoIf);
9719 alock.acquire();
9720 }
9721 if (pKey)
9722 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9723#endif
9724 return hrc;
9725 }
9726
9727 // keep a pointer to the current settings structures
9728 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9729 settings::MachineConfigFile *pNewConfig = NULL;
9730
9731 try
9732 {
9733 // make a fresh one to have everyone write stuff into
9734 pNewConfig = new settings::MachineConfigFile(NULL);
9735 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9736#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9737 pNewConfig->strKeyId = mData->mstrKeyId;
9738 pNewConfig->strKeyStore = mData->mstrKeyStore;
9739#endif
9740
9741 // now go and copy all the settings data from COM to the settings structures
9742 // (this calls i_saveSettings() on all the COM objects in the machine)
9743 i_copyMachineDataToSettings(*pNewConfig);
9744
9745 if (aFlags & SaveS_ResetCurStateModified)
9746 {
9747 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9748 mData->mCurrentStateModified = FALSE;
9749 fNeedsWrite = true; // always, no need to compare
9750 }
9751 else if (aFlags & SaveS_Force)
9752 {
9753 fNeedsWrite = true; // always, no need to compare
9754 }
9755 else
9756 {
9757 if (!mData->mCurrentStateModified)
9758 {
9759 // do a deep compare of the settings that we just saved with the settings
9760 // previously stored in the config file; this invokes MachineConfigFile::operator==
9761 // which does a deep compare of all the settings, which is expensive but less expensive
9762 // than writing out XML in vain
9763 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9764
9765 // could still be modified if any settings changed
9766 mData->mCurrentStateModified = fAnySettingsChanged;
9767
9768 fNeedsWrite = fAnySettingsChanged;
9769 }
9770 else
9771 fNeedsWrite = true;
9772 }
9773
9774 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9775
9776 if (fNeedsWrite)
9777 {
9778 // now spit it all out!
9779 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
9780 if (aFlags & SaveS_RemoveBackup)
9781 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
9782 }
9783
9784 mData->pMachineConfigFile = pNewConfig;
9785 delete pOldConfig;
9786 i_commit();
9787
9788 // after saving settings, we are no longer different from the XML on disk
9789 mData->flModifications = 0;
9790 }
9791 catch (HRESULT err)
9792 {
9793 // we assume that error info is set by the thrower
9794 hrc = err;
9795
9796 // delete any newly created settings file
9797 if (fSettingsFileIsNew)
9798 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
9799
9800 // restore old config
9801 delete pNewConfig;
9802 mData->pMachineConfigFile = pOldConfig;
9803 }
9804 catch (...)
9805 {
9806 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9807 }
9808
9809#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9810 if (pCryptoIf)
9811 {
9812 alock.release(); /** @todo Revise the locking. */
9813 mParent->i_releaseCryptoIf(pCryptoIf);
9814 alock.acquire();
9815 }
9816 if (pKey)
9817 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9818#endif
9819
9820 if (fNeedsWrite)
9821 {
9822 /* Fire the data change event, even on failure (since we've already
9823 * committed all data). This is done only for SessionMachines because
9824 * mutable Machine instances are always not registered (i.e. private
9825 * to the client process that creates them) and thus don't need to
9826 * inform callbacks. */
9827 if (i_isSessionMachine())
9828 mParent->i_onMachineDataChanged(mData->mUuid);
9829 }
9830
9831 LogFlowThisFunc(("hrc=%08X\n", hrc));
9832 LogFlowThisFuncLeave();
9833 return hrc;
9834}
9835
9836/**
9837 * Implementation for saving the machine settings into the given
9838 * settings::MachineConfigFile instance. This copies machine extradata
9839 * from the previous machine config file in the instance data, if any.
9840 *
9841 * This gets called from two locations:
9842 *
9843 * -- Machine::i_saveSettings(), during the regular XML writing;
9844 *
9845 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9846 * exported to OVF and we write the VirtualBox proprietary XML
9847 * into a <vbox:Machine> tag.
9848 *
9849 * This routine fills all the fields in there, including snapshots, *except*
9850 * for the following:
9851 *
9852 * -- fCurrentStateModified. There is some special logic associated with that.
9853 *
9854 * The caller can then call MachineConfigFile::write() or do something else
9855 * with it.
9856 *
9857 * Caller must hold the machine lock!
9858 *
9859 * This throws XML errors and HRESULT, so the caller must have a catch block!
9860 */
9861void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9862{
9863 // deep copy extradata, being extra careful with self assignment (the STL
9864 // map assignment on Mac OS X clang based Xcode isn't checking)
9865 if (&config != mData->pMachineConfigFile)
9866 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9867
9868 config.uuid = mData->mUuid;
9869
9870 // copy name, description, OS type, teleport, UTC etc.
9871 config.machineUserData = mUserData->s;
9872
9873#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9874 config.strStateKeyId = mSSData->strStateKeyId;
9875 config.strStateKeyStore = mSSData->strStateKeyStore;
9876 config.strLogKeyId = mData->mstrLogKeyId;
9877 config.strLogKeyStore = mData->mstrLogKeyStore;
9878#endif
9879
9880 if ( mData->mMachineState == MachineState_Saved
9881 || mData->mMachineState == MachineState_AbortedSaved
9882 || mData->mMachineState == MachineState_Restoring
9883 // when doing certain snapshot operations we may or may not have
9884 // a saved state in the current state, so keep everything as is
9885 || ( ( mData->mMachineState == MachineState_Snapshotting
9886 || mData->mMachineState == MachineState_DeletingSnapshot
9887 || mData->mMachineState == MachineState_RestoringSnapshot)
9888 && (!mSSData->strStateFilePath.isEmpty())
9889 )
9890 )
9891 {
9892 Assert(!mSSData->strStateFilePath.isEmpty());
9893 /* try to make the file name relative to the settings file dir */
9894 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9895 }
9896 else
9897 {
9898 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9899 config.strStateFile.setNull();
9900 }
9901
9902 if (mData->mCurrentSnapshot)
9903 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9904 else
9905 config.uuidCurrentSnapshot.clear();
9906
9907 config.timeLastStateChange = mData->mLastStateChange;
9908 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
9909 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9910
9911 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
9912 if (FAILED(hrc)) throw hrc;
9913
9914 // save machine's media registry if this is VirtualBox 4.0 or later
9915 if (config.canHaveOwnMediaRegistry())
9916 {
9917 // determine machine folder
9918 Utf8Str strMachineFolder = i_getSettingsFileFull();
9919 strMachineFolder.stripFilename();
9920 mParent->i_saveMediaRegistry(config.mediaRegistry,
9921 i_getId(), // only media with registry ID == machine UUID
9922 strMachineFolder);
9923 // this throws HRESULT
9924 }
9925
9926 // save snapshots
9927 hrc = i_saveAllSnapshots(config);
9928 if (FAILED(hrc)) throw hrc;
9929}
9930
9931/**
9932 * Saves all snapshots of the machine into the given machine config file. Called
9933 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9934 * @param config
9935 * @return
9936 */
9937HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9938{
9939 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9940
9941 HRESULT hrc = S_OK;
9942
9943 try
9944 {
9945 config.llFirstSnapshot.clear();
9946
9947 if (mData->mFirstSnapshot)
9948 {
9949 // the settings use a list for "the first snapshot"
9950 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9951
9952 // get reference to the snapshot on the list and work on that
9953 // element straight in the list to avoid excessive copying later
9954 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9955 if (FAILED(hrc)) throw hrc;
9956 }
9957
9958// if (mType == IsSessionMachine)
9959// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9960
9961 }
9962 catch (HRESULT err)
9963 {
9964 /* we assume that error info is set by the thrower */
9965 hrc = err;
9966 }
9967 catch (...)
9968 {
9969 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9970 }
9971
9972 return hrc;
9973}
9974
9975/**
9976 * Saves the VM hardware configuration. It is assumed that the
9977 * given node is empty.
9978 *
9979 * @param data Reference to the settings object for the hardware config.
9980 * @param pDbg Pointer to the settings object for the debugging config
9981 * which happens to live in mHWData.
9982 * @param pAutostart Pointer to the settings object for the autostart config
9983 * which happens to live in mHWData.
9984 * @param recording Reference to reecording settings.
9985 */
9986HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9987 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
9988{
9989 HRESULT hrc = S_OK;
9990
9991 try
9992 {
9993 /* The hardware version attribute (optional).
9994 Automatically upgrade from 1 to current default hardware version
9995 when there is no saved state. (ugly!) */
9996 if ( mHWData->mHWVersion == "1"
9997 && mSSData->strStateFilePath.isEmpty()
9998 )
9999 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10000
10001 data.strVersion = mHWData->mHWVersion;
10002 data.uuid = mHWData->mHardwareUUID;
10003
10004 // CPU
10005 data.cCPUs = mHWData->mCPUCount;
10006 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10007 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10008 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10009 data.strCpuProfile = mHWData->mCpuProfile;
10010
10011 data.llCpus.clear();
10012 if (data.fCpuHotPlug)
10013 {
10014 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10015 {
10016 if (mHWData->mCPUAttached[idx])
10017 {
10018 settings::Cpu cpu;
10019 cpu.ulId = idx;
10020 data.llCpus.push_back(cpu);
10021 }
10022 }
10023 }
10024
10025 // memory
10026 data.ulMemorySizeMB = mHWData->mMemorySize;
10027 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10028
10029 // HID
10030 data.pointingHIDType = mHWData->mPointingHIDType;
10031 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10032
10033 // paravirt
10034 data.paravirtProvider = mHWData->mParavirtProvider;
10035 data.strParavirtDebug = mHWData->mParavirtDebug;
10036
10037 // emulated USB card reader
10038 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10039
10040 // boot order
10041 data.mapBootOrder.clear();
10042 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10043 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10044
10045 /* VRDEServer settings (optional) */
10046 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10047 if (FAILED(hrc)) throw hrc;
10048
10049 /* Platform (required) */
10050 hrc = mPlatform->i_saveSettings(data.platformSettings);
10051 if (FAILED(hrc)) return hrc;
10052
10053 /* Firmware settings (required) */
10054 hrc = mFirmwareSettings->i_saveSettings(data.firmwareSettings);
10055 if (FAILED(hrc)) throw hrc;
10056
10057 /* Recording settings. */
10058 hrc = mRecordingSettings->i_saveSettings(recording);
10059 if (FAILED(hrc)) throw hrc;
10060
10061 /* Trusted Platform Module settings (required) */
10062 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10063 if (FAILED(hrc)) throw hrc;
10064
10065 /* NVRAM settings (required) */
10066 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10067 if (FAILED(hrc)) throw hrc;
10068
10069 /* GraphicsAdapter settings (required) */
10070 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10071 if (FAILED(hrc)) throw hrc;
10072
10073 /* USB Controller (required) */
10074 data.usbSettings.llUSBControllers.clear();
10075 for (USBControllerList::const_iterator
10076 it = mUSBControllers->begin();
10077 it != mUSBControllers->end();
10078 ++it)
10079 {
10080 ComObjPtr<USBController> ctrl = *it;
10081 settings::USBController settingsCtrl;
10082
10083 settingsCtrl.strName = ctrl->i_getName();
10084 settingsCtrl.enmType = ctrl->i_getControllerType();
10085
10086 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10087 }
10088
10089 /* USB device filters (required) */
10090 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10091 if (FAILED(hrc)) throw hrc;
10092
10093 /* Network adapters (required) */
10094 size_t const uMaxNICs =
10095 RT_MIN(PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType), mNetworkAdapters.size());
10096 data.llNetworkAdapters.clear();
10097 /* Write out only the nominal number of network adapters for this
10098 * chipset type. Since Machine::commit() hasn't been called there
10099 * may be extra NIC settings in the vector. */
10100 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10101 {
10102 settings::NetworkAdapter nic;
10103 nic.ulSlot = (uint32_t)slot;
10104 /* paranoia check... must not be NULL, but must not crash either. */
10105 if (mNetworkAdapters[slot])
10106 {
10107 if (mNetworkAdapters[slot]->i_hasDefaults())
10108 continue;
10109
10110 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10111 if (FAILED(hrc)) throw hrc;
10112
10113 data.llNetworkAdapters.push_back(nic);
10114 }
10115 }
10116
10117 /* Serial ports */
10118 data.llSerialPorts.clear();
10119 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10120 {
10121 if (mSerialPorts[slot]->i_hasDefaults())
10122 continue;
10123
10124 settings::SerialPort s;
10125 s.ulSlot = slot;
10126 hrc = mSerialPorts[slot]->i_saveSettings(s);
10127 if (FAILED(hrc)) return hrc;
10128
10129 data.llSerialPorts.push_back(s);
10130 }
10131
10132 /* Parallel ports */
10133 data.llParallelPorts.clear();
10134 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10135 {
10136 if (mParallelPorts[slot]->i_hasDefaults())
10137 continue;
10138
10139 settings::ParallelPort p;
10140 p.ulSlot = slot;
10141 hrc = mParallelPorts[slot]->i_saveSettings(p);
10142 if (FAILED(hrc)) return hrc;
10143
10144 data.llParallelPorts.push_back(p);
10145 }
10146
10147 /* Audio settings */
10148 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10149 if (FAILED(hrc)) return hrc;
10150
10151 hrc = i_saveStorageControllers(data.storage);
10152 if (FAILED(hrc)) return hrc;
10153
10154 /* Shared folders */
10155 data.llSharedFolders.clear();
10156 for (HWData::SharedFolderList::const_iterator
10157 it = mHWData->mSharedFolders.begin();
10158 it != mHWData->mSharedFolders.end();
10159 ++it)
10160 {
10161 SharedFolder *pSF = *it;
10162 AutoCaller sfCaller(pSF);
10163 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10164 settings::SharedFolder sf;
10165 sf.strName = pSF->i_getName();
10166 sf.strHostPath = pSF->i_getHostPath();
10167 sf.fWritable = !!pSF->i_isWritable();
10168 sf.fAutoMount = !!pSF->i_isAutoMounted();
10169 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10170
10171 data.llSharedFolders.push_back(sf);
10172 }
10173
10174 // clipboard
10175 data.clipboardMode = mHWData->mClipboardMode;
10176 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10177
10178 // drag'n'drop
10179 data.dndMode = mHWData->mDnDMode;
10180
10181 /* Guest */
10182 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10183
10184 // IO settings
10185 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10186 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10187
10188 /* BandwidthControl (required) */
10189 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10190 if (FAILED(hrc)) throw hrc;
10191
10192 /* Host PCI devices */
10193 data.pciAttachments.clear();
10194 for (HWData::PCIDeviceAssignmentList::const_iterator
10195 it = mHWData->mPCIDeviceAssignments.begin();
10196 it != mHWData->mPCIDeviceAssignments.end();
10197 ++it)
10198 {
10199 ComObjPtr<PCIDeviceAttachment> pda = *it;
10200 settings::HostPCIDeviceAttachment hpda;
10201
10202 hrc = pda->i_saveSettings(hpda);
10203 if (FAILED(hrc)) throw hrc;
10204
10205 data.pciAttachments.push_back(hpda);
10206 }
10207
10208 // guest properties
10209 data.llGuestProperties.clear();
10210#ifdef VBOX_WITH_GUEST_PROPS
10211 for (HWData::GuestPropertyMap::const_iterator
10212 it = mHWData->mGuestProperties.begin();
10213 it != mHWData->mGuestProperties.end();
10214 ++it)
10215 {
10216 HWData::GuestProperty property = it->second;
10217
10218 /* Remove transient guest properties at shutdown unless we
10219 * are saving state. Note that restoring snapshot intentionally
10220 * keeps them, they will be removed if appropriate once the final
10221 * machine state is set (as crashes etc. need to work). */
10222 if ( ( mData->mMachineState == MachineState_PoweredOff
10223 || mData->mMachineState == MachineState_Aborted
10224 || mData->mMachineState == MachineState_Teleported)
10225 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10226 continue;
10227 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10228 prop.strName = it->first;
10229 prop.strValue = property.strValue;
10230 prop.timestamp = (uint64_t)property.mTimestamp;
10231 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10232 GuestPropWriteFlags(property.mFlags, szFlags);
10233 prop.strFlags = szFlags;
10234
10235 data.llGuestProperties.push_back(prop);
10236 }
10237
10238 /* I presume this doesn't require a backup(). */
10239 mData->mGuestPropertiesModified = FALSE;
10240#endif /* VBOX_WITH_GUEST_PROPS defined */
10241
10242 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10243 if (FAILED(hrc)) throw hrc;
10244
10245 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10246 *pAutostart = mHWData->mAutostart;
10247
10248 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10249 }
10250 catch (std::bad_alloc &)
10251 {
10252 return E_OUTOFMEMORY;
10253 }
10254
10255 AssertComRC(hrc);
10256 return hrc;
10257}
10258
10259/**
10260 * Saves the storage controller configuration.
10261 *
10262 * @param data storage settings.
10263 */
10264HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10265{
10266 data.llStorageControllers.clear();
10267
10268 for (StorageControllerList::const_iterator
10269 it = mStorageControllers->begin();
10270 it != mStorageControllers->end();
10271 ++it)
10272 {
10273 ComObjPtr<StorageController> pCtl = *it;
10274
10275 settings::StorageController ctl;
10276 ctl.strName = pCtl->i_getName();
10277 ctl.controllerType = pCtl->i_getControllerType();
10278 ctl.storageBus = pCtl->i_getStorageBus();
10279 ctl.ulInstance = pCtl->i_getInstance();
10280 ctl.fBootable = pCtl->i_getBootable();
10281
10282 /* Save the port count. */
10283 ULONG portCount;
10284 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10285 ComAssertComRCRet(hrc, hrc);
10286 ctl.ulPortCount = portCount;
10287
10288 /* Save fUseHostIOCache */
10289 BOOL fUseHostIOCache;
10290 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10291 ComAssertComRCRet(hrc, hrc);
10292 ctl.fUseHostIOCache = !!fUseHostIOCache;
10293
10294 /* save the devices now. */
10295 hrc = i_saveStorageDevices(pCtl, ctl);
10296 ComAssertComRCRet(hrc, hrc);
10297
10298 data.llStorageControllers.push_back(ctl);
10299 }
10300
10301 return S_OK;
10302}
10303
10304/**
10305 * Saves the hard disk configuration.
10306 */
10307HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10308 settings::StorageController &data)
10309{
10310 MediumAttachmentList atts;
10311
10312 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10313 if (FAILED(hrc)) return hrc;
10314
10315 data.llAttachedDevices.clear();
10316 for (MediumAttachmentList::const_iterator
10317 it = atts.begin();
10318 it != atts.end();
10319 ++it)
10320 {
10321 settings::AttachedDevice dev;
10322 IMediumAttachment *iA = *it;
10323 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10324 Medium *pMedium = pAttach->i_getMedium();
10325
10326 dev.deviceType = pAttach->i_getType();
10327 dev.lPort = pAttach->i_getPort();
10328 dev.lDevice = pAttach->i_getDevice();
10329 dev.fPassThrough = pAttach->i_getPassthrough();
10330 dev.fHotPluggable = pAttach->i_getHotPluggable();
10331 if (pMedium)
10332 {
10333 if (pMedium->i_isHostDrive())
10334 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10335 else
10336 dev.uuid = pMedium->i_getId();
10337 dev.fTempEject = pAttach->i_getTempEject();
10338 dev.fNonRotational = pAttach->i_getNonRotational();
10339 dev.fDiscard = pAttach->i_getDiscard();
10340 }
10341
10342 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10343
10344 data.llAttachedDevices.push_back(dev);
10345 }
10346
10347 return S_OK;
10348}
10349
10350/**
10351 * Saves machine state settings as defined by aFlags
10352 * (SaveSTS_* values).
10353 *
10354 * @param aFlags Combination of SaveSTS_* flags.
10355 *
10356 * @note Locks objects for writing.
10357 */
10358HRESULT Machine::i_saveStateSettings(int aFlags)
10359{
10360 if (aFlags == 0)
10361 return S_OK;
10362
10363 AutoCaller autoCaller(this);
10364 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10365
10366 /* This object's write lock is also necessary to serialize file access
10367 * (prevent concurrent reads and writes) */
10368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10369
10370 HRESULT hrc = S_OK;
10371
10372 Assert(mData->pMachineConfigFile);
10373
10374 try
10375 {
10376 if (aFlags & SaveSTS_CurStateModified)
10377 mData->pMachineConfigFile->fCurrentStateModified = true;
10378
10379 if (aFlags & SaveSTS_StateFilePath)
10380 {
10381 if (!mSSData->strStateFilePath.isEmpty())
10382 /* try to make the file name relative to the settings file dir */
10383 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10384 else
10385 mData->pMachineConfigFile->strStateFile.setNull();
10386 }
10387
10388 if (aFlags & SaveSTS_StateTimeStamp)
10389 {
10390 Assert( mData->mMachineState != MachineState_Aborted
10391 || mSSData->strStateFilePath.isEmpty());
10392
10393 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10394
10395 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10396 || mData->mMachineState == MachineState_AbortedSaved);
10397/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10398 }
10399
10400 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10401 }
10402 catch (...)
10403 {
10404 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10405 }
10406
10407 return hrc;
10408}
10409
10410/**
10411 * Ensures that the given medium is added to a media registry. If this machine
10412 * was created with 4.0 or later, then the machine registry is used. Otherwise
10413 * the global VirtualBox media registry is used.
10414 *
10415 * Caller must NOT hold machine lock, media tree or any medium locks!
10416 *
10417 * @param pMedium
10418 */
10419void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10420{
10421 /* Paranoia checks: do not hold machine or media tree locks. */
10422 AssertReturnVoid(!isWriteLockOnCurrentThread());
10423 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10424
10425 ComObjPtr<Medium> pBase;
10426 {
10427 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10428 pBase = pMedium->i_getBase();
10429 }
10430
10431 /* Paranoia checks: do not hold medium locks. */
10432 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10433 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10434
10435 // decide which medium registry to use now that the medium is attached:
10436 Guid uuid;
10437 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10438 if (fCanHaveOwnMediaRegistry)
10439 // machine XML is VirtualBox 4.0 or higher:
10440 uuid = i_getId(); // machine UUID
10441 else
10442 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10443
10444 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10445 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10446 if (pMedium->i_addRegistry(uuid))
10447 mParent->i_markRegistryModified(uuid);
10448
10449 /* For more complex hard disk structures it can happen that the base
10450 * medium isn't yet associated with any medium registry. Do that now. */
10451 if (pMedium != pBase)
10452 {
10453 /* Tree lock needed by Medium::addRegistryAll. */
10454 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10455 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10456 {
10457 treeLock.release();
10458 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10459 treeLock.acquire();
10460 }
10461 if (pBase->i_addRegistryAll(uuid))
10462 {
10463 treeLock.release();
10464 mParent->i_markRegistryModified(uuid);
10465 }
10466 }
10467}
10468
10469/**
10470 * Physically deletes a file belonging to a machine.
10471 *
10472 * @returns HRESULT
10473 * @retval VBOX_E_FILE_ERROR on failure.
10474 * @param strFile File to delete.
10475 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
10476 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
10477 * @param strWhat File hint which will be used when setting an error. Optional.
10478 * @param prc Where to return IPRT's status code on failure.
10479 * Optional and can be NULL.
10480 */
10481HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
10482 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
10483{
10484 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
10485
10486 HRESULT hrc = S_OK;
10487
10488 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
10489
10490 int vrc = RTFileDelete(strFile.c_str());
10491 if (RT_FAILURE(vrc))
10492 {
10493 if ( !fIgnoreFailures
10494 /* Don't (externally) bitch about stuff which doesn't exist. */
10495 && ( vrc != VERR_FILE_NOT_FOUND
10496 && vrc != VERR_PATH_NOT_FOUND
10497 )
10498 )
10499 {
10500 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
10501
10502 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
10503 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
10504 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
10505 }
10506 }
10507
10508 if (prc)
10509 *prc = vrc;
10510 return hrc;
10511}
10512
10513/**
10514 * Creates differencing hard disks for all normal hard disks attached to this
10515 * machine and a new set of attachments to refer to created disks.
10516 *
10517 * Used when taking a snapshot or when deleting the current state. Gets called
10518 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10519 *
10520 * This method assumes that mMediumAttachments contains the original hard disk
10521 * attachments it needs to create diffs for. On success, these attachments will
10522 * be replaced with the created diffs.
10523 *
10524 * Attachments with non-normal hard disks are left as is.
10525 *
10526 * If @a aOnline is @c false then the original hard disks that require implicit
10527 * diffs will be locked for reading. Otherwise it is assumed that they are
10528 * already locked for writing (when the VM was started). Note that in the latter
10529 * case it is responsibility of the caller to lock the newly created diffs for
10530 * writing if this method succeeds.
10531 *
10532 * @param aProgress Progress object to run (must contain at least as
10533 * many operations left as the number of hard disks
10534 * attached).
10535 * @param aWeight Weight of this operation.
10536 * @param aOnline Whether the VM was online prior to this operation.
10537 *
10538 * @note The progress object is not marked as completed, neither on success nor
10539 * on failure. This is a responsibility of the caller.
10540 *
10541 * @note Locks this object and the media tree for writing.
10542 */
10543HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10544 ULONG aWeight,
10545 bool aOnline)
10546{
10547 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10548
10549 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10550 AssertReturn(!!pProgressControl, E_INVALIDARG);
10551
10552 AutoCaller autoCaller(this);
10553 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10554
10555 AutoMultiWriteLock2 alock(this->lockHandle(),
10556 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10557
10558 /* must be in a protective state because we release the lock below */
10559 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10560 || mData->mMachineState == MachineState_OnlineSnapshotting
10561 || mData->mMachineState == MachineState_LiveSnapshotting
10562 || mData->mMachineState == MachineState_RestoringSnapshot
10563 || mData->mMachineState == MachineState_DeletingSnapshot
10564 , E_FAIL);
10565
10566 HRESULT hrc = S_OK;
10567
10568 // use appropriate locked media map (online or offline)
10569 MediumLockListMap lockedMediaOffline;
10570 MediumLockListMap *lockedMediaMap;
10571 if (aOnline)
10572 lockedMediaMap = &mData->mSession.mLockedMedia;
10573 else
10574 lockedMediaMap = &lockedMediaOffline;
10575
10576 try
10577 {
10578 if (!aOnline)
10579 {
10580 /* lock all attached hard disks early to detect "in use"
10581 * situations before creating actual diffs */
10582 for (MediumAttachmentList::const_iterator
10583 it = mMediumAttachments->begin();
10584 it != mMediumAttachments->end();
10585 ++it)
10586 {
10587 MediumAttachment *pAtt = *it;
10588 if (pAtt->i_getType() == DeviceType_HardDisk)
10589 {
10590 Medium *pMedium = pAtt->i_getMedium();
10591 Assert(pMedium);
10592
10593 MediumLockList *pMediumLockList(new MediumLockList());
10594 alock.release();
10595 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10596 NULL /* pToLockWrite */,
10597 false /* fMediumLockWriteAll */,
10598 NULL,
10599 *pMediumLockList);
10600 alock.acquire();
10601 if (FAILED(hrc))
10602 {
10603 delete pMediumLockList;
10604 throw hrc;
10605 }
10606 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10607 if (FAILED(hrc))
10608 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
10609 }
10610 }
10611
10612 /* Now lock all media. If this fails, nothing is locked. */
10613 alock.release();
10614 hrc = lockedMediaMap->Lock();
10615 alock.acquire();
10616 if (FAILED(hrc))
10617 throw setError(hrc, tr("Locking of attached media failed"));
10618 }
10619
10620 /* remember the current list (note that we don't use backup() since
10621 * mMediumAttachments may be already backed up) */
10622 MediumAttachmentList atts = *mMediumAttachments.data();
10623
10624 /* start from scratch */
10625 mMediumAttachments->clear();
10626
10627 /* go through remembered attachments and create diffs for normal hard
10628 * disks and attach them */
10629 for (MediumAttachmentList::const_iterator
10630 it = atts.begin();
10631 it != atts.end();
10632 ++it)
10633 {
10634 MediumAttachment *pAtt = *it;
10635
10636 DeviceType_T devType = pAtt->i_getType();
10637 Medium *pMedium = pAtt->i_getMedium();
10638
10639 if ( devType != DeviceType_HardDisk
10640 || pMedium == NULL
10641 || pMedium->i_getType() != MediumType_Normal)
10642 {
10643 /* copy the attachment as is */
10644
10645 /** @todo the progress object created in SessionMachine::TakeSnaphot
10646 * only expects operations for hard disks. Later other
10647 * device types need to show up in the progress as well. */
10648 if (devType == DeviceType_HardDisk)
10649 {
10650 if (pMedium == NULL)
10651 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10652 aWeight); // weight
10653 else
10654 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10655 pMedium->i_getBase()->i_getName().c_str()).raw(),
10656 aWeight); // weight
10657 }
10658
10659 mMediumAttachments->push_back(pAtt);
10660 continue;
10661 }
10662
10663 /* need a diff */
10664 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10665 pMedium->i_getBase()->i_getName().c_str()).raw(),
10666 aWeight); // weight
10667
10668 Utf8Str strFullSnapshotFolder;
10669 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10670
10671 ComObjPtr<Medium> diff;
10672 diff.createObject();
10673 // store the diff in the same registry as the parent
10674 // (this cannot fail here because we can't create implicit diffs for
10675 // unregistered images)
10676 Guid uuidRegistryParent;
10677 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10678 Assert(fInRegistry); NOREF(fInRegistry);
10679 hrc = diff->init(mParent,
10680 pMedium->i_getPreferredDiffFormat(),
10681 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10682 uuidRegistryParent,
10683 DeviceType_HardDisk);
10684 if (FAILED(hrc)) throw hrc;
10685
10686 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10687 * the push_back? Looks like we're going to release medium with the
10688 * wrong kind of lock (general issue with if we fail anywhere at all)
10689 * and an orphaned VDI in the snapshots folder. */
10690
10691 /* update the appropriate lock list */
10692 MediumLockList *pMediumLockList;
10693 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
10694 AssertComRCThrowRC(hrc);
10695 if (aOnline)
10696 {
10697 alock.release();
10698 /* The currently attached medium will be read-only, change
10699 * the lock type to read. */
10700 hrc = pMediumLockList->Update(pMedium, false);
10701 alock.acquire();
10702 AssertComRCThrowRC(hrc);
10703 }
10704
10705 /* release the locks before the potentially lengthy operation */
10706 alock.release();
10707 hrc = pMedium->i_createDiffStorage(diff,
10708 pMedium->i_getPreferredDiffVariant(),
10709 pMediumLockList,
10710 NULL /* aProgress */,
10711 true /* aWait */,
10712 false /* aNotify */);
10713 alock.acquire();
10714 if (FAILED(hrc)) throw hrc;
10715
10716 /* actual lock list update is done in Machine::i_commitMedia */
10717
10718 hrc = diff->i_addBackReference(mData->mUuid);
10719 AssertComRCThrowRC(hrc);
10720
10721 /* add a new attachment */
10722 ComObjPtr<MediumAttachment> attachment;
10723 attachment.createObject();
10724 hrc = attachment->init(this,
10725 diff,
10726 pAtt->i_getControllerName(),
10727 pAtt->i_getPort(),
10728 pAtt->i_getDevice(),
10729 DeviceType_HardDisk,
10730 true /* aImplicit */,
10731 false /* aPassthrough */,
10732 false /* aTempEject */,
10733 pAtt->i_getNonRotational(),
10734 pAtt->i_getDiscard(),
10735 pAtt->i_getHotPluggable(),
10736 pAtt->i_getBandwidthGroup());
10737 if (FAILED(hrc)) throw hrc;
10738
10739 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10740 AssertComRCThrowRC(hrc);
10741 mMediumAttachments->push_back(attachment);
10742 }
10743 }
10744 catch (HRESULT hrcXcpt)
10745 {
10746 hrc = hrcXcpt;
10747 }
10748
10749 /* unlock all hard disks we locked when there is no VM */
10750 if (!aOnline)
10751 {
10752 ErrorInfoKeeper eik;
10753
10754 HRESULT hrc2 = lockedMediaMap->Clear();
10755 AssertComRC(hrc2);
10756 }
10757
10758 return hrc;
10759}
10760
10761/**
10762 * Deletes implicit differencing hard disks created either by
10763 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10764 * mMediumAttachments.
10765 *
10766 * Note that to delete hard disks created by #attachDevice() this method is
10767 * called from #i_rollbackMedia() when the changes are rolled back.
10768 *
10769 * @note Locks this object and the media tree for writing.
10770 */
10771HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10772{
10773 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10774
10775 AutoCaller autoCaller(this);
10776 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10777
10778 AutoMultiWriteLock2 alock(this->lockHandle(),
10779 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10780
10781 /* We absolutely must have backed up state. */
10782 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10783
10784 /* Check if there are any implicitly created diff images. */
10785 bool fImplicitDiffs = false;
10786 for (MediumAttachmentList::const_iterator
10787 it = mMediumAttachments->begin();
10788 it != mMediumAttachments->end();
10789 ++it)
10790 {
10791 const ComObjPtr<MediumAttachment> &pAtt = *it;
10792 if (pAtt->i_isImplicit())
10793 {
10794 fImplicitDiffs = true;
10795 break;
10796 }
10797 }
10798 /* If there is nothing to do, leave early. This saves lots of image locking
10799 * effort. It also avoids a MachineStateChanged event without real reason.
10800 * This is important e.g. when loading a VM config, because there should be
10801 * no events. Otherwise API clients can become thoroughly confused for
10802 * inaccessible VMs (the code for loading VM configs uses this method for
10803 * cleanup if the config makes no sense), as they take such events as an
10804 * indication that the VM is alive, and they would force the VM config to
10805 * be reread, leading to an endless loop. */
10806 if (!fImplicitDiffs)
10807 return S_OK;
10808
10809 HRESULT hrc = S_OK;
10810 MachineState_T oldState = mData->mMachineState;
10811
10812 /* will release the lock before the potentially lengthy operation,
10813 * so protect with the special state (unless already protected) */
10814 if ( oldState != MachineState_Snapshotting
10815 && oldState != MachineState_OnlineSnapshotting
10816 && oldState != MachineState_LiveSnapshotting
10817 && oldState != MachineState_RestoringSnapshot
10818 && oldState != MachineState_DeletingSnapshot
10819 && oldState != MachineState_DeletingSnapshotOnline
10820 && oldState != MachineState_DeletingSnapshotPaused
10821 )
10822 i_setMachineState(MachineState_SettingUp);
10823
10824 // use appropriate locked media map (online or offline)
10825 MediumLockListMap lockedMediaOffline;
10826 MediumLockListMap *lockedMediaMap;
10827 if (aOnline)
10828 lockedMediaMap = &mData->mSession.mLockedMedia;
10829 else
10830 lockedMediaMap = &lockedMediaOffline;
10831
10832 try
10833 {
10834 if (!aOnline)
10835 {
10836 /* lock all attached hard disks early to detect "in use"
10837 * situations before deleting actual diffs */
10838 for (MediumAttachmentList::const_iterator
10839 it = mMediumAttachments->begin();
10840 it != mMediumAttachments->end();
10841 ++it)
10842 {
10843 MediumAttachment *pAtt = *it;
10844 if (pAtt->i_getType() == DeviceType_HardDisk)
10845 {
10846 Medium *pMedium = pAtt->i_getMedium();
10847 Assert(pMedium);
10848
10849 MediumLockList *pMediumLockList(new MediumLockList());
10850 alock.release();
10851 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10852 NULL /* pToLockWrite */,
10853 false /* fMediumLockWriteAll */,
10854 NULL,
10855 *pMediumLockList);
10856 alock.acquire();
10857
10858 if (FAILED(hrc))
10859 {
10860 delete pMediumLockList;
10861 throw hrc;
10862 }
10863
10864 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10865 if (FAILED(hrc))
10866 throw hrc;
10867 }
10868 }
10869
10870 if (FAILED(hrc))
10871 throw hrc;
10872 } // end of offline
10873
10874 /* Lock lists are now up to date and include implicitly created media */
10875
10876 /* Go through remembered attachments and delete all implicitly created
10877 * diffs and fix up the attachment information */
10878 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10879 MediumAttachmentList implicitAtts;
10880 for (MediumAttachmentList::const_iterator
10881 it = mMediumAttachments->begin();
10882 it != mMediumAttachments->end();
10883 ++it)
10884 {
10885 ComObjPtr<MediumAttachment> pAtt = *it;
10886 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10887 if (pMedium.isNull())
10888 continue;
10889
10890 // Implicit attachments go on the list for deletion and back references are removed.
10891 if (pAtt->i_isImplicit())
10892 {
10893 /* Deassociate and mark for deletion */
10894 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10895 hrc = pMedium->i_removeBackReference(mData->mUuid);
10896 if (FAILED(hrc))
10897 throw hrc;
10898 implicitAtts.push_back(pAtt);
10899 continue;
10900 }
10901
10902 /* Was this medium attached before? */
10903 if (!i_findAttachment(oldAtts, pMedium))
10904 {
10905 /* no: de-associate */
10906 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10907 hrc = pMedium->i_removeBackReference(mData->mUuid);
10908 if (FAILED(hrc))
10909 throw hrc;
10910 continue;
10911 }
10912 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10913 }
10914
10915 /* If there are implicit attachments to delete, throw away the lock
10916 * map contents (which will unlock all media) since the medium
10917 * attachments will be rolled back. Below we need to completely
10918 * recreate the lock map anyway since it is infinitely complex to
10919 * do this incrementally (would need reconstructing each attachment
10920 * change, which would be extremely hairy). */
10921 if (implicitAtts.size() != 0)
10922 {
10923 ErrorInfoKeeper eik;
10924
10925 HRESULT hrc2 = lockedMediaMap->Clear();
10926 AssertComRC(hrc2);
10927 }
10928
10929 /* rollback hard disk changes */
10930 mMediumAttachments.rollback();
10931
10932 MultiResult mrc(S_OK);
10933
10934 // Delete unused implicit diffs.
10935 if (implicitAtts.size() != 0)
10936 {
10937 alock.release();
10938
10939 for (MediumAttachmentList::const_iterator
10940 it = implicitAtts.begin();
10941 it != implicitAtts.end();
10942 ++it)
10943 {
10944 // Remove medium associated with this attachment.
10945 ComObjPtr<MediumAttachment> pAtt = *it;
10946 Assert(pAtt);
10947 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10948 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10949 Assert(pMedium);
10950
10951 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10952 // continue on delete failure, just collect error messages
10953 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
10954 pMedium->i_getLocationFull().c_str() ));
10955 mrc = hrc;
10956 }
10957 // Clear the list of deleted implicit attachments now, while not
10958 // holding the lock, as it will ultimately trigger Medium::uninit()
10959 // calls which assume that the media tree lock isn't held.
10960 implicitAtts.clear();
10961
10962 alock.acquire();
10963
10964 /* if there is a VM recreate media lock map as mentioned above,
10965 * otherwise it is a waste of time and we leave things unlocked */
10966 if (aOnline)
10967 {
10968 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10969 /* must never be NULL, but better safe than sorry */
10970 if (!pMachine.isNull())
10971 {
10972 alock.release();
10973 hrc = mData->mSession.mMachine->i_lockMedia();
10974 alock.acquire();
10975 if (FAILED(hrc))
10976 throw hrc;
10977 }
10978 }
10979 }
10980 }
10981 catch (HRESULT hrcXcpt)
10982 {
10983 hrc = hrcXcpt;
10984 }
10985
10986 if (mData->mMachineState == MachineState_SettingUp)
10987 i_setMachineState(oldState);
10988
10989 /* unlock all hard disks we locked when there is no VM */
10990 if (!aOnline)
10991 {
10992 ErrorInfoKeeper eik;
10993
10994 HRESULT hrc2 = lockedMediaMap->Clear();
10995 AssertComRC(hrc2);
10996 }
10997
10998 return hrc;
10999}
11000
11001
11002/**
11003 * Looks through the given list of media attachments for one with the given parameters
11004 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11005 * can be searched as well if needed.
11006 *
11007 * @param ll
11008 * @param aControllerName
11009 * @param aControllerPort
11010 * @param aDevice
11011 * @return
11012 */
11013MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11014 const Utf8Str &aControllerName,
11015 LONG aControllerPort,
11016 LONG aDevice)
11017{
11018 for (MediumAttachmentList::const_iterator
11019 it = ll.begin();
11020 it != ll.end();
11021 ++it)
11022 {
11023 MediumAttachment *pAttach = *it;
11024 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11025 return pAttach;
11026 }
11027
11028 return NULL;
11029}
11030
11031/**
11032 * Looks through the given list of media attachments for one with the given parameters
11033 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11034 * can be searched as well if needed.
11035 *
11036 * @param ll
11037 * @param pMedium
11038 * @return
11039 */
11040MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11041 ComObjPtr<Medium> pMedium)
11042{
11043 for (MediumAttachmentList::const_iterator
11044 it = ll.begin();
11045 it != ll.end();
11046 ++it)
11047 {
11048 MediumAttachment *pAttach = *it;
11049 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11050 if (pMediumThis == pMedium)
11051 return pAttach;
11052 }
11053
11054 return NULL;
11055}
11056
11057/**
11058 * Looks through the given list of media attachments for one with the given parameters
11059 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11060 * can be searched as well if needed.
11061 *
11062 * @param ll
11063 * @param id
11064 * @return
11065 */
11066MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11067 Guid &id)
11068{
11069 for (MediumAttachmentList::const_iterator
11070 it = ll.begin();
11071 it != ll.end();
11072 ++it)
11073 {
11074 MediumAttachment *pAttach = *it;
11075 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11076 if (pMediumThis->i_getId() == id)
11077 return pAttach;
11078 }
11079
11080 return NULL;
11081}
11082
11083/**
11084 * Main implementation for Machine::DetachDevice. This also gets called
11085 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11086 *
11087 * @param pAttach Medium attachment to detach.
11088 * @param writeLock Machine write lock which the caller must have locked once.
11089 * This may be released temporarily in here.
11090 * @param pSnapshot If NULL, then the detachment is for the current machine.
11091 * Otherwise this is for a SnapshotMachine, and this must be
11092 * its snapshot.
11093 * @return
11094 */
11095HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11096 AutoWriteLock &writeLock,
11097 Snapshot *pSnapshot)
11098{
11099 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11100 DeviceType_T mediumType = pAttach->i_getType();
11101
11102 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11103
11104 if (pAttach->i_isImplicit())
11105 {
11106 /* attempt to implicitly delete the implicitly created diff */
11107
11108 /// @todo move the implicit flag from MediumAttachment to Medium
11109 /// and forbid any hard disk operation when it is implicit. Or maybe
11110 /// a special media state for it to make it even more simple.
11111
11112 Assert(mMediumAttachments.isBackedUp());
11113
11114 /* will release the lock before the potentially lengthy operation, so
11115 * protect with the special state */
11116 MachineState_T oldState = mData->mMachineState;
11117 i_setMachineState(MachineState_SettingUp);
11118
11119 writeLock.release();
11120
11121 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11122
11123 writeLock.acquire();
11124
11125 i_setMachineState(oldState);
11126
11127 if (FAILED(hrc)) return hrc;
11128 }
11129
11130 i_setModified(IsModified_Storage);
11131 mMediumAttachments.backup();
11132 mMediumAttachments->remove(pAttach);
11133
11134 if (!oldmedium.isNull())
11135 {
11136 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11137 if (pSnapshot)
11138 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11139 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11140 else if (mediumType != DeviceType_HardDisk)
11141 oldmedium->i_removeBackReference(mData->mUuid);
11142 }
11143
11144 return S_OK;
11145}
11146
11147/**
11148 * Goes thru all media of the given list and
11149 *
11150 * 1) calls i_detachDevice() on each of them for this machine and
11151 * 2) adds all Medium objects found in the process to the given list,
11152 * depending on cleanupMode.
11153 *
11154 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11155 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11156 * media to the list.
11157 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11158 * also removable media if they are located in the VM folder and referenced
11159 * only by this VM (media prepared by unattended installer).
11160 *
11161 * This gets called from Machine::Unregister, both for the actual Machine and
11162 * the SnapshotMachine objects that might be found in the snapshots.
11163 *
11164 * Requires caller and locking. The machine lock must be passed in because it
11165 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11166 *
11167 * @param writeLock Machine lock from top-level caller; this gets passed to
11168 * i_detachDevice.
11169 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11170 * object if called for a SnapshotMachine.
11171 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11172 * added to llMedia; if Full, then all media get added;
11173 * otherwise no media get added.
11174 * @param llMedia Caller's list to receive Medium objects which got detached so
11175 * caller can close() them, depending on cleanupMode.
11176 * @return
11177 */
11178HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11179 Snapshot *pSnapshot,
11180 CleanupMode_T cleanupMode,
11181 MediaList &llMedia)
11182{
11183 Assert(isWriteLockOnCurrentThread());
11184
11185 HRESULT hrc;
11186
11187 // make a temporary list because i_detachDevice invalidates iterators into
11188 // mMediumAttachments
11189 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11190
11191 for (MediumAttachmentList::iterator
11192 it = llAttachments2.begin();
11193 it != llAttachments2.end();
11194 ++it)
11195 {
11196 ComObjPtr<MediumAttachment> &pAttach = *it;
11197 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11198
11199 if (!pMedium.isNull())
11200 {
11201 AutoCaller mac(pMedium);
11202 if (FAILED(mac.hrc())) return mac.hrc();
11203 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11204 DeviceType_T devType = pMedium->i_getDeviceType();
11205 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11206 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11207 strMediumLocation.stripFilename();
11208 Utf8Str strMachineFolder = i_getSettingsFileFull();
11209 strMachineFolder.stripFilename();
11210 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11211 && devType == DeviceType_HardDisk)
11212 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11213 && ( devType == DeviceType_HardDisk
11214 || ( cBackRefs <= 1
11215 && strMediumLocation == strMachineFolder
11216 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11217 || (cleanupMode == CleanupMode_Full)
11218 )
11219 {
11220 llMedia.push_back(pMedium);
11221 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11222 /* Not allowed to keep this lock as below we need the parent
11223 * medium lock, and the lock order is parent to child. */
11224 lock.release();
11225 /*
11226 * Search for media which are not attached to any machine, but
11227 * in the chain to an attached disk. Media are only consided
11228 * if they are:
11229 * - have only one child
11230 * - no references to any machines
11231 * - are of normal medium type
11232 */
11233 while (!pParent.isNull())
11234 {
11235 AutoCaller mac1(pParent);
11236 if (FAILED(mac1.hrc())) return mac1.hrc();
11237 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11238 if (pParent->i_getChildren().size() == 1)
11239 {
11240 if ( pParent->i_getMachineBackRefCount() == 0
11241 && pParent->i_getType() == MediumType_Normal
11242 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11243 llMedia.push_back(pParent);
11244 }
11245 else
11246 break;
11247 pParent = pParent->i_getParent();
11248 }
11249 }
11250 }
11251
11252 // real machine: then we need to use the proper method
11253 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11254
11255 if (FAILED(hrc))
11256 return hrc;
11257 }
11258
11259 return S_OK;
11260}
11261
11262/**
11263 * Perform deferred hard disk detachments.
11264 *
11265 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11266 * changed (not backed up).
11267 *
11268 * If @a aOnline is @c true then this method will also unlock the old hard
11269 * disks for which the new implicit diffs were created and will lock these new
11270 * diffs for writing.
11271 *
11272 * @param aOnline Whether the VM was online prior to this operation.
11273 *
11274 * @note Locks this object for writing!
11275 */
11276void Machine::i_commitMedia(bool aOnline /*= false*/)
11277{
11278 AutoCaller autoCaller(this);
11279 AssertComRCReturnVoid(autoCaller.hrc());
11280
11281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11282
11283 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11284
11285 HRESULT hrc = S_OK;
11286
11287 /* no attach/detach operations -- nothing to do */
11288 if (!mMediumAttachments.isBackedUp())
11289 return;
11290
11291 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11292 bool fMediaNeedsLocking = false;
11293
11294 /* enumerate new attachments */
11295 for (MediumAttachmentList::const_iterator
11296 it = mMediumAttachments->begin();
11297 it != mMediumAttachments->end();
11298 ++it)
11299 {
11300 MediumAttachment *pAttach = *it;
11301
11302 pAttach->i_commit();
11303
11304 Medium *pMedium = pAttach->i_getMedium();
11305 bool fImplicit = pAttach->i_isImplicit();
11306
11307 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11308 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11309 fImplicit));
11310
11311 /** @todo convert all this Machine-based voodoo to MediumAttachment
11312 * based commit logic. */
11313 if (fImplicit)
11314 {
11315 /* convert implicit attachment to normal */
11316 pAttach->i_setImplicit(false);
11317
11318 if ( aOnline
11319 && pMedium
11320 && pAttach->i_getType() == DeviceType_HardDisk
11321 )
11322 {
11323 /* update the appropriate lock list */
11324 MediumLockList *pMediumLockList;
11325 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11326 AssertComRC(hrc);
11327 if (pMediumLockList)
11328 {
11329 /* unlock if there's a need to change the locking */
11330 if (!fMediaNeedsLocking)
11331 {
11332 Assert(mData->mSession.mLockedMedia.IsLocked());
11333 hrc = mData->mSession.mLockedMedia.Unlock();
11334 AssertComRC(hrc);
11335 fMediaNeedsLocking = true;
11336 }
11337 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
11338 AssertComRC(hrc);
11339 hrc = pMediumLockList->Append(pMedium, true);
11340 AssertComRC(hrc);
11341 }
11342 }
11343
11344 continue;
11345 }
11346
11347 if (pMedium)
11348 {
11349 /* was this medium attached before? */
11350 for (MediumAttachmentList::iterator
11351 oldIt = oldAtts.begin();
11352 oldIt != oldAtts.end();
11353 ++oldIt)
11354 {
11355 MediumAttachment *pOldAttach = *oldIt;
11356 if (pOldAttach->i_getMedium() == pMedium)
11357 {
11358 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11359
11360 /* yes: remove from old to avoid de-association */
11361 oldAtts.erase(oldIt);
11362 break;
11363 }
11364 }
11365 }
11366 }
11367
11368 /* enumerate remaining old attachments and de-associate from the
11369 * current machine state */
11370 for (MediumAttachmentList::const_iterator
11371 it = oldAtts.begin();
11372 it != oldAtts.end();
11373 ++it)
11374 {
11375 MediumAttachment *pAttach = *it;
11376 Medium *pMedium = pAttach->i_getMedium();
11377
11378 /* Detach only hard disks, since DVD/floppy media is detached
11379 * instantly in MountMedium. */
11380 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11381 {
11382 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11383
11384 /* now de-associate from the current machine state */
11385 hrc = pMedium->i_removeBackReference(mData->mUuid);
11386 AssertComRC(hrc);
11387
11388 if (aOnline)
11389 {
11390 /* unlock since medium is not used anymore */
11391 MediumLockList *pMediumLockList;
11392 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11393 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
11394 {
11395 /* this happens for online snapshots, there the attachment
11396 * is changing, but only to a diff image created under
11397 * the old one, so there is no separate lock list */
11398 Assert(!pMediumLockList);
11399 }
11400 else
11401 {
11402 AssertComRC(hrc);
11403 if (pMediumLockList)
11404 {
11405 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
11406 AssertComRC(hrc);
11407 }
11408 }
11409 }
11410 }
11411 }
11412
11413 /* take media locks again so that the locking state is consistent */
11414 if (fMediaNeedsLocking)
11415 {
11416 Assert(aOnline);
11417 hrc = mData->mSession.mLockedMedia.Lock();
11418 AssertComRC(hrc);
11419 }
11420
11421 /* commit the hard disk changes */
11422 mMediumAttachments.commit();
11423
11424 if (i_isSessionMachine())
11425 {
11426 /*
11427 * Update the parent machine to point to the new owner.
11428 * This is necessary because the stored parent will point to the
11429 * session machine otherwise and cause crashes or errors later
11430 * when the session machine gets invalid.
11431 */
11432 /** @todo Change the MediumAttachment class to behave like any other
11433 * class in this regard by creating peer MediumAttachment
11434 * objects for session machines and share the data with the peer
11435 * machine.
11436 */
11437 for (MediumAttachmentList::const_iterator
11438 it = mMediumAttachments->begin();
11439 it != mMediumAttachments->end();
11440 ++it)
11441 (*it)->i_updateParentMachine(mPeer);
11442
11443 /* attach new data to the primary machine and reshare it */
11444 mPeer->mMediumAttachments.attach(mMediumAttachments);
11445 }
11446
11447 return;
11448}
11449
11450/**
11451 * Perform deferred deletion of implicitly created diffs.
11452 *
11453 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11454 * changed (not backed up).
11455 *
11456 * @note Locks this object for writing!
11457 */
11458void Machine::i_rollbackMedia()
11459{
11460 AutoCaller autoCaller(this);
11461 AssertComRCReturnVoid(autoCaller.hrc());
11462
11463 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11464 LogFlowThisFunc(("Entering rollbackMedia\n"));
11465
11466 HRESULT hrc = S_OK;
11467
11468 /* no attach/detach operations -- nothing to do */
11469 if (!mMediumAttachments.isBackedUp())
11470 return;
11471
11472 /* enumerate new attachments */
11473 for (MediumAttachmentList::const_iterator
11474 it = mMediumAttachments->begin();
11475 it != mMediumAttachments->end();
11476 ++it)
11477 {
11478 MediumAttachment *pAttach = *it;
11479 /* Fix up the backrefs for DVD/floppy media. */
11480 if (pAttach->i_getType() != DeviceType_HardDisk)
11481 {
11482 Medium *pMedium = pAttach->i_getMedium();
11483 if (pMedium)
11484 {
11485 hrc = pMedium->i_removeBackReference(mData->mUuid);
11486 AssertComRC(hrc);
11487 }
11488 }
11489
11490 (*it)->i_rollback();
11491
11492 pAttach = *it;
11493 /* Fix up the backrefs for DVD/floppy media. */
11494 if (pAttach->i_getType() != DeviceType_HardDisk)
11495 {
11496 Medium *pMedium = pAttach->i_getMedium();
11497 if (pMedium)
11498 {
11499 hrc = pMedium->i_addBackReference(mData->mUuid);
11500 AssertComRC(hrc);
11501 }
11502 }
11503 }
11504
11505 /** @todo convert all this Machine-based voodoo to MediumAttachment
11506 * based rollback logic. */
11507 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11508
11509 return;
11510}
11511
11512/**
11513 * Returns true if the settings file is located in the directory named exactly
11514 * as the machine; this means, among other things, that the machine directory
11515 * should be auto-renamed.
11516 *
11517 * @param aSettingsDir if not NULL, the full machine settings file directory
11518 * name will be assigned there.
11519 *
11520 * @note Doesn't lock anything.
11521 * @note Not thread safe (must be called from this object's lock).
11522 */
11523bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11524{
11525 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11526 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11527 if (aSettingsDir)
11528 *aSettingsDir = strMachineDirName;
11529 strMachineDirName.stripPath(); // vmname
11530 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11531 strConfigFileOnly.stripPath() // vmname.vbox
11532 .stripSuffix(); // vmname
11533 /** @todo hack, make somehow use of ComposeMachineFilename */
11534 if (mUserData->s.fDirectoryIncludesUUID)
11535 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11536
11537 AssertReturn(!strMachineDirName.isEmpty(), false);
11538 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11539
11540 return strMachineDirName == strConfigFileOnly;
11541}
11542
11543/**
11544 * Discards all changes to machine settings.
11545 *
11546 * @param aNotify Whether to notify the direct session about changes or not.
11547 *
11548 * @note Locks objects for writing!
11549 */
11550void Machine::i_rollback(bool aNotify)
11551{
11552 AutoCaller autoCaller(this);
11553 AssertComRCReturn(autoCaller.hrc(), (void)0);
11554
11555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11556
11557 if (!mStorageControllers.isNull())
11558 {
11559 if (mStorageControllers.isBackedUp())
11560 {
11561 /* unitialize all new devices (absent in the backed up list). */
11562 StorageControllerList *backedList = mStorageControllers.backedUpData();
11563 for (StorageControllerList::const_iterator
11564 it = mStorageControllers->begin();
11565 it != mStorageControllers->end();
11566 ++it)
11567 {
11568 if ( std::find(backedList->begin(), backedList->end(), *it)
11569 == backedList->end()
11570 )
11571 {
11572 (*it)->uninit();
11573 }
11574 }
11575
11576 /* restore the list */
11577 mStorageControllers.rollback();
11578 }
11579
11580 /* rollback any changes to devices after restoring the list */
11581 if (mData->flModifications & IsModified_Storage)
11582 {
11583 for (StorageControllerList::const_iterator
11584 it = mStorageControllers->begin();
11585 it != mStorageControllers->end();
11586 ++it)
11587 {
11588 (*it)->i_rollback();
11589 }
11590 }
11591 }
11592
11593 if (!mUSBControllers.isNull())
11594 {
11595 if (mUSBControllers.isBackedUp())
11596 {
11597 /* unitialize all new devices (absent in the backed up list). */
11598 USBControllerList *backedList = mUSBControllers.backedUpData();
11599 for (USBControllerList::const_iterator
11600 it = mUSBControllers->begin();
11601 it != mUSBControllers->end();
11602 ++it)
11603 {
11604 if ( std::find(backedList->begin(), backedList->end(), *it)
11605 == backedList->end()
11606 )
11607 {
11608 (*it)->uninit();
11609 }
11610 }
11611
11612 /* restore the list */
11613 mUSBControllers.rollback();
11614 }
11615
11616 /* rollback any changes to devices after restoring the list */
11617 if (mData->flModifications & IsModified_USB)
11618 {
11619 for (USBControllerList::const_iterator
11620 it = mUSBControllers->begin();
11621 it != mUSBControllers->end();
11622 ++it)
11623 {
11624 (*it)->i_rollback();
11625 }
11626 }
11627 }
11628
11629 mUserData.rollback();
11630
11631 mHWData.rollback();
11632
11633 if (mData->flModifications & IsModified_Storage)
11634 i_rollbackMedia();
11635
11636 if (mPlatform)
11637 {
11638 mPlatform->i_rollback();
11639 i_platformPropertiesUpdate();
11640 }
11641
11642 if (mFirmwareSettings)
11643 mFirmwareSettings->i_rollback();
11644
11645 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11646 mRecordingSettings->i_rollback();
11647
11648 if (mTrustedPlatformModule)
11649 mTrustedPlatformModule->i_rollback();
11650
11651 if (mNvramStore)
11652 mNvramStore->i_rollback();
11653
11654 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11655 mGraphicsAdapter->i_rollback();
11656
11657 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11658 mVRDEServer->i_rollback();
11659
11660 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
11661 mAudioSettings->i_rollback();
11662
11663 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11664 mUSBDeviceFilters->i_rollback();
11665
11666 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11667 mBandwidthControl->i_rollback();
11668
11669 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
11670 mGuestDebugControl->i_rollback();
11671
11672 if (mPlatform && (mData->flModifications & IsModified_Platform))
11673 {
11674 ChipsetType_T enmChipset;
11675 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11676 ComAssertComRC(hrc);
11677
11678 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipset));
11679 }
11680
11681 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11682 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11683 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11684
11685 if (mData->flModifications & IsModified_NetworkAdapters)
11686 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11687 if ( mNetworkAdapters[slot]
11688 && mNetworkAdapters[slot]->i_isModified())
11689 {
11690 mNetworkAdapters[slot]->i_rollback();
11691 networkAdapters[slot] = mNetworkAdapters[slot];
11692 }
11693
11694 if (mData->flModifications & IsModified_SerialPorts)
11695 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11696 if ( mSerialPorts[slot]
11697 && mSerialPorts[slot]->i_isModified())
11698 {
11699 mSerialPorts[slot]->i_rollback();
11700 serialPorts[slot] = mSerialPorts[slot];
11701 }
11702
11703 if (mData->flModifications & IsModified_ParallelPorts)
11704 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11705 if ( mParallelPorts[slot]
11706 && mParallelPorts[slot]->i_isModified())
11707 {
11708 mParallelPorts[slot]->i_rollback();
11709 parallelPorts[slot] = mParallelPorts[slot];
11710 }
11711
11712 if (aNotify)
11713 {
11714 /* inform the direct session about changes */
11715
11716 ComObjPtr<Machine> that = this;
11717 uint32_t flModifications = mData->flModifications;
11718 alock.release();
11719
11720 if (flModifications & IsModified_SharedFolders)
11721 that->i_onSharedFolderChange();
11722
11723 if (flModifications & IsModified_VRDEServer)
11724 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11725 if (flModifications & IsModified_USB)
11726 that->i_onUSBControllerChange();
11727
11728 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11729 if (networkAdapters[slot])
11730 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11731 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11732 if (serialPorts[slot])
11733 that->i_onSerialPortChange(serialPorts[slot]);
11734 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11735 if (parallelPorts[slot])
11736 that->i_onParallelPortChange(parallelPorts[slot]);
11737
11738 if (flModifications & IsModified_Storage)
11739 {
11740 for (StorageControllerList::const_iterator
11741 it = mStorageControllers->begin();
11742 it != mStorageControllers->end();
11743 ++it)
11744 {
11745 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11746 }
11747 }
11748
11749 if (flModifications & IsModified_GuestDebugControl)
11750 that->i_onGuestDebugControlChange(mGuestDebugControl);
11751
11752#if 0
11753 if (flModifications & IsModified_BandwidthControl)
11754 that->onBandwidthControlChange();
11755#endif
11756 }
11757}
11758
11759/**
11760 * Commits all the changes to machine settings.
11761 *
11762 * Note that this operation is supposed to never fail.
11763 *
11764 * @note Locks this object and children for writing.
11765 */
11766void Machine::i_commit()
11767{
11768 AutoCaller autoCaller(this);
11769 AssertComRCReturnVoid(autoCaller.hrc());
11770
11771 AutoCaller peerCaller(mPeer);
11772 AssertComRCReturnVoid(peerCaller.hrc());
11773
11774 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11775
11776 /*
11777 * use safe commit to ensure Snapshot machines (that share mUserData)
11778 * will still refer to a valid memory location
11779 */
11780 mUserData.commitCopy();
11781
11782 mHWData.commit();
11783
11784 if (mMediumAttachments.isBackedUp())
11785 i_commitMedia(Global::IsOnline(mData->mMachineState));
11786
11787 mPlatform->i_commit();
11788 mFirmwareSettings->i_commit();
11789 mRecordingSettings->i_commit();
11790 mTrustedPlatformModule->i_commit();
11791 mNvramStore->i_commit();
11792 mGraphicsAdapter->i_commit();
11793 mVRDEServer->i_commit();
11794 mAudioSettings->i_commit();
11795 mUSBDeviceFilters->i_commit();
11796 mBandwidthControl->i_commit();
11797 mGuestDebugControl->i_commit();
11798
11799 /* Since mNetworkAdapters is a list which might have been changed (resized)
11800 * without using the Backupable<> template we need to handle the copying
11801 * of the list entries manually, including the creation of peers for the
11802 * new objects. */
11803 ChipsetType_T enmChipset;
11804 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11805 ComAssertComRC(hrc);
11806
11807 bool commitNetworkAdapters = false;
11808 size_t const newSize = PlatformProperties::s_getMaxNetworkAdapters(enmChipset);
11809 if (mPeer)
11810 {
11811 size_t const oldSize = mNetworkAdapters.size();
11812 size_t const oldPeerSize = mPeer->mNetworkAdapters.size();
11813
11814 /* commit everything, even the ones which will go away */
11815 for (size_t slot = 0; slot < oldSize; slot++)
11816 mNetworkAdapters[slot]->i_commit();
11817 /* copy over the new entries, creating a peer and uninit the original */
11818 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, oldPeerSize));
11819 /* make sure to have enough room for iterating over the (newly added) slots down below */
11820 if (newSize > oldSize)
11821 {
11822 mNetworkAdapters.resize(newSize);
11823
11824 com::Utf8Str osTypeId;
11825 ComObjPtr<GuestOSType> osType = NULL;
11826 hrc = getOSTypeId(osTypeId);
11827 if (SUCCEEDED(hrc))
11828 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
11829
11830 for (size_t slot = oldSize; slot < newSize; slot++)
11831 {
11832 mNetworkAdapters[slot].createObject();
11833 mNetworkAdapters[slot]->init(this, (ULONG)slot);
11834 mNetworkAdapters[slot]->i_applyDefaults(SUCCEEDED(hrc) ? osType : NULL);
11835 }
11836 }
11837 for (size_t slot = 0; slot < newSize; slot++)
11838 {
11839 /* look if this adapter has a peer device */
11840 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11841 if (!peer)
11842 {
11843 /* no peer means the adapter is a newly created one;
11844 * create a peer owning data this data share it with */
11845 peer.createObject();
11846 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11847 }
11848 mPeer->mNetworkAdapters[slot] = peer;
11849 }
11850 /* uninit any no longer needed network adapters */
11851 for (size_t slot = newSize; slot < oldSize; ++slot)
11852 mNetworkAdapters[slot]->uninit();
11853 for (size_t slot = newSize; slot < oldPeerSize; ++slot)
11854 {
11855 if (mPeer->mNetworkAdapters[slot])
11856 mPeer->mNetworkAdapters[slot]->uninit();
11857 }
11858 /* Keep the original network adapter count until this point, so that
11859 * discarding a chipset type change will not lose settings. */
11860 mNetworkAdapters.resize(newSize);
11861 mPeer->mNetworkAdapters.resize(newSize);
11862 }
11863 else
11864 {
11865 /* we have no peer (our parent is the newly created machine);
11866 * just commit changes to the network adapters */
11867 commitNetworkAdapters = true;
11868 }
11869 if (commitNetworkAdapters)
11870 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11871 mNetworkAdapters[slot]->i_commit();
11872
11873 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11874 mSerialPorts[slot]->i_commit();
11875 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11876 mParallelPorts[slot]->i_commit();
11877
11878 bool commitStorageControllers = false;
11879
11880 if (mStorageControllers.isBackedUp())
11881 {
11882 mStorageControllers.commit();
11883
11884 if (mPeer)
11885 {
11886 /* Commit all changes to new controllers (this will reshare data with
11887 * peers for those who have peers) */
11888 StorageControllerList *newList = new StorageControllerList();
11889 for (StorageControllerList::const_iterator
11890 it = mStorageControllers->begin();
11891 it != mStorageControllers->end();
11892 ++it)
11893 {
11894 (*it)->i_commit();
11895
11896 /* look if this controller has a peer device */
11897 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11898 if (!peer)
11899 {
11900 /* no peer means the device is a newly created one;
11901 * create a peer owning data this device share it with */
11902 peer.createObject();
11903 peer->init(mPeer, *it, true /* aReshare */);
11904 }
11905 else
11906 {
11907 /* remove peer from the old list */
11908 mPeer->mStorageControllers->remove(peer);
11909 }
11910 /* and add it to the new list */
11911 newList->push_back(peer);
11912 }
11913
11914 /* uninit old peer's controllers that are left */
11915 for (StorageControllerList::const_iterator
11916 it = mPeer->mStorageControllers->begin();
11917 it != mPeer->mStorageControllers->end();
11918 ++it)
11919 {
11920 (*it)->uninit();
11921 }
11922
11923 /* attach new list of controllers to our peer */
11924 mPeer->mStorageControllers.attach(newList);
11925 }
11926 else
11927 {
11928 /* we have no peer (our parent is the newly created machine);
11929 * just commit changes to devices */
11930 commitStorageControllers = true;
11931 }
11932 }
11933 else
11934 {
11935 /* the list of controllers itself is not changed,
11936 * just commit changes to controllers themselves */
11937 commitStorageControllers = true;
11938 }
11939
11940 if (commitStorageControllers)
11941 {
11942 for (StorageControllerList::const_iterator
11943 it = mStorageControllers->begin();
11944 it != mStorageControllers->end();
11945 ++it)
11946 {
11947 (*it)->i_commit();
11948 }
11949 }
11950
11951 bool commitUSBControllers = false;
11952
11953 if (mUSBControllers.isBackedUp())
11954 {
11955 mUSBControllers.commit();
11956
11957 if (mPeer)
11958 {
11959 /* Commit all changes to new controllers (this will reshare data with
11960 * peers for those who have peers) */
11961 USBControllerList *newList = new USBControllerList();
11962 for (USBControllerList::const_iterator
11963 it = mUSBControllers->begin();
11964 it != mUSBControllers->end();
11965 ++it)
11966 {
11967 (*it)->i_commit();
11968
11969 /* look if this controller has a peer device */
11970 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11971 if (!peer)
11972 {
11973 /* no peer means the device is a newly created one;
11974 * create a peer owning data this device share it with */
11975 peer.createObject();
11976 peer->init(mPeer, *it, true /* aReshare */);
11977 }
11978 else
11979 {
11980 /* remove peer from the old list */
11981 mPeer->mUSBControllers->remove(peer);
11982 }
11983 /* and add it to the new list */
11984 newList->push_back(peer);
11985 }
11986
11987 /* uninit old peer's controllers that are left */
11988 for (USBControllerList::const_iterator
11989 it = mPeer->mUSBControllers->begin();
11990 it != mPeer->mUSBControllers->end();
11991 ++it)
11992 {
11993 (*it)->uninit();
11994 }
11995
11996 /* attach new list of controllers to our peer */
11997 mPeer->mUSBControllers.attach(newList);
11998 }
11999 else
12000 {
12001 /* we have no peer (our parent is the newly created machine);
12002 * just commit changes to devices */
12003 commitUSBControllers = true;
12004 }
12005 }
12006 else
12007 {
12008 /* the list of controllers itself is not changed,
12009 * just commit changes to controllers themselves */
12010 commitUSBControllers = true;
12011 }
12012
12013 if (commitUSBControllers)
12014 {
12015 for (USBControllerList::const_iterator
12016 it = mUSBControllers->begin();
12017 it != mUSBControllers->end();
12018 ++it)
12019 {
12020 (*it)->i_commit();
12021 }
12022 }
12023
12024 if (i_isSessionMachine())
12025 {
12026 /* attach new data to the primary machine and reshare it */
12027 mPeer->mUserData.attach(mUserData);
12028 mPeer->mHWData.attach(mHWData);
12029 /* mmMediumAttachments is reshared by fixupMedia */
12030 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12031 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12032 }
12033}
12034
12035/**
12036 * Copies all the hardware data from the given machine.
12037 *
12038 * Currently, only called when the VM is being restored from a snapshot. In
12039 * particular, this implies that the VM is not running during this method's
12040 * call.
12041 *
12042 * @note This method must be called from under this object's lock.
12043 *
12044 * @note This method doesn't call #i_commit(), so all data remains backed up and
12045 * unsaved.
12046 */
12047void Machine::i_copyFrom(Machine *aThat)
12048{
12049 AssertReturnVoid(!i_isSnapshotMachine());
12050 AssertReturnVoid(aThat->i_isSnapshotMachine());
12051
12052 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12053
12054 mHWData.assignCopy(aThat->mHWData);
12055
12056 // create copies of all shared folders (mHWData after attaching a copy
12057 // contains just references to original objects)
12058 for (HWData::SharedFolderList::iterator
12059 it = mHWData->mSharedFolders.begin();
12060 it != mHWData->mSharedFolders.end();
12061 ++it)
12062 {
12063 ComObjPtr<SharedFolder> folder;
12064 folder.createObject();
12065 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12066 AssertComRC(hrc);
12067 *it = folder;
12068 }
12069
12070 mPlatform->i_copyFrom(aThat->mPlatform);
12071 i_platformPropertiesUpdate();
12072 mFirmwareSettings->i_copyFrom(aThat->mFirmwareSettings);
12073 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12074 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12075 mNvramStore->i_copyFrom(aThat->mNvramStore);
12076 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12077 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12078 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12079 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12080 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12081 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12082
12083 /* create private copies of all controllers */
12084 mStorageControllers.backup();
12085 mStorageControllers->clear();
12086 for (StorageControllerList::const_iterator
12087 it = aThat->mStorageControllers->begin();
12088 it != aThat->mStorageControllers->end();
12089 ++it)
12090 {
12091 ComObjPtr<StorageController> ctrl;
12092 ctrl.createObject();
12093 ctrl->initCopy(this, *it);
12094 mStorageControllers->push_back(ctrl);
12095 }
12096
12097 /* create private copies of all USB controllers */
12098 mUSBControllers.backup();
12099 mUSBControllers->clear();
12100 for (USBControllerList::const_iterator
12101 it = aThat->mUSBControllers->begin();
12102 it != aThat->mUSBControllers->end();
12103 ++it)
12104 {
12105 ComObjPtr<USBController> ctrl;
12106 ctrl.createObject();
12107 ctrl->initCopy(this, *it);
12108 mUSBControllers->push_back(ctrl);
12109 }
12110
12111 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12112 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12113 {
12114 if (mNetworkAdapters[slot].isNotNull())
12115 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12116 else
12117 {
12118 unconst(mNetworkAdapters[slot]).createObject();
12119 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12120 }
12121 }
12122 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12123 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12124 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12125 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12126}
12127
12128/**
12129 * Returns whether the given storage controller is hotplug capable.
12130 *
12131 * @returns true if the controller supports hotplugging
12132 * false otherwise.
12133 * @param enmCtrlType The controller type to check for.
12134 */
12135bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12136{
12137 BOOL aHotplugCapable = FALSE;
12138 HRESULT hrc = mPlatformProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12139 AssertComRC(hrc);
12140
12141 return RT_BOOL(aHotplugCapable);
12142}
12143
12144#ifdef VBOX_WITH_RESOURCE_USAGE_API
12145
12146void Machine::i_getDiskList(MediaList &list)
12147{
12148 for (MediumAttachmentList::const_iterator
12149 it = mMediumAttachments->begin();
12150 it != mMediumAttachments->end();
12151 ++it)
12152 {
12153 MediumAttachment *pAttach = *it;
12154 /* just in case */
12155 AssertContinue(pAttach);
12156
12157 AutoCaller localAutoCallerA(pAttach);
12158 if (FAILED(localAutoCallerA.hrc())) continue;
12159
12160 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12161
12162 if (pAttach->i_getType() == DeviceType_HardDisk)
12163 list.push_back(pAttach->i_getMedium());
12164 }
12165}
12166
12167void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12168{
12169 AssertReturnVoid(isWriteLockOnCurrentThread());
12170 AssertPtrReturnVoid(aCollector);
12171
12172 pm::CollectorHAL *hal = aCollector->getHAL();
12173 /* Create sub metrics */
12174 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12175 "Percentage of processor time spent in user mode by the VM process.");
12176 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12177 "Percentage of processor time spent in kernel mode by the VM process.");
12178 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12179 "Size of resident portion of VM process in memory.");
12180 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12181 "Actual size of all VM disks combined.");
12182 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12183 "Network receive rate.");
12184 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12185 "Network transmit rate.");
12186 /* Create and register base metrics */
12187 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12188 cpuLoadUser, cpuLoadKernel);
12189 aCollector->registerBaseMetric(cpuLoad);
12190 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12191 ramUsageUsed);
12192 aCollector->registerBaseMetric(ramUsage);
12193 MediaList disks;
12194 i_getDiskList(disks);
12195 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12196 diskUsageUsed);
12197 aCollector->registerBaseMetric(diskUsage);
12198
12199 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12200 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12201 new pm::AggregateAvg()));
12202 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12203 new pm::AggregateMin()));
12204 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12205 new pm::AggregateMax()));
12206 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12207 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12208 new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12210 new pm::AggregateMin()));
12211 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12212 new pm::AggregateMax()));
12213
12214 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12215 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12216 new pm::AggregateAvg()));
12217 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12218 new pm::AggregateMin()));
12219 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12220 new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12223 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12224 new pm::AggregateAvg()));
12225 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12226 new pm::AggregateMin()));
12227 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12228 new pm::AggregateMax()));
12229
12230
12231 /* Guest metrics collector */
12232 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12233 aCollector->registerGuest(mCollectorGuest);
12234 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12235
12236 /* Create sub metrics */
12237 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12238 "Percentage of processor time spent in user mode as seen by the guest.");
12239 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12240 "Percentage of processor time spent in kernel mode as seen by the guest.");
12241 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12242 "Percentage of processor time spent idling as seen by the guest.");
12243
12244 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12245 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12246 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12247 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12248 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12249 pm::SubMetric *guestMemCache = new pm::SubMetric(
12250 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12251
12252 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12253 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12254
12255 /* Create and register base metrics */
12256 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12257 machineNetRx, machineNetTx);
12258 aCollector->registerBaseMetric(machineNetRate);
12259
12260 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12261 guestLoadUser, guestLoadKernel, guestLoadIdle);
12262 aCollector->registerBaseMetric(guestCpuLoad);
12263
12264 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12265 guestMemTotal, guestMemFree,
12266 guestMemBalloon, guestMemShared,
12267 guestMemCache, guestPagedTotal);
12268 aCollector->registerBaseMetric(guestCpuMem);
12269
12270 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12271 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12274
12275 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12276 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12281 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12284
12285 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12286 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12291 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12296 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12301 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12306 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12324}
12325
12326void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12327{
12328 AssertReturnVoid(isWriteLockOnCurrentThread());
12329
12330 if (aCollector)
12331 {
12332 aCollector->unregisterMetricsFor(aMachine);
12333 aCollector->unregisterBaseMetricsFor(aMachine);
12334 }
12335}
12336
12337#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12338
12339/**
12340 * Updates the machine's platform properties based on the current platform architecture.
12341 *
12342 * @note Called internally when committing, rolling back or loading settings.
12343 */
12344void Machine::i_platformPropertiesUpdate()
12345{
12346 if (mPlatform)
12347 {
12348 /* Update architecture for platform properties. */
12349 PlatformArchitecture_T platformArchitecture;
12350 HRESULT hrc = mPlatform->getArchitecture(&platformArchitecture);
12351 ComAssertComRC(hrc);
12352 hrc = mPlatformProperties->i_setArchitecture(platformArchitecture);
12353 ComAssertComRC(hrc);
12354 }
12355}
12356
12357
12358////////////////////////////////////////////////////////////////////////////////
12359
12360DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12361
12362HRESULT SessionMachine::FinalConstruct()
12363{
12364 LogFlowThisFunc(("\n"));
12365
12366 mClientToken = NULL;
12367
12368 return BaseFinalConstruct();
12369}
12370
12371void SessionMachine::FinalRelease()
12372{
12373 LogFlowThisFunc(("\n"));
12374
12375 Assert(!mClientToken);
12376 /* paranoia, should not hang around any more */
12377 if (mClientToken)
12378 {
12379 delete mClientToken;
12380 mClientToken = NULL;
12381 }
12382
12383 uninit(Uninit::Unexpected);
12384
12385 BaseFinalRelease();
12386}
12387
12388/**
12389 * @note Must be called only by Machine::LockMachine() from its own write lock.
12390 */
12391HRESULT SessionMachine::init(Machine *aMachine)
12392{
12393 LogFlowThisFuncEnter();
12394 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12395
12396 AssertReturn(aMachine, E_INVALIDARG);
12397
12398 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12399
12400 /* Enclose the state transition NotReady->InInit->Ready */
12401 AutoInitSpan autoInitSpan(this);
12402 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12403
12404 HRESULT hrc = S_OK;
12405
12406 RT_ZERO(mAuthLibCtx);
12407
12408 /* create the machine client token */
12409 try
12410 {
12411 mClientToken = new ClientToken(aMachine, this);
12412 if (!mClientToken->isReady())
12413 {
12414 delete mClientToken;
12415 mClientToken = NULL;
12416 hrc = E_FAIL;
12417 }
12418 }
12419 catch (std::bad_alloc &)
12420 {
12421 hrc = E_OUTOFMEMORY;
12422 }
12423 if (FAILED(hrc))
12424 return hrc;
12425
12426 /* memorize the peer Machine */
12427 unconst(mPeer) = aMachine;
12428 /* share the parent pointer */
12429 unconst(mParent) = aMachine->mParent;
12430
12431 /* take the pointers to data to share */
12432 mData.share(aMachine->mData);
12433 mSSData.share(aMachine->mSSData);
12434
12435 mUserData.share(aMachine->mUserData);
12436 mHWData.share(aMachine->mHWData);
12437 mMediumAttachments.share(aMachine->mMediumAttachments);
12438
12439 mStorageControllers.allocate();
12440 for (StorageControllerList::const_iterator
12441 it = aMachine->mStorageControllers->begin();
12442 it != aMachine->mStorageControllers->end();
12443 ++it)
12444 {
12445 ComObjPtr<StorageController> ctl;
12446 ctl.createObject();
12447 ctl->init(this, *it);
12448 mStorageControllers->push_back(ctl);
12449 }
12450
12451 mUSBControllers.allocate();
12452 for (USBControllerList::const_iterator
12453 it = aMachine->mUSBControllers->begin();
12454 it != aMachine->mUSBControllers->end();
12455 ++it)
12456 {
12457 ComObjPtr<USBController> ctl;
12458 ctl.createObject();
12459 ctl->init(this, *it);
12460 mUSBControllers->push_back(ctl);
12461 }
12462
12463 unconst(mPlatformProperties).createObject();
12464 mPlatformProperties->init(mParent);
12465 unconst(mPlatform).createObject();
12466 mPlatform->init(this, aMachine->mPlatform);
12467
12468 i_platformPropertiesUpdate();
12469
12470 unconst(mFirmwareSettings).createObject();
12471 mFirmwareSettings->init(this, aMachine->mFirmwareSettings);
12472
12473 unconst(mRecordingSettings).createObject();
12474 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12475
12476 unconst(mTrustedPlatformModule).createObject();
12477 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12478
12479 unconst(mNvramStore).createObject();
12480 mNvramStore->init(this, aMachine->mNvramStore);
12481
12482 /* create another GraphicsAdapter object that will be mutable */
12483 unconst(mGraphicsAdapter).createObject();
12484 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12485 /* create another VRDEServer object that will be mutable */
12486 unconst(mVRDEServer).createObject();
12487 mVRDEServer->init(this, aMachine->mVRDEServer);
12488 /* create another audio settings object that will be mutable */
12489 unconst(mAudioSettings).createObject();
12490 mAudioSettings->init(this, aMachine->mAudioSettings);
12491 /* create a list of serial ports that will be mutable */
12492 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12493 {
12494 unconst(mSerialPorts[slot]).createObject();
12495 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12496 }
12497 /* create a list of parallel ports that will be mutable */
12498 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12499 {
12500 unconst(mParallelPorts[slot]).createObject();
12501 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12502 }
12503
12504 /* create another USB device filters object that will be mutable */
12505 unconst(mUSBDeviceFilters).createObject();
12506 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12507
12508 /* create a list of network adapters that will be mutable */
12509 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12510 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12511 {
12512 unconst(mNetworkAdapters[slot]).createObject();
12513 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12514 }
12515
12516 /* create another bandwidth control object that will be mutable */
12517 unconst(mBandwidthControl).createObject();
12518 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12519
12520 unconst(mGuestDebugControl).createObject();
12521 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
12522
12523 /* default is to delete saved state on Saved -> PoweredOff transition */
12524 mRemoveSavedState = true;
12525
12526 /* Confirm a successful initialization when it's the case */
12527 autoInitSpan.setSucceeded();
12528
12529 miNATNetworksStarted = 0;
12530
12531 LogFlowThisFuncLeave();
12532 return hrc;
12533}
12534
12535/**
12536 * Uninitializes this session object. If the reason is other than
12537 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12538 * or the client watcher code.
12539 *
12540 * @param aReason uninitialization reason
12541 *
12542 * @note Locks mParent + this object for writing.
12543 */
12544void SessionMachine::uninit(Uninit::Reason aReason)
12545{
12546 LogFlowThisFuncEnter();
12547 LogFlowThisFunc(("reason=%d\n", aReason));
12548
12549 /*
12550 * Strongly reference ourselves to prevent this object deletion after
12551 * mData->mSession.mMachine.setNull() below (which can release the last
12552 * reference and call the destructor). Important: this must be done before
12553 * accessing any members (and before AutoUninitSpan that does it as well).
12554 * This self reference will be released as the very last step on return.
12555 */
12556 ComObjPtr<SessionMachine> selfRef;
12557 if (aReason != Uninit::Unexpected)
12558 selfRef = this;
12559
12560 /* Enclose the state transition Ready->InUninit->NotReady */
12561 AutoUninitSpan autoUninitSpan(this);
12562 if (autoUninitSpan.uninitDone())
12563 {
12564 LogFlowThisFunc(("Already uninitialized\n"));
12565 LogFlowThisFuncLeave();
12566 return;
12567 }
12568
12569 if (autoUninitSpan.initFailed())
12570 {
12571 /* We've been called by init() because it's failed. It's not really
12572 * necessary (nor it's safe) to perform the regular uninit sequence
12573 * below, the following is enough.
12574 */
12575 LogFlowThisFunc(("Initialization failed.\n"));
12576 /* destroy the machine client token */
12577 if (mClientToken)
12578 {
12579 delete mClientToken;
12580 mClientToken = NULL;
12581 }
12582 uninitDataAndChildObjects();
12583 mData.free();
12584 unconst(mParent) = NULL;
12585 unconst(mPeer) = NULL;
12586 LogFlowThisFuncLeave();
12587 return;
12588 }
12589
12590 MachineState_T lastState;
12591 {
12592 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12593 lastState = mData->mMachineState;
12594 }
12595 NOREF(lastState);
12596
12597#ifdef VBOX_WITH_USB
12598 // release all captured USB devices, but do this before requesting the locks below
12599 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12600 {
12601 /* Console::captureUSBDevices() is called in the VM process only after
12602 * setting the machine state to Starting or Restoring.
12603 * Console::detachAllUSBDevices() will be called upon successful
12604 * termination. So, we need to release USB devices only if there was
12605 * an abnormal termination of a running VM.
12606 *
12607 * This is identical to SessionMachine::DetachAllUSBDevices except
12608 * for the aAbnormal argument. */
12609 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12610 AssertComRC(hrc);
12611 NOREF(hrc);
12612
12613 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12614 if (service)
12615 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12616 }
12617#endif /* VBOX_WITH_USB */
12618
12619 // we need to lock this object in uninit() because the lock is shared
12620 // with mPeer (as well as data we modify below). mParent lock is needed
12621 // by several calls to it.
12622 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12623
12624#ifdef VBOX_WITH_RESOURCE_USAGE_API
12625 /*
12626 * It is safe to call Machine::i_unregisterMetrics() here because
12627 * PerformanceCollector::samplerCallback no longer accesses guest methods
12628 * holding the lock.
12629 */
12630 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12631 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12632 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12633 if (mCollectorGuest)
12634 {
12635 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12636 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12637 mCollectorGuest = NULL;
12638 }
12639#endif
12640
12641 if (aReason == Uninit::Abnormal)
12642 {
12643 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12644
12645 /*
12646 * Move the VM to the 'Aborted' machine state unless we are restoring a
12647 * VM that was in the 'Saved' machine state. In that case, if the VM
12648 * fails before reaching either the 'Restoring' machine state or the
12649 * 'Running' machine state then we set the machine state to
12650 * 'AbortedSaved' in order to preserve the saved state file so that the
12651 * VM can be restored in the future.
12652 */
12653 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12654 i_setMachineState(MachineState_AbortedSaved);
12655 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12656 i_setMachineState(MachineState_Aborted);
12657 }
12658
12659 // any machine settings modified?
12660 if (mData->flModifications)
12661 {
12662 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12663 i_rollback(false /* aNotify */);
12664 }
12665
12666 mData->mSession.mPID = NIL_RTPROCESS;
12667
12668 if (aReason == Uninit::Unexpected)
12669 {
12670 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12671 * client watcher thread to update the set of machines that have open
12672 * sessions. */
12673 mParent->i_updateClientWatcher();
12674 }
12675
12676 /* uninitialize all remote controls */
12677 if (mData->mSession.mRemoteControls.size())
12678 {
12679 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12680 mData->mSession.mRemoteControls.size()));
12681
12682 /* Always restart a the beginning, since the iterator is invalidated
12683 * by using erase(). */
12684 for (Data::Session::RemoteControlList::iterator
12685 it = mData->mSession.mRemoteControls.begin();
12686 it != mData->mSession.mRemoteControls.end();
12687 it = mData->mSession.mRemoteControls.begin())
12688 {
12689 ComPtr<IInternalSessionControl> pControl = *it;
12690 mData->mSession.mRemoteControls.erase(it);
12691 multilock.release();
12692 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12693 HRESULT hrc = pControl->Uninitialize();
12694 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
12695 if (FAILED(hrc))
12696 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12697 multilock.acquire();
12698 }
12699 mData->mSession.mRemoteControls.clear();
12700 }
12701
12702 /* Remove all references to the NAT network service. The service will stop
12703 * if all references (also from other VMs) are removed. */
12704 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12705 {
12706 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12707 {
12708 BOOL enabled;
12709 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12710 if ( FAILED(hrc)
12711 || !enabled)
12712 continue;
12713
12714 NetworkAttachmentType_T type;
12715 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12716 if ( SUCCEEDED(hrc)
12717 && type == NetworkAttachmentType_NATNetwork)
12718 {
12719 Bstr name;
12720 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12721 if (SUCCEEDED(hrc))
12722 {
12723 multilock.release();
12724 Utf8Str strName(name);
12725 LogRel(("VM '%s' stops using NAT network '%s'\n",
12726 mUserData->s.strName.c_str(), strName.c_str()));
12727 mParent->i_natNetworkRefDec(strName);
12728 multilock.acquire();
12729 }
12730 }
12731 }
12732 }
12733
12734 /*
12735 * An expected uninitialization can come only from #i_checkForDeath().
12736 * Otherwise it means that something's gone really wrong (for example,
12737 * the Session implementation has released the VirtualBox reference
12738 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12739 * etc). However, it's also possible, that the client releases the IPC
12740 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12741 * but the VirtualBox release event comes first to the server process.
12742 * This case is practically possible, so we should not assert on an
12743 * unexpected uninit, just log a warning.
12744 */
12745
12746 if (aReason == Uninit::Unexpected)
12747 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12748
12749 if (aReason != Uninit::Normal)
12750 {
12751 mData->mSession.mDirectControl.setNull();
12752 }
12753 else
12754 {
12755 /* this must be null here (see #OnSessionEnd()) */
12756 Assert(mData->mSession.mDirectControl.isNull());
12757 Assert(mData->mSession.mState == SessionState_Unlocking);
12758 Assert(!mData->mSession.mProgress.isNull());
12759 }
12760 if (mData->mSession.mProgress)
12761 {
12762 if (aReason == Uninit::Normal)
12763 mData->mSession.mProgress->i_notifyComplete(S_OK);
12764 else
12765 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12766 COM_IIDOF(ISession),
12767 getComponentName(),
12768 tr("The VM session was aborted"));
12769 mData->mSession.mProgress.setNull();
12770 }
12771
12772 if (mConsoleTaskData.mProgress)
12773 {
12774 Assert(aReason == Uninit::Abnormal);
12775 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12776 COM_IIDOF(ISession),
12777 getComponentName(),
12778 tr("The VM session was aborted"));
12779 mConsoleTaskData.mProgress.setNull();
12780 }
12781
12782 /* remove the association between the peer machine and this session machine */
12783 Assert( (SessionMachine*)mData->mSession.mMachine == this
12784 || aReason == Uninit::Unexpected);
12785
12786 /* reset the rest of session data */
12787 mData->mSession.mLockType = LockType_Null;
12788 mData->mSession.mMachine.setNull();
12789 mData->mSession.mState = SessionState_Unlocked;
12790 mData->mSession.mName.setNull();
12791
12792 /* destroy the machine client token before leaving the exclusive lock */
12793 if (mClientToken)
12794 {
12795 delete mClientToken;
12796 mClientToken = NULL;
12797 }
12798
12799 /* fire an event */
12800 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12801
12802 uninitDataAndChildObjects();
12803
12804 /* free the essential data structure last */
12805 mData.free();
12806
12807 /* release the exclusive lock before setting the below two to NULL */
12808 multilock.release();
12809
12810 unconst(mParent) = NULL;
12811 unconst(mPeer) = NULL;
12812
12813 AuthLibUnload(&mAuthLibCtx);
12814
12815 LogFlowThisFuncLeave();
12816}
12817
12818// util::Lockable interface
12819////////////////////////////////////////////////////////////////////////////////
12820
12821/**
12822 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12823 * with the primary Machine instance (mPeer).
12824 */
12825RWLockHandle *SessionMachine::lockHandle() const
12826{
12827 AssertReturn(mPeer != NULL, NULL);
12828 return mPeer->lockHandle();
12829}
12830
12831// IInternalMachineControl methods
12832////////////////////////////////////////////////////////////////////////////////
12833
12834/**
12835 * Passes collected guest statistics to performance collector object
12836 */
12837HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12838 ULONG aCpuKernel, ULONG aCpuIdle,
12839 ULONG aMemTotal, ULONG aMemFree,
12840 ULONG aMemBalloon, ULONG aMemShared,
12841 ULONG aMemCache, ULONG aPageTotal,
12842 ULONG aAllocVMM, ULONG aFreeVMM,
12843 ULONG aBalloonedVMM, ULONG aSharedVMM,
12844 ULONG aVmNetRx, ULONG aVmNetTx)
12845{
12846#ifdef VBOX_WITH_RESOURCE_USAGE_API
12847 if (mCollectorGuest)
12848 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12849 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12850 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12851 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12852
12853 return S_OK;
12854#else
12855 NOREF(aValidStats);
12856 NOREF(aCpuUser);
12857 NOREF(aCpuKernel);
12858 NOREF(aCpuIdle);
12859 NOREF(aMemTotal);
12860 NOREF(aMemFree);
12861 NOREF(aMemBalloon);
12862 NOREF(aMemShared);
12863 NOREF(aMemCache);
12864 NOREF(aPageTotal);
12865 NOREF(aAllocVMM);
12866 NOREF(aFreeVMM);
12867 NOREF(aBalloonedVMM);
12868 NOREF(aSharedVMM);
12869 NOREF(aVmNetRx);
12870 NOREF(aVmNetTx);
12871 return E_NOTIMPL;
12872#endif
12873}
12874
12875////////////////////////////////////////////////////////////////////////////////
12876//
12877// SessionMachine task records
12878//
12879////////////////////////////////////////////////////////////////////////////////
12880
12881/**
12882 * Task record for saving the machine state.
12883 */
12884class SessionMachine::SaveStateTask
12885 : public Machine::Task
12886{
12887public:
12888 SaveStateTask(SessionMachine *m,
12889 Progress *p,
12890 const Utf8Str &t,
12891 Reason_T enmReason,
12892 const Utf8Str &strStateFilePath)
12893 : Task(m, p, t),
12894 m_enmReason(enmReason),
12895 m_strStateFilePath(strStateFilePath)
12896 {}
12897
12898private:
12899 void handler()
12900 {
12901 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12902 }
12903
12904 Reason_T m_enmReason;
12905 Utf8Str m_strStateFilePath;
12906
12907 friend class SessionMachine;
12908};
12909
12910/**
12911 * Task thread implementation for SessionMachine::SaveState(), called from
12912 * SessionMachine::taskHandler().
12913 *
12914 * @note Locks this object for writing.
12915 *
12916 * @param task
12917 */
12918void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12919{
12920 LogFlowThisFuncEnter();
12921
12922 AutoCaller autoCaller(this);
12923 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12924 if (FAILED(autoCaller.hrc()))
12925 {
12926 /* we might have been uninitialized because the session was accidentally
12927 * closed by the client, so don't assert */
12928 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
12929 task.m_pProgress->i_notifyComplete(hrc);
12930 LogFlowThisFuncLeave();
12931 return;
12932 }
12933
12934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12935
12936 HRESULT hrc = S_OK;
12937
12938 try
12939 {
12940 ComPtr<IInternalSessionControl> directControl;
12941 if (mData->mSession.mLockType == LockType_VM)
12942 directControl = mData->mSession.mDirectControl;
12943 if (directControl.isNull())
12944 throw setError(VBOX_E_INVALID_VM_STATE,
12945 tr("Trying to save state without a running VM"));
12946 alock.release();
12947 BOOL fSuspendedBySave;
12948 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12949 Assert(!fSuspendedBySave);
12950 alock.acquire();
12951
12952 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
12953 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
12954 throw E_FAIL);
12955
12956 if (SUCCEEDED(hrc))
12957 {
12958 mSSData->strStateFilePath = task.m_strStateFilePath;
12959
12960 /* save all VM settings */
12961 hrc = i_saveSettings(NULL, alock);
12962 // no need to check whether VirtualBox.xml needs saving also since
12963 // we can't have a name change pending at this point
12964 }
12965 else
12966 {
12967 // On failure, set the state to the state we had at the beginning.
12968 i_setMachineState(task.m_machineStateBackup);
12969 i_updateMachineStateOnClient();
12970
12971 // Delete the saved state file (might have been already created).
12972 // No need to check whether this is shared with a snapshot here
12973 // because we certainly created a fresh saved state file here.
12974 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
12975 }
12976 }
12977 catch (HRESULT hrcXcpt)
12978 {
12979 hrc = hrcXcpt;
12980 }
12981
12982 task.m_pProgress->i_notifyComplete(hrc);
12983
12984 LogFlowThisFuncLeave();
12985}
12986
12987/**
12988 * @note Locks this object for writing.
12989 */
12990HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12991{
12992 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12993}
12994
12995HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12996{
12997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12998
12999 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13000 if (FAILED(hrc)) return hrc;
13001
13002 if ( mData->mMachineState != MachineState_Running
13003 && mData->mMachineState != MachineState_Paused
13004 )
13005 return setError(VBOX_E_INVALID_VM_STATE,
13006 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13007 Global::stringifyMachineState(mData->mMachineState));
13008
13009 ComObjPtr<Progress> pProgress;
13010 pProgress.createObject();
13011 hrc = pProgress->init(i_getVirtualBox(),
13012 static_cast<IMachine *>(this) /* aInitiator */,
13013 tr("Saving the execution state of the virtual machine"),
13014 FALSE /* aCancelable */);
13015 if (FAILED(hrc))
13016 return hrc;
13017
13018 Utf8Str strStateFilePath;
13019 i_composeSavedStateFilename(strStateFilePath);
13020
13021 /* create and start the task on a separate thread (note that it will not
13022 * start working until we release alock) */
13023 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13024 hrc = pTask->createThread();
13025 if (FAILED(hrc))
13026 return hrc;
13027
13028 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13029 i_setMachineState(MachineState_Saving);
13030 i_updateMachineStateOnClient();
13031
13032 pProgress.queryInterfaceTo(aProgress.asOutParam());
13033
13034 return S_OK;
13035}
13036
13037/**
13038 * @note Locks this object for writing.
13039 */
13040HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13041{
13042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13043
13044 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13045 if (FAILED(hrc)) return hrc;
13046
13047 if ( mData->mMachineState != MachineState_PoweredOff
13048 && mData->mMachineState != MachineState_Teleported
13049 && mData->mMachineState != MachineState_Aborted
13050 )
13051 return setError(VBOX_E_INVALID_VM_STATE,
13052 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13053 Global::stringifyMachineState(mData->mMachineState));
13054
13055 com::Utf8Str stateFilePathFull;
13056 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13057 if (RT_FAILURE(vrc))
13058 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13059 tr("Invalid saved state file path '%s' (%Rrc)"),
13060 aSavedStateFile.c_str(),
13061 vrc);
13062
13063 mSSData->strStateFilePath = stateFilePathFull;
13064
13065 /* The below i_setMachineState() will detect the state transition and will
13066 * update the settings file */
13067
13068 return i_setMachineState(MachineState_Saved);
13069}
13070
13071/**
13072 * @note Locks this object for writing.
13073 */
13074HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13075{
13076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13077
13078 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13079 if (FAILED(hrc)) return hrc;
13080
13081 if ( mData->mMachineState != MachineState_Saved
13082 && mData->mMachineState != MachineState_AbortedSaved)
13083 return setError(VBOX_E_INVALID_VM_STATE,
13084 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13085 Global::stringifyMachineState(mData->mMachineState));
13086
13087 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13088
13089 /*
13090 * Saved -> PoweredOff transition will be detected in the SessionMachine
13091 * and properly handled.
13092 */
13093 hrc = i_setMachineState(MachineState_PoweredOff);
13094 return hrc;
13095}
13096
13097
13098/**
13099 * @note Locks the same as #i_setMachineState() does.
13100 */
13101HRESULT SessionMachine::updateState(MachineState_T aState)
13102{
13103 return i_setMachineState(aState);
13104}
13105
13106/**
13107 * @note Locks this object for writing.
13108 */
13109HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13110{
13111 IProgress *pProgress(aProgress);
13112
13113 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13114
13115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13116
13117 if (mData->mSession.mState != SessionState_Locked)
13118 return VBOX_E_INVALID_OBJECT_STATE;
13119
13120 if (!mData->mSession.mProgress.isNull())
13121 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13122
13123 /* If we didn't reference the NAT network service yet, add a reference to
13124 * force a start */
13125 if (miNATNetworksStarted < 1)
13126 {
13127 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13128 {
13129 BOOL enabled;
13130 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13131 if ( FAILED(hrc)
13132 || !enabled)
13133 continue;
13134
13135 NetworkAttachmentType_T type;
13136 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13137 if ( SUCCEEDED(hrc)
13138 && type == NetworkAttachmentType_NATNetwork)
13139 {
13140 Bstr name;
13141 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13142 if (SUCCEEDED(hrc))
13143 {
13144 Utf8Str strName(name);
13145 LogRel(("VM '%s' starts using NAT network '%s'\n",
13146 mUserData->s.strName.c_str(), strName.c_str()));
13147 mPeer->lockHandle()->unlockWrite();
13148 mParent->i_natNetworkRefInc(strName);
13149#ifdef RT_LOCK_STRICT
13150 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13151#else
13152 mPeer->lockHandle()->lockWrite();
13153#endif
13154 }
13155 }
13156 }
13157 miNATNetworksStarted++;
13158 }
13159
13160 LogFlowThisFunc(("returns S_OK.\n"));
13161 return S_OK;
13162}
13163
13164/**
13165 * @note Locks this object for writing.
13166 */
13167HRESULT SessionMachine::endPowerUp(LONG aResult)
13168{
13169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13170
13171 if (mData->mSession.mState != SessionState_Locked)
13172 return VBOX_E_INVALID_OBJECT_STATE;
13173
13174 /* Finalize the LaunchVMProcess progress object. */
13175 if (mData->mSession.mProgress)
13176 {
13177 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13178 mData->mSession.mProgress.setNull();
13179 }
13180
13181 if (SUCCEEDED((HRESULT)aResult))
13182 {
13183#ifdef VBOX_WITH_RESOURCE_USAGE_API
13184 /* The VM has been powered up successfully, so it makes sense
13185 * now to offer the performance metrics for a running machine
13186 * object. Doing it earlier wouldn't be safe. */
13187 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13188 mData->mSession.mPID);
13189#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13190 }
13191
13192 return S_OK;
13193}
13194
13195/**
13196 * @note Locks this object for writing.
13197 */
13198HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13199{
13200 LogFlowThisFuncEnter();
13201
13202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13203
13204 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13205 E_FAIL);
13206
13207 /* create a progress object to track operation completion */
13208 ComObjPtr<Progress> pProgress;
13209 pProgress.createObject();
13210 pProgress->init(i_getVirtualBox(),
13211 static_cast<IMachine *>(this) /* aInitiator */,
13212 tr("Stopping the virtual machine"),
13213 FALSE /* aCancelable */);
13214
13215 /* fill in the console task data */
13216 mConsoleTaskData.mLastState = mData->mMachineState;
13217 mConsoleTaskData.mProgress = pProgress;
13218
13219 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13220 i_setMachineState(MachineState_Stopping);
13221
13222 pProgress.queryInterfaceTo(aProgress.asOutParam());
13223
13224 return S_OK;
13225}
13226
13227/**
13228 * @note Locks this object for writing.
13229 */
13230HRESULT SessionMachine::endPoweringDown(LONG aResult,
13231 const com::Utf8Str &aErrMsg)
13232{
13233 HRESULT const hrcResult = (HRESULT)aResult;
13234 LogFlowThisFuncEnter();
13235
13236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13237
13238 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13239 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13240 && mConsoleTaskData.mLastState != MachineState_Null,
13241 E_FAIL);
13242
13243 /*
13244 * On failure, set the state to the state we had when BeginPoweringDown()
13245 * was called (this is expected by Console::PowerDown() and the associated
13246 * task). On success the VM process already changed the state to
13247 * MachineState_PoweredOff, so no need to do anything.
13248 */
13249 if (FAILED(hrcResult))
13250 i_setMachineState(mConsoleTaskData.mLastState);
13251
13252 /* notify the progress object about operation completion */
13253 Assert(mConsoleTaskData.mProgress);
13254 if (SUCCEEDED(hrcResult))
13255 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13256 else
13257 {
13258 if (aErrMsg.length())
13259 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13260 COM_IIDOF(ISession),
13261 getComponentName(),
13262 aErrMsg.c_str());
13263 else
13264 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13265 }
13266
13267 /* clear out the temporary saved state data */
13268 mConsoleTaskData.mLastState = MachineState_Null;
13269 mConsoleTaskData.mProgress.setNull();
13270
13271 LogFlowThisFuncLeave();
13272 return S_OK;
13273}
13274
13275
13276/**
13277 * Goes through the USB filters of the given machine to see if the given
13278 * device matches any filter or not.
13279 *
13280 * @note Locks the same as USBController::hasMatchingFilter() does.
13281 */
13282HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13283 BOOL *aMatched,
13284 ULONG *aMaskedInterfaces)
13285{
13286 LogFlowThisFunc(("\n"));
13287
13288#ifdef VBOX_WITH_USB
13289 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13290#else
13291 NOREF(aDevice);
13292 NOREF(aMaskedInterfaces);
13293 *aMatched = FALSE;
13294#endif
13295
13296 return S_OK;
13297}
13298
13299/**
13300 * @note Locks the same as Host::captureUSBDevice() does.
13301 */
13302HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13303{
13304 LogFlowThisFunc(("\n"));
13305
13306#ifdef VBOX_WITH_USB
13307 /* if captureDeviceForVM() fails, it must have set extended error info */
13308 clearError();
13309 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13310 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13311 return hrc;
13312
13313 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13314 AssertReturn(service, E_FAIL);
13315 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13316#else
13317 RT_NOREF(aId, aCaptureFilename);
13318 return E_NOTIMPL;
13319#endif
13320}
13321
13322/**
13323 * @note Locks the same as Host::detachUSBDevice() does.
13324 */
13325HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13326 BOOL aDone)
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330#ifdef VBOX_WITH_USB
13331 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13332 AssertReturn(service, E_FAIL);
13333 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13334#else
13335 NOREF(aId);
13336 NOREF(aDone);
13337 return E_NOTIMPL;
13338#endif
13339}
13340
13341/**
13342 * Inserts all machine filters to the USB proxy service and then calls
13343 * Host::autoCaptureUSBDevices().
13344 *
13345 * Called by Console from the VM process upon VM startup.
13346 *
13347 * @note Locks what called methods lock.
13348 */
13349HRESULT SessionMachine::autoCaptureUSBDevices()
13350{
13351 LogFlowThisFunc(("\n"));
13352
13353#ifdef VBOX_WITH_USB
13354 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13355 AssertComRC(hrc);
13356 NOREF(hrc);
13357
13358 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13359 AssertReturn(service, E_FAIL);
13360 return service->autoCaptureDevicesForVM(this);
13361#else
13362 return S_OK;
13363#endif
13364}
13365
13366/**
13367 * Removes all machine filters from the USB proxy service and then calls
13368 * Host::detachAllUSBDevices().
13369 *
13370 * Called by Console from the VM process upon normal VM termination or by
13371 * SessionMachine::uninit() upon abnormal VM termination (from under the
13372 * Machine/SessionMachine lock).
13373 *
13374 * @note Locks what called methods lock.
13375 */
13376HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380#ifdef VBOX_WITH_USB
13381 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13382 AssertComRC(hrc);
13383 NOREF(hrc);
13384
13385 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13386 AssertReturn(service, E_FAIL);
13387 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13388#else
13389 NOREF(aDone);
13390 return S_OK;
13391#endif
13392}
13393
13394/**
13395 * @note Locks this object for writing.
13396 */
13397HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13398 ComPtr<IProgress> &aProgress)
13399{
13400 LogFlowThisFuncEnter();
13401
13402 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13403 /*
13404 * We don't assert below because it might happen that a non-direct session
13405 * informs us it is closed right after we've been uninitialized -- it's ok.
13406 */
13407
13408 /* get IInternalSessionControl interface */
13409 ComPtr<IInternalSessionControl> control(aSession);
13410
13411 ComAssertRet(!control.isNull(), E_INVALIDARG);
13412
13413 /* Creating a Progress object requires the VirtualBox lock, and
13414 * thus locking it here is required by the lock order rules. */
13415 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13416
13417 if (control == mData->mSession.mDirectControl)
13418 {
13419 /* The direct session is being normally closed by the client process
13420 * ----------------------------------------------------------------- */
13421
13422 /* go to the closing state (essential for all open*Session() calls and
13423 * for #i_checkForDeath()) */
13424 Assert(mData->mSession.mState == SessionState_Locked);
13425 mData->mSession.mState = SessionState_Unlocking;
13426
13427 /* set direct control to NULL to release the remote instance */
13428 mData->mSession.mDirectControl.setNull();
13429 LogFlowThisFunc(("Direct control is set to NULL\n"));
13430
13431 if (mData->mSession.mProgress)
13432 {
13433 /* finalize the progress, someone might wait if a frontend
13434 * closes the session before powering on the VM. */
13435 mData->mSession.mProgress->notifyComplete(E_FAIL,
13436 COM_IIDOF(ISession),
13437 getComponentName(),
13438 tr("The VM session was closed before any attempt to power it on"));
13439 mData->mSession.mProgress.setNull();
13440 }
13441
13442 /* Create the progress object the client will use to wait until
13443 * #i_checkForDeath() is called to uninitialize this session object after
13444 * it releases the IPC semaphore.
13445 * Note! Because we're "reusing" mProgress here, this must be a proxy
13446 * object just like for LaunchVMProcess. */
13447 Assert(mData->mSession.mProgress.isNull());
13448 ComObjPtr<ProgressProxy> progress;
13449 progress.createObject();
13450 ComPtr<IUnknown> pPeer(mPeer);
13451 progress->init(mParent, pPeer,
13452 Bstr(tr("Closing session")).raw(),
13453 FALSE /* aCancelable */);
13454 progress.queryInterfaceTo(aProgress.asOutParam());
13455 mData->mSession.mProgress = progress;
13456 }
13457 else
13458 {
13459 /* the remote session is being normally closed */
13460 bool found = false;
13461 for (Data::Session::RemoteControlList::iterator
13462 it = mData->mSession.mRemoteControls.begin();
13463 it != mData->mSession.mRemoteControls.end();
13464 ++it)
13465 {
13466 if (control == *it)
13467 {
13468 found = true;
13469 // This MUST be erase(it), not remove(*it) as the latter
13470 // triggers a very nasty use after free due to the place where
13471 // the value "lives".
13472 mData->mSession.mRemoteControls.erase(it);
13473 break;
13474 }
13475 }
13476 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13477 E_INVALIDARG);
13478 }
13479
13480 /* signal the client watcher thread, because the client is going away */
13481 mParent->i_updateClientWatcher();
13482
13483 LogFlowThisFuncLeave();
13484 return S_OK;
13485}
13486
13487HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13488 std::vector<com::Utf8Str> &aValues,
13489 std::vector<LONG64> &aTimestamps,
13490 std::vector<com::Utf8Str> &aFlags)
13491{
13492 LogFlowThisFunc(("\n"));
13493
13494#ifdef VBOX_WITH_GUEST_PROPS
13495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13496
13497 size_t cEntries = mHWData->mGuestProperties.size();
13498 aNames.resize(cEntries);
13499 aValues.resize(cEntries);
13500 aTimestamps.resize(cEntries);
13501 aFlags.resize(cEntries);
13502
13503 size_t i = 0;
13504 for (HWData::GuestPropertyMap::const_iterator
13505 it = mHWData->mGuestProperties.begin();
13506 it != mHWData->mGuestProperties.end();
13507 ++it, ++i)
13508 {
13509 aNames[i] = it->first;
13510 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
13511 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13512
13513 aValues[i] = it->second.strValue;
13514 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
13515 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13516
13517 aTimestamps[i] = it->second.mTimestamp;
13518
13519 /* If it is NULL, keep it NULL. */
13520 if (it->second.mFlags)
13521 {
13522 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13523 GuestPropWriteFlags(it->second.mFlags, szFlags);
13524 aFlags[i] = szFlags;
13525 }
13526 else
13527 aFlags[i] = "";
13528 }
13529 return S_OK;
13530#else
13531 ReturnComNotImplemented();
13532#endif
13533}
13534
13535HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13536 const com::Utf8Str &aValue,
13537 LONG64 aTimestamp,
13538 const com::Utf8Str &aFlags,
13539 BOOL fWasDeleted)
13540{
13541 LogFlowThisFunc(("\n"));
13542
13543#ifdef VBOX_WITH_GUEST_PROPS
13544 try
13545 {
13546 /*
13547 * Convert input up front.
13548 */
13549 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13550 if (aFlags.length())
13551 {
13552 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13553 AssertRCReturn(vrc, E_INVALIDARG);
13554 }
13555
13556 /*
13557 * Now grab the object lock, validate the state and do the update.
13558 */
13559
13560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13561
13562 if (!Global::IsOnline(mData->mMachineState))
13563 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13564
13565 i_setModified(IsModified_MachineData);
13566 mHWData.backup();
13567
13568 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13569 if (it != mHWData->mGuestProperties.end())
13570 {
13571 if (!fWasDeleted)
13572 {
13573 it->second.strValue = aValue;
13574 it->second.mTimestamp = aTimestamp;
13575 it->second.mFlags = fFlags;
13576 }
13577 else
13578 mHWData->mGuestProperties.erase(it);
13579
13580 mData->mGuestPropertiesModified = TRUE;
13581 }
13582 else if (!fWasDeleted)
13583 {
13584 HWData::GuestProperty prop;
13585 prop.strValue = aValue;
13586 prop.mTimestamp = aTimestamp;
13587 prop.mFlags = fFlags;
13588
13589 mHWData->mGuestProperties[aName] = prop;
13590 mData->mGuestPropertiesModified = TRUE;
13591 }
13592
13593 alock.release();
13594
13595 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13596 }
13597 catch (...)
13598 {
13599 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13600 }
13601 return S_OK;
13602#else
13603 ReturnComNotImplemented();
13604#endif
13605}
13606
13607
13608HRESULT SessionMachine::lockMedia()
13609{
13610 AutoMultiWriteLock2 alock(this->lockHandle(),
13611 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13612
13613 AssertReturn( mData->mMachineState == MachineState_Starting
13614 || mData->mMachineState == MachineState_Restoring
13615 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13616
13617 clearError();
13618 alock.release();
13619 return i_lockMedia();
13620}
13621
13622HRESULT SessionMachine::unlockMedia()
13623{
13624 HRESULT hrc = i_unlockMedia();
13625 return hrc;
13626}
13627
13628HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13629 ComPtr<IMediumAttachment> &aNewAttachment)
13630{
13631 // request the host lock first, since might be calling Host methods for getting host drives;
13632 // next, protect the media tree all the while we're in here, as well as our member variables
13633 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13634 this->lockHandle(),
13635 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13636
13637 IMediumAttachment *iAttach = aAttachment;
13638 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13639
13640 Utf8Str ctrlName;
13641 LONG lPort;
13642 LONG lDevice;
13643 bool fTempEject;
13644 {
13645 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13646
13647 /* Need to query the details first, as the IMediumAttachment reference
13648 * might be to the original settings, which we are going to change. */
13649 ctrlName = pAttach->i_getControllerName();
13650 lPort = pAttach->i_getPort();
13651 lDevice = pAttach->i_getDevice();
13652 fTempEject = pAttach->i_getTempEject();
13653 }
13654
13655 if (!fTempEject)
13656 {
13657 /* Remember previously mounted medium. The medium before taking the
13658 * backup is not necessarily the same thing. */
13659 ComObjPtr<Medium> oldmedium;
13660 oldmedium = pAttach->i_getMedium();
13661
13662 i_setModified(IsModified_Storage);
13663 mMediumAttachments.backup();
13664
13665 // The backup operation makes the pAttach reference point to the
13666 // old settings. Re-get the correct reference.
13667 pAttach = i_findAttachment(*mMediumAttachments.data(),
13668 ctrlName,
13669 lPort,
13670 lDevice);
13671
13672 {
13673 AutoCaller autoAttachCaller(this);
13674 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
13675
13676 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13677 if (!oldmedium.isNull())
13678 oldmedium->i_removeBackReference(mData->mUuid);
13679
13680 pAttach->i_updateMedium(NULL);
13681 pAttach->i_updateEjected();
13682 }
13683
13684 i_setModified(IsModified_Storage);
13685 }
13686 else
13687 {
13688 {
13689 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13690 pAttach->i_updateEjected();
13691 }
13692 }
13693
13694 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13695
13696 return S_OK;
13697}
13698
13699HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13700 com::Utf8Str &aResult)
13701{
13702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13703
13704 HRESULT hrc = S_OK;
13705
13706 if (!mAuthLibCtx.hAuthLibrary)
13707 {
13708 /* Load the external authentication library. */
13709 Bstr authLibrary;
13710 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13711
13712 Utf8Str filename = authLibrary;
13713
13714 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13715 if (RT_FAILURE(vrc))
13716 hrc = setErrorBoth(E_FAIL, vrc,
13717 tr("Could not load the external authentication library '%s' (%Rrc)"),
13718 filename.c_str(), vrc);
13719 }
13720
13721 /* The auth library might need the machine lock. */
13722 alock.release();
13723
13724 if (FAILED(hrc))
13725 return hrc;
13726
13727 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13728 {
13729 enum VRDEAuthParams
13730 {
13731 parmUuid = 1,
13732 parmGuestJudgement,
13733 parmUser,
13734 parmPassword,
13735 parmDomain,
13736 parmClientId
13737 };
13738
13739 AuthResult result = AuthResultAccessDenied;
13740
13741 Guid uuid(aAuthParams[parmUuid]);
13742 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13743 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13744
13745 result = AuthLibAuthenticate(&mAuthLibCtx,
13746 uuid.raw(), guestJudgement,
13747 aAuthParams[parmUser].c_str(),
13748 aAuthParams[parmPassword].c_str(),
13749 aAuthParams[parmDomain].c_str(),
13750 u32ClientId);
13751
13752 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13753 size_t cbPassword = aAuthParams[parmPassword].length();
13754 if (cbPassword)
13755 {
13756 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13757 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13758 }
13759
13760 if (result == AuthResultAccessGranted)
13761 aResult = "granted";
13762 else
13763 aResult = "denied";
13764
13765 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13766 aAuthParams[parmUser].c_str(), aResult.c_str()));
13767 }
13768 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13769 {
13770 enum VRDEAuthDisconnectParams
13771 {
13772 parmUuid = 1,
13773 parmClientId
13774 };
13775
13776 Guid uuid(aAuthParams[parmUuid]);
13777 uint32_t u32ClientId = 0;
13778 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13779 }
13780 else
13781 {
13782 hrc = E_INVALIDARG;
13783 }
13784
13785 return hrc;
13786}
13787
13788// public methods only for internal purposes
13789/////////////////////////////////////////////////////////////////////////////
13790
13791#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13792/**
13793 * Called from the client watcher thread to check for expected or unexpected
13794 * death of the client process that has a direct session to this machine.
13795 *
13796 * On Win32 and on OS/2, this method is called only when we've got the
13797 * mutex (i.e. the client has either died or terminated normally) so it always
13798 * returns @c true (the client is terminated, the session machine is
13799 * uninitialized).
13800 *
13801 * On other platforms, the method returns @c true if the client process has
13802 * terminated normally or abnormally and the session machine was uninitialized,
13803 * and @c false if the client process is still alive.
13804 *
13805 * @note Locks this object for writing.
13806 */
13807bool SessionMachine::i_checkForDeath()
13808{
13809 Uninit::Reason reason;
13810 bool terminated = false;
13811
13812 /* Enclose autoCaller with a block because calling uninit() from under it
13813 * will deadlock. */
13814 {
13815 AutoCaller autoCaller(this);
13816 if (!autoCaller.isOk())
13817 {
13818 /* return true if not ready, to cause the client watcher to exclude
13819 * the corresponding session from watching */
13820 LogFlowThisFunc(("Already uninitialized!\n"));
13821 return true;
13822 }
13823
13824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13825
13826 /* Determine the reason of death: if the session state is Closing here,
13827 * everything is fine. Otherwise it means that the client did not call
13828 * OnSessionEnd() before it released the IPC semaphore. This may happen
13829 * either because the client process has abnormally terminated, or
13830 * because it simply forgot to call ISession::Close() before exiting. We
13831 * threat the latter also as an abnormal termination (see
13832 * Session::uninit() for details). */
13833 reason = mData->mSession.mState == SessionState_Unlocking ?
13834 Uninit::Normal :
13835 Uninit::Abnormal;
13836
13837 if (mClientToken)
13838 terminated = mClientToken->release();
13839 } /* AutoCaller block */
13840
13841 if (terminated)
13842 uninit(reason);
13843
13844 return terminated;
13845}
13846
13847void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13848{
13849 LogFlowThisFunc(("\n"));
13850
13851 strTokenId.setNull();
13852
13853 AutoCaller autoCaller(this);
13854 AssertComRCReturnVoid(autoCaller.hrc());
13855
13856 Assert(mClientToken);
13857 if (mClientToken)
13858 mClientToken->getId(strTokenId);
13859}
13860#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13861IToken *SessionMachine::i_getToken()
13862{
13863 LogFlowThisFunc(("\n"));
13864
13865 AutoCaller autoCaller(this);
13866 AssertComRCReturn(autoCaller.hrc(), NULL);
13867
13868 Assert(mClientToken);
13869 if (mClientToken)
13870 return mClientToken->getToken();
13871 else
13872 return NULL;
13873}
13874#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13875
13876Machine::ClientToken *SessionMachine::i_getClientToken()
13877{
13878 LogFlowThisFunc(("\n"));
13879
13880 AutoCaller autoCaller(this);
13881 AssertComRCReturn(autoCaller.hrc(), NULL);
13882
13883 return mClientToken;
13884}
13885
13886
13887/**
13888 * @note Locks this object for reading.
13889 */
13890HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13891{
13892 LogFlowThisFunc(("\n"));
13893
13894 AutoCaller autoCaller(this);
13895 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13896
13897 ComPtr<IInternalSessionControl> directControl;
13898 {
13899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13900 if (mData->mSession.mLockType == LockType_VM)
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* ignore notifications sent after #OnSessionEnd() is called */
13905 if (!directControl)
13906 return S_OK;
13907
13908 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13909}
13910
13911/**
13912 * @note Locks this object for reading.
13913 */
13914HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13915 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13916 const Utf8Str &aGuestIp, LONG aGuestPort)
13917{
13918 LogFlowThisFunc(("\n"));
13919
13920 AutoCaller autoCaller(this);
13921 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13922
13923 ComPtr<IInternalSessionControl> directControl;
13924 {
13925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13926 if (mData->mSession.mLockType == LockType_VM)
13927 directControl = mData->mSession.mDirectControl;
13928 }
13929
13930 /* ignore notifications sent after #OnSessionEnd() is called */
13931 if (!directControl)
13932 return S_OK;
13933 /*
13934 * instead acting like callback we ask IVirtualBox deliver corresponding event
13935 */
13936
13937 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13938 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13939 return S_OK;
13940}
13941
13942/**
13943 * @note Locks this object for reading.
13944 */
13945HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13951
13952 ComPtr<IInternalSessionControl> directControl;
13953 {
13954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13955 if (mData->mSession.mLockType == LockType_VM)
13956 directControl = mData->mSession.mDirectControl;
13957 }
13958
13959 /* ignore notifications sent after #OnSessionEnd() is called */
13960 if (!directControl)
13961 return S_OK;
13962
13963 return directControl->OnAudioAdapterChange(audioAdapter);
13964}
13965
13966/**
13967 * @note Locks this object for reading.
13968 */
13969HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973 AutoCaller autoCaller(this);
13974 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13975
13976 ComPtr<IInternalSessionControl> directControl;
13977 {
13978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13979 if (mData->mSession.mLockType == LockType_VM)
13980 directControl = mData->mSession.mDirectControl;
13981 }
13982
13983 /* ignore notifications sent after #OnSessionEnd() is called */
13984 if (!directControl)
13985 return S_OK;
13986
13987 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
13988}
13989
13990/**
13991 * @note Locks this object for reading.
13992 */
13993HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13994{
13995 LogFlowThisFunc(("\n"));
13996
13997 AutoCaller autoCaller(this);
13998 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13999
14000 ComPtr<IInternalSessionControl> directControl;
14001 {
14002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14003 if (mData->mSession.mLockType == LockType_VM)
14004 directControl = mData->mSession.mDirectControl;
14005 }
14006
14007 /* ignore notifications sent after #OnSessionEnd() is called */
14008 if (!directControl)
14009 return S_OK;
14010
14011 return directControl->OnSerialPortChange(serialPort);
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 if (mData->mSession.mLockType == LockType_VM)
14028 directControl = mData->mSession.mDirectControl;
14029 }
14030
14031 /* ignore notifications sent after #OnSessionEnd() is called */
14032 if (!directControl)
14033 return S_OK;
14034
14035 return directControl->OnParallelPortChange(parallelPort);
14036}
14037
14038/**
14039 * @note Locks this object for reading.
14040 */
14041HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14042{
14043 LogFlowThisFunc(("\n"));
14044
14045 AutoCaller autoCaller(this);
14046 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14047
14048 ComPtr<IInternalSessionControl> directControl;
14049 {
14050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14051 if (mData->mSession.mLockType == LockType_VM)
14052 directControl = mData->mSession.mDirectControl;
14053 }
14054
14055 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14062}
14063
14064/**
14065 * @note Locks this object for reading.
14066 */
14067HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14068{
14069 LogFlowThisFunc(("\n"));
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 if (mData->mSession.mLockType == LockType_VM)
14078 directControl = mData->mSession.mDirectControl;
14079 }
14080
14081 mParent->i_onMediumChanged(aAttachment);
14082
14083 /* ignore notifications sent after #OnSessionEnd() is called */
14084 if (!directControl)
14085 return S_OK;
14086
14087 return directControl->OnMediumChange(aAttachment, aForce);
14088}
14089
14090HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14096
14097 ComPtr<IInternalSessionControl> directControl;
14098 {
14099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14100 if (mData->mSession.mLockType == LockType_VM)
14101 directControl = mData->mSession.mDirectControl;
14102 }
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnVMProcessPriorityChange(aPriority);
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnCPUChange(aCPU, aRemove);
14133}
14134
14135HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
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->OnCPUExecutionCapChange(aExecutionCap);
14154}
14155
14156/**
14157 * @note Locks this object for reading.
14158 */
14159HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163 AutoCaller autoCaller(this);
14164 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14165
14166 ComPtr<IInternalSessionControl> directControl;
14167 {
14168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14169 if (mData->mSession.mLockType == LockType_VM)
14170 directControl = mData->mSession.mDirectControl;
14171 }
14172
14173 /* ignore notifications sent after #OnSessionEnd() is called */
14174 if (!directControl)
14175 return S_OK;
14176
14177 return directControl->OnVRDEServerChange(aRestart);
14178}
14179
14180/**
14181 * @note Locks this object for reading.
14182 */
14183HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14184{
14185 LogFlowThisFunc(("\n"));
14186
14187 AutoCaller autoCaller(this);
14188 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14189
14190 ComPtr<IInternalSessionControl> directControl;
14191 {
14192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14193 if (mData->mSession.mLockType == LockType_VM)
14194 directControl = mData->mSession.mDirectControl;
14195 }
14196
14197 /* ignore notifications sent after #OnSessionEnd() is called */
14198 if (!directControl)
14199 return S_OK;
14200
14201 return directControl->OnRecordingChange(aEnable);
14202}
14203
14204/**
14205 * @note Locks this object for reading.
14206 */
14207HRESULT SessionMachine::i_onUSBControllerChange()
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14213
14214 ComPtr<IInternalSessionControl> directControl;
14215 {
14216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14217 if (mData->mSession.mLockType == LockType_VM)
14218 directControl = mData->mSession.mDirectControl;
14219 }
14220
14221 /* ignore notifications sent after #OnSessionEnd() is called */
14222 if (!directControl)
14223 return S_OK;
14224
14225 return directControl->OnUSBControllerChange();
14226}
14227
14228/**
14229 * @note Locks this object for reading.
14230 */
14231HRESULT SessionMachine::i_onSharedFolderChange()
14232{
14233 LogFlowThisFunc(("\n"));
14234
14235 AutoCaller autoCaller(this);
14236 AssertComRCReturnRC(autoCaller.hrc());
14237
14238 ComPtr<IInternalSessionControl> directControl;
14239 {
14240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14241 if (mData->mSession.mLockType == LockType_VM)
14242 directControl = mData->mSession.mDirectControl;
14243 }
14244
14245 /* ignore notifications sent after #OnSessionEnd() is called */
14246 if (!directControl)
14247 return S_OK;
14248
14249 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14250}
14251
14252/**
14253 * @note Locks this object for reading.
14254 */
14255HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturnRC(autoCaller.hrc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14265 if (mData->mSession.mLockType == LockType_VM)
14266 directControl = mData->mSession.mDirectControl;
14267 }
14268
14269 /* ignore notifications sent after #OnSessionEnd() is called */
14270 if (!directControl)
14271 return S_OK;
14272
14273 return directControl->OnClipboardModeChange(aClipboardMode);
14274}
14275
14276/**
14277 * @note Locks this object for reading.
14278 */
14279HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14280{
14281 LogFlowThisFunc(("\n"));
14282
14283 AutoCaller autoCaller(this);
14284 AssertComRCReturnRC(autoCaller.hrc());
14285
14286 ComPtr<IInternalSessionControl> directControl;
14287 {
14288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14289 if (mData->mSession.mLockType == LockType_VM)
14290 directControl = mData->mSession.mDirectControl;
14291 }
14292
14293 /* ignore notifications sent after #OnSessionEnd() is called */
14294 if (!directControl)
14295 return S_OK;
14296
14297 return directControl->OnClipboardFileTransferModeChange(aEnable);
14298}
14299
14300/**
14301 * @note Locks this object for reading.
14302 */
14303HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14304{
14305 LogFlowThisFunc(("\n"));
14306
14307 AutoCaller autoCaller(this);
14308 AssertComRCReturnRC(autoCaller.hrc());
14309
14310 ComPtr<IInternalSessionControl> directControl;
14311 {
14312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14313 if (mData->mSession.mLockType == LockType_VM)
14314 directControl = mData->mSession.mDirectControl;
14315 }
14316
14317 /* ignore notifications sent after #OnSessionEnd() is called */
14318 if (!directControl)
14319 return S_OK;
14320
14321 return directControl->OnDnDModeChange(aDnDMode);
14322}
14323
14324/**
14325 * @note Locks this object for reading.
14326 */
14327HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14328{
14329 LogFlowThisFunc(("\n"));
14330
14331 AutoCaller autoCaller(this);
14332 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14333
14334 ComPtr<IInternalSessionControl> directControl;
14335 {
14336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14337 if (mData->mSession.mLockType == LockType_VM)
14338 directControl = mData->mSession.mDirectControl;
14339 }
14340
14341 /* ignore notifications sent after #OnSessionEnd() is called */
14342 if (!directControl)
14343 return S_OK;
14344
14345 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14346}
14347
14348/**
14349 * @note Locks this object for reading.
14350 */
14351HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14352{
14353 LogFlowThisFunc(("\n"));
14354
14355 AutoCaller autoCaller(this);
14356 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14357
14358 ComPtr<IInternalSessionControl> directControl;
14359 {
14360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14361 if (mData->mSession.mLockType == LockType_VM)
14362 directControl = mData->mSession.mDirectControl;
14363 }
14364
14365 /* ignore notifications sent after #OnSessionEnd() is called */
14366 if (!directControl)
14367 return S_OK;
14368
14369 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14370}
14371
14372/**
14373 * @note Locks this object for reading.
14374 */
14375HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14376{
14377 LogFlowThisFunc(("\n"));
14378
14379 AutoCaller autoCaller(this);
14380 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14381
14382 ComPtr<IInternalSessionControl> directControl;
14383 {
14384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14385 if (mData->mSession.mLockType == LockType_VM)
14386 directControl = mData->mSession.mDirectControl;
14387 }
14388
14389 /* ignore notifications sent after #OnSessionEnd() is called */
14390 if (!directControl)
14391 return S_OK;
14392
14393 return directControl->OnGuestDebugControlChange(guestDebugControl);
14394}
14395
14396/**
14397 * Returns @c true if this machine's USB controller reports it has a matching
14398 * filter for the given USB device and @c false otherwise.
14399 *
14400 * @note locks this object for reading.
14401 */
14402bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14403{
14404 AutoCaller autoCaller(this);
14405 /* silently return if not ready -- this method may be called after the
14406 * direct machine session has been called */
14407 if (!autoCaller.isOk())
14408 return false;
14409
14410#ifdef VBOX_WITH_USB
14411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14412
14413 switch (mData->mMachineState)
14414 {
14415 case MachineState_Starting:
14416 case MachineState_Restoring:
14417 case MachineState_TeleportingIn:
14418 case MachineState_Paused:
14419 case MachineState_Running:
14420 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14421 * elsewhere... */
14422 alock.release();
14423 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14424 default: break;
14425 }
14426#else
14427 NOREF(aDevice);
14428 NOREF(aMaskedIfs);
14429#endif
14430 return false;
14431}
14432
14433/**
14434 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14435 */
14436HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14437 IVirtualBoxErrorInfo *aError,
14438 ULONG aMaskedIfs,
14439 const com::Utf8Str &aCaptureFilename)
14440{
14441 LogFlowThisFunc(("\n"));
14442
14443 AutoCaller autoCaller(this);
14444
14445 /* This notification may happen after the machine object has been
14446 * uninitialized (the session was closed), so don't assert. */
14447 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14448
14449 ComPtr<IInternalSessionControl> directControl;
14450 {
14451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14452 if (mData->mSession.mLockType == LockType_VM)
14453 directControl = mData->mSession.mDirectControl;
14454 }
14455
14456 /* fail on notifications sent after #OnSessionEnd() is called, it is
14457 * expected by the caller */
14458 if (!directControl)
14459 return E_FAIL;
14460
14461 /* No locks should be held at this point. */
14462 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14463 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14464
14465 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14466}
14467
14468/**
14469 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14470 */
14471HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14472 IVirtualBoxErrorInfo *aError)
14473{
14474 LogFlowThisFunc(("\n"));
14475
14476 AutoCaller autoCaller(this);
14477
14478 /* This notification may happen after the machine object has been
14479 * uninitialized (the session was closed), so don't assert. */
14480 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14481
14482 ComPtr<IInternalSessionControl> directControl;
14483 {
14484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14485 if (mData->mSession.mLockType == LockType_VM)
14486 directControl = mData->mSession.mDirectControl;
14487 }
14488
14489 /* fail on notifications sent after #OnSessionEnd() is called, it is
14490 * expected by the caller */
14491 if (!directControl)
14492 return E_FAIL;
14493
14494 /* No locks should be held at this point. */
14495 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14496 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14497
14498 return directControl->OnUSBDeviceDetach(aId, aError);
14499}
14500
14501// protected methods
14502/////////////////////////////////////////////////////////////////////////////
14503
14504/**
14505 * Deletes the given file if it is no longer in use by either the current machine state
14506 * (if the machine is "saved") or any of the machine's snapshots.
14507 *
14508 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14509 * but is different for each SnapshotMachine. When calling this, the order of calling this
14510 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14511 * is therefore critical. I know, it's all rather messy.
14512 *
14513 * @param strStateFile
14514 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14515 * the test for whether the saved state file is in use.
14516 */
14517void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14518 Snapshot *pSnapshotToIgnore)
14519{
14520 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14521 if ( (strStateFile.isNotEmpty())
14522 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14523 )
14524 // ... and it must also not be shared with other snapshots
14525 if ( !mData->mFirstSnapshot
14526 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14527 // this checks the SnapshotMachine's state file paths
14528 )
14529 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
14530}
14531
14532/**
14533 * Locks the attached media.
14534 *
14535 * All attached hard disks are locked for writing and DVD/floppy are locked for
14536 * reading. Parents of attached hard disks (if any) are locked for reading.
14537 *
14538 * This method also performs accessibility check of all media it locks: if some
14539 * media is inaccessible, the method will return a failure and a bunch of
14540 * extended error info objects per each inaccessible medium.
14541 *
14542 * Note that this method is atomic: if it returns a success, all media are
14543 * locked as described above; on failure no media is locked at all (all
14544 * succeeded individual locks will be undone).
14545 *
14546 * The caller is responsible for doing the necessary state sanity checks.
14547 *
14548 * The locks made by this method must be undone by calling #unlockMedia() when
14549 * no more needed.
14550 */
14551HRESULT SessionMachine::i_lockMedia()
14552{
14553 AutoCaller autoCaller(this);
14554 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14555
14556 AutoMultiWriteLock2 alock(this->lockHandle(),
14557 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14558
14559 /* bail out if trying to lock things with already set up locking */
14560 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14561
14562 MultiResult hrcMult(S_OK);
14563
14564 /* Collect locking information for all medium objects attached to the VM. */
14565 for (MediumAttachmentList::const_iterator
14566 it = mMediumAttachments->begin();
14567 it != mMediumAttachments->end();
14568 ++it)
14569 {
14570 MediumAttachment *pAtt = *it;
14571 DeviceType_T devType = pAtt->i_getType();
14572 Medium *pMedium = pAtt->i_getMedium();
14573
14574 MediumLockList *pMediumLockList(new MediumLockList());
14575 // There can be attachments without a medium (floppy/dvd), and thus
14576 // it's impossible to create a medium lock list. It still makes sense
14577 // to have the empty medium lock list in the map in case a medium is
14578 // attached later.
14579 if (pMedium != NULL)
14580 {
14581 MediumType_T mediumType = pMedium->i_getType();
14582 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14583 || mediumType == MediumType_Shareable;
14584 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14585
14586 alock.release();
14587 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14588 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14589 false /* fMediumLockWriteAll */,
14590 NULL,
14591 *pMediumLockList);
14592 alock.acquire();
14593 if (FAILED(hrcMult))
14594 {
14595 delete pMediumLockList;
14596 mData->mSession.mLockedMedia.Clear();
14597 break;
14598 }
14599 }
14600
14601 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14602 if (FAILED(hrc))
14603 {
14604 mData->mSession.mLockedMedia.Clear();
14605 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
14606 break;
14607 }
14608 }
14609
14610 if (SUCCEEDED(hrcMult))
14611 {
14612 /* Now lock all media. If this fails, nothing is locked. */
14613 alock.release();
14614 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
14615 alock.acquire();
14616 if (FAILED(hrc))
14617 hrcMult = setError(hrc,
14618 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14619 }
14620
14621 return hrcMult;
14622}
14623
14624/**
14625 * Undoes the locks made by by #lockMedia().
14626 */
14627HRESULT SessionMachine::i_unlockMedia()
14628{
14629 AutoCaller autoCaller(this);
14630 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
14631
14632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14633
14634 /* we may be holding important error info on the current thread;
14635 * preserve it */
14636 ErrorInfoKeeper eik;
14637
14638 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
14639 AssertComRC(hrc);
14640 return hrc;
14641}
14642
14643/**
14644 * Helper to change the machine state (reimplementation).
14645 *
14646 * @note Locks this object for writing.
14647 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14648 * it can cause crashes in random places due to unexpectedly committing
14649 * the current settings. The caller is responsible for that. The call
14650 * to saveStateSettings is fine, because this method does not commit.
14651 */
14652HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14653{
14654 LogFlowThisFuncEnter();
14655
14656 AutoCaller autoCaller(this);
14657 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14658
14659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14660
14661 MachineState_T oldMachineState = mData->mMachineState;
14662
14663 AssertMsgReturn(oldMachineState != aMachineState,
14664 ("oldMachineState=%s, aMachineState=%s\n",
14665 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14666 E_FAIL);
14667
14668 HRESULT hrc = S_OK;
14669
14670 int stsFlags = 0;
14671 bool deleteSavedState = false;
14672
14673 /* detect some state transitions */
14674
14675 if ( ( ( oldMachineState == MachineState_Saved
14676 || oldMachineState == MachineState_AbortedSaved
14677 )
14678 && aMachineState == MachineState_Restoring
14679 )
14680 || ( ( oldMachineState == MachineState_PoweredOff
14681 || oldMachineState == MachineState_Teleported
14682 || oldMachineState == MachineState_Aborted
14683 )
14684 && ( aMachineState == MachineState_TeleportingIn
14685 || aMachineState == MachineState_Starting
14686 )
14687 )
14688 )
14689 {
14690 /* The EMT thread is about to start */
14691
14692 /* Nothing to do here for now... */
14693
14694 /// @todo NEWMEDIA don't let mDVDDrive and other children
14695 /// change anything when in the Starting/Restoring state
14696 }
14697 else if ( ( oldMachineState == MachineState_Running
14698 || oldMachineState == MachineState_Paused
14699 || oldMachineState == MachineState_Teleporting
14700 || oldMachineState == MachineState_OnlineSnapshotting
14701 || oldMachineState == MachineState_LiveSnapshotting
14702 || oldMachineState == MachineState_Stuck
14703 || oldMachineState == MachineState_Starting
14704 || oldMachineState == MachineState_Stopping
14705 || oldMachineState == MachineState_Saving
14706 || oldMachineState == MachineState_Restoring
14707 || oldMachineState == MachineState_TeleportingPausedVM
14708 || oldMachineState == MachineState_TeleportingIn
14709 )
14710 && ( aMachineState == MachineState_PoweredOff
14711 || aMachineState == MachineState_Saved
14712 || aMachineState == MachineState_Teleported
14713 || aMachineState == MachineState_Aborted
14714 || aMachineState == MachineState_AbortedSaved
14715 )
14716 )
14717 {
14718 /* The EMT thread has just stopped, unlock attached media. Note that as
14719 * opposed to locking that is done from Console, we do unlocking here
14720 * because the VM process may have aborted before having a chance to
14721 * properly unlock all media it locked. */
14722
14723 unlockMedia();
14724 }
14725
14726 if (oldMachineState == MachineState_Restoring)
14727 {
14728 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14729 {
14730 /*
14731 * delete the saved state file once the machine has finished
14732 * restoring from it (note that Console sets the state from
14733 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14734 * to give the user an ability to fix an error and retry --
14735 * we keep the saved state file in this case)
14736 */
14737 deleteSavedState = true;
14738 }
14739 }
14740 else if ( ( oldMachineState == MachineState_Saved
14741 || oldMachineState == MachineState_AbortedSaved
14742 )
14743 && ( aMachineState == MachineState_PoweredOff
14744 || aMachineState == MachineState_Teleported
14745 )
14746 )
14747 {
14748 /* delete the saved state after SessionMachine::discardSavedState() is called */
14749 deleteSavedState = true;
14750 mData->mCurrentStateModified = TRUE;
14751 stsFlags |= SaveSTS_CurStateModified;
14752 }
14753 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14754 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14755
14756 if ( aMachineState == MachineState_Starting
14757 || aMachineState == MachineState_Restoring
14758 || aMachineState == MachineState_TeleportingIn
14759 )
14760 {
14761 /* set the current state modified flag to indicate that the current
14762 * state is no more identical to the state in the
14763 * current snapshot */
14764 if (!mData->mCurrentSnapshot.isNull())
14765 {
14766 mData->mCurrentStateModified = TRUE;
14767 stsFlags |= SaveSTS_CurStateModified;
14768 }
14769 }
14770
14771 if (deleteSavedState)
14772 {
14773 if (mRemoveSavedState)
14774 {
14775 Assert(!mSSData->strStateFilePath.isEmpty());
14776
14777 // it is safe to delete the saved state file if ...
14778 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14779 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14780 // ... none of the snapshots share the saved state file
14781 )
14782 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
14783 }
14784
14785 mSSData->strStateFilePath.setNull();
14786 stsFlags |= SaveSTS_StateFilePath;
14787 }
14788
14789 /* redirect to the underlying peer machine */
14790 mPeer->i_setMachineState(aMachineState);
14791
14792 if ( oldMachineState != MachineState_RestoringSnapshot
14793 && ( aMachineState == MachineState_PoweredOff
14794 || aMachineState == MachineState_Teleported
14795 || aMachineState == MachineState_Aborted
14796 || aMachineState == MachineState_AbortedSaved
14797 || aMachineState == MachineState_Saved))
14798 {
14799 /* the machine has stopped execution
14800 * (or the saved state file was adopted) */
14801 stsFlags |= SaveSTS_StateTimeStamp;
14802 }
14803
14804 if ( ( oldMachineState == MachineState_PoweredOff
14805 || oldMachineState == MachineState_Aborted
14806 || oldMachineState == MachineState_Teleported
14807 )
14808 && aMachineState == MachineState_Saved)
14809 {
14810 /* the saved state file was adopted */
14811 Assert(!mSSData->strStateFilePath.isEmpty());
14812 stsFlags |= SaveSTS_StateFilePath;
14813 }
14814
14815#ifdef VBOX_WITH_GUEST_PROPS
14816 if ( aMachineState == MachineState_PoweredOff
14817 || aMachineState == MachineState_Aborted
14818 || aMachineState == MachineState_Teleported)
14819 {
14820 /* Make sure any transient guest properties get removed from the
14821 * property store on shutdown. */
14822 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14823
14824 /* remove it from the settings representation */
14825 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14826 for (settings::GuestPropertiesList::iterator
14827 it = llGuestProperties.begin();
14828 it != llGuestProperties.end();
14829 /*nothing*/)
14830 {
14831 const settings::GuestProperty &prop = *it;
14832 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14833 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14834 {
14835 it = llGuestProperties.erase(it);
14836 fNeedsSaving = true;
14837 }
14838 else
14839 {
14840 ++it;
14841 }
14842 }
14843
14844 /* Additionally remove it from the HWData representation. Required to
14845 * keep everything in sync, as this is what the API keeps using. */
14846 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14847 for (HWData::GuestPropertyMap::iterator
14848 it = llHWGuestProperties.begin();
14849 it != llHWGuestProperties.end();
14850 /*nothing*/)
14851 {
14852 uint32_t fFlags = it->second.mFlags;
14853 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14854 {
14855 /* iterator where we need to continue after the erase call
14856 * (C++03 is a fact still, and it doesn't return the iterator
14857 * which would allow continuing) */
14858 HWData::GuestPropertyMap::iterator it2 = it;
14859 ++it2;
14860 llHWGuestProperties.erase(it);
14861 it = it2;
14862 fNeedsSaving = true;
14863 }
14864 else
14865 {
14866 ++it;
14867 }
14868 }
14869
14870 if (fNeedsSaving)
14871 {
14872 mData->mCurrentStateModified = TRUE;
14873 stsFlags |= SaveSTS_CurStateModified;
14874 }
14875 }
14876#endif /* VBOX_WITH_GUEST_PROPS */
14877
14878 hrc = i_saveStateSettings(stsFlags);
14879
14880 if ( ( oldMachineState != MachineState_PoweredOff
14881 && oldMachineState != MachineState_Aborted
14882 && oldMachineState != MachineState_Teleported
14883 )
14884 && ( aMachineState == MachineState_PoweredOff
14885 || aMachineState == MachineState_Aborted
14886 || aMachineState == MachineState_Teleported
14887 )
14888 )
14889 {
14890 /* we've been shut down for any reason */
14891 /* no special action so far */
14892 }
14893
14894 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
14895 LogFlowThisFuncLeave();
14896 return hrc;
14897}
14898
14899/**
14900 * Sends the current machine state value to the VM process.
14901 *
14902 * @note Locks this object for reading, then calls a client process.
14903 */
14904HRESULT SessionMachine::i_updateMachineStateOnClient()
14905{
14906 AutoCaller autoCaller(this);
14907 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14908
14909 ComPtr<IInternalSessionControl> directControl;
14910 {
14911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14912 AssertReturn(!!mData, E_FAIL);
14913 if (mData->mSession.mLockType == LockType_VM)
14914 directControl = mData->mSession.mDirectControl;
14915
14916 /* directControl may be already set to NULL here in #OnSessionEnd()
14917 * called too early by the direct session process while there is still
14918 * some operation (like deleting the snapshot) in progress. The client
14919 * process in this case is waiting inside Session::close() for the
14920 * "end session" process object to complete, while #uninit() called by
14921 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14922 * operation to complete. For now, we accept this inconsistent behavior
14923 * and simply do nothing here. */
14924
14925 if (mData->mSession.mState == SessionState_Unlocking)
14926 return S_OK;
14927 }
14928
14929 /* ignore notifications sent after #OnSessionEnd() is called */
14930 if (!directControl)
14931 return S_OK;
14932
14933 return directControl->UpdateMachineState(mData->mMachineState);
14934}
14935
14936
14937/*static*/
14938HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14939{
14940 va_list args;
14941 va_start(args, pcszMsg);
14942 HRESULT hrc = setErrorInternalV(aResultCode,
14943 getStaticClassIID(),
14944 getStaticComponentName(),
14945 pcszMsg, args,
14946 false /* aWarning */,
14947 true /* aLogIt */);
14948 va_end(args);
14949 return hrc;
14950}
14951
14952
14953HRESULT Machine::updateState(MachineState_T aState)
14954{
14955 NOREF(aState);
14956 ReturnComNotImplemented();
14957}
14958
14959HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14960{
14961 NOREF(aProgress);
14962 ReturnComNotImplemented();
14963}
14964
14965HRESULT Machine::endPowerUp(LONG aResult)
14966{
14967 NOREF(aResult);
14968 ReturnComNotImplemented();
14969}
14970
14971HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14972{
14973 NOREF(aProgress);
14974 ReturnComNotImplemented();
14975}
14976
14977HRESULT Machine::endPoweringDown(LONG aResult,
14978 const com::Utf8Str &aErrMsg)
14979{
14980 NOREF(aResult);
14981 NOREF(aErrMsg);
14982 ReturnComNotImplemented();
14983}
14984
14985HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14986 BOOL *aMatched,
14987 ULONG *aMaskedInterfaces)
14988{
14989 NOREF(aDevice);
14990 NOREF(aMatched);
14991 NOREF(aMaskedInterfaces);
14992 ReturnComNotImplemented();
14993
14994}
14995
14996HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14997{
14998 NOREF(aId); NOREF(aCaptureFilename);
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15003 BOOL aDone)
15004{
15005 NOREF(aId);
15006 NOREF(aDone);
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::autoCaptureUSBDevices()
15011{
15012 ReturnComNotImplemented();
15013}
15014
15015HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15016{
15017 NOREF(aDone);
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15022 ComPtr<IProgress> &aProgress)
15023{
15024 NOREF(aSession);
15025 NOREF(aProgress);
15026 ReturnComNotImplemented();
15027}
15028
15029HRESULT Machine::finishOnlineMergeMedium()
15030{
15031 ReturnComNotImplemented();
15032}
15033
15034HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15035 std::vector<com::Utf8Str> &aValues,
15036 std::vector<LONG64> &aTimestamps,
15037 std::vector<com::Utf8Str> &aFlags)
15038{
15039 NOREF(aNames);
15040 NOREF(aValues);
15041 NOREF(aTimestamps);
15042 NOREF(aFlags);
15043 ReturnComNotImplemented();
15044}
15045
15046HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15047 const com::Utf8Str &aValue,
15048 LONG64 aTimestamp,
15049 const com::Utf8Str &aFlags,
15050 BOOL fWasDeleted)
15051{
15052 NOREF(aName);
15053 NOREF(aValue);
15054 NOREF(aTimestamp);
15055 NOREF(aFlags);
15056 NOREF(fWasDeleted);
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::lockMedia()
15061{
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::unlockMedia()
15066{
15067 ReturnComNotImplemented();
15068}
15069
15070HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15071 ComPtr<IMediumAttachment> &aNewAttachment)
15072{
15073 NOREF(aAttachment);
15074 NOREF(aNewAttachment);
15075 ReturnComNotImplemented();
15076}
15077
15078HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15079 ULONG aCpuUser,
15080 ULONG aCpuKernel,
15081 ULONG aCpuIdle,
15082 ULONG aMemTotal,
15083 ULONG aMemFree,
15084 ULONG aMemBalloon,
15085 ULONG aMemShared,
15086 ULONG aMemCache,
15087 ULONG aPagedTotal,
15088 ULONG aMemAllocTotal,
15089 ULONG aMemFreeTotal,
15090 ULONG aMemBalloonTotal,
15091 ULONG aMemSharedTotal,
15092 ULONG aVmNetRx,
15093 ULONG aVmNetTx)
15094{
15095 NOREF(aValidStats);
15096 NOREF(aCpuUser);
15097 NOREF(aCpuKernel);
15098 NOREF(aCpuIdle);
15099 NOREF(aMemTotal);
15100 NOREF(aMemFree);
15101 NOREF(aMemBalloon);
15102 NOREF(aMemShared);
15103 NOREF(aMemCache);
15104 NOREF(aPagedTotal);
15105 NOREF(aMemAllocTotal);
15106 NOREF(aMemFreeTotal);
15107 NOREF(aMemBalloonTotal);
15108 NOREF(aMemSharedTotal);
15109 NOREF(aVmNetRx);
15110 NOREF(aVmNetTx);
15111 ReturnComNotImplemented();
15112}
15113
15114HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15115 com::Utf8Str &aResult)
15116{
15117 NOREF(aAuthParams);
15118 NOREF(aResult);
15119 ReturnComNotImplemented();
15120}
15121
15122HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15123{
15124 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15125
15126 AutoCaller autoCaller(this);
15127 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15128
15129 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15130 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15131 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15132 if (FAILED(hrc)) return hrc;
15133
15134 NOREF(aFlags);
15135 com::Utf8Str osTypeId;
15136 ComObjPtr<GuestOSType> osType = NULL;
15137
15138 /* Get the guest os type as a string from the VB. */
15139 hrc = getOSTypeId(osTypeId);
15140 if (FAILED(hrc)) return hrc;
15141
15142 /* Get the os type obj that coresponds, can be used to get
15143 * the defaults for this guest OS. */
15144 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15145 if (FAILED(hrc)) return hrc;
15146
15147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15148
15149 mPlatform->i_applyDefaults(osType);
15150
15151 /* This one covers IOAPICEnabled. */
15152 mFirmwareSettings->i_applyDefaults(osType);
15153
15154 /* Initialize default record settings. */
15155 mRecordingSettings->i_applyDefaults();
15156
15157 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15158 if (FAILED(hrc)) return hrc;
15159
15160 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15161 if (FAILED(hrc)) return hrc;
15162
15163 /* Graphics stuff. */
15164 GraphicsControllerType_T graphicsController;
15165 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15166 if (FAILED(hrc)) return hrc;
15167
15168 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15169 if (FAILED(hrc)) return hrc;
15170
15171 ULONG vramSize;
15172 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15173 if (FAILED(hrc)) return hrc;
15174
15175 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15176 if (FAILED(hrc)) return hrc;
15177
15178 BOOL fAccelerate2DVideoEnabled;
15179 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15180 if (FAILED(hrc)) return hrc;
15181
15182 hrc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15183 if (FAILED(hrc)) return hrc;
15184
15185 BOOL fAccelerate3DEnabled;
15186 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15187 if (FAILED(hrc)) return hrc;
15188
15189 hrc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15190 if (FAILED(hrc)) return hrc;
15191
15192 /* Apply network adapters defaults */
15193 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15194 mNetworkAdapters[slot]->i_applyDefaults(osType);
15195
15196 /* Apply serial port defaults */
15197 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15198 mSerialPorts[slot]->i_applyDefaults(osType);
15199
15200 /* Apply parallel port defaults - not OS dependent*/
15201 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15202 mParallelPorts[slot]->i_applyDefaults();
15203
15204 /* This one covers the TPM type. */
15205 mTrustedPlatformModule->i_applyDefaults(osType);
15206
15207 /* This one covers secure boot. */
15208 hrc = mNvramStore->i_applyDefaults(osType);
15209 if (FAILED(hrc)) return hrc;
15210
15211 /* Audio stuff. */
15212 hrc = mAudioSettings->i_applyDefaults(osType);
15213 if (FAILED(hrc)) return hrc;
15214
15215 /* Storage Controllers */
15216 StorageControllerType_T hdStorageControllerType;
15217 StorageBus_T hdStorageBusType;
15218 StorageControllerType_T dvdStorageControllerType;
15219 StorageBus_T dvdStorageBusType;
15220 BOOL recommendedFloppy;
15221 ComPtr<IStorageController> floppyController;
15222 ComPtr<IStorageController> hdController;
15223 ComPtr<IStorageController> dvdController;
15224 Utf8Str strFloppyName, strDVDName, strHDName;
15225
15226 /* GUI auto generates controller names using bus type. Do the same*/
15227 strFloppyName = StorageController::i_controllerNameFromBusType(StorageBus_Floppy);
15228
15229 /* Floppy recommended? add one. */
15230 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15231 if (FAILED(hrc)) return hrc;
15232 if (recommendedFloppy)
15233 {
15234 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15235 if (FAILED(hrc)) return hrc;
15236 }
15237
15238 /* Setup one DVD storage controller. */
15239 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15240 if (FAILED(hrc)) return hrc;
15241
15242 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15243 if (FAILED(hrc)) return hrc;
15244
15245 strDVDName = StorageController::i_controllerNameFromBusType(dvdStorageBusType);
15246
15247 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15248 if (FAILED(hrc)) return hrc;
15249
15250 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15251 if (FAILED(hrc)) return hrc;
15252
15253 /* Setup one HDD storage controller. */
15254 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15255 if (FAILED(hrc)) return hrc;
15256
15257 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15258 if (FAILED(hrc)) return hrc;
15259
15260 strHDName = StorageController::i_controllerNameFromBusType(hdStorageBusType);
15261
15262 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15263 {
15264 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15265 if (FAILED(hrc)) return hrc;
15266
15267 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15268 if (FAILED(hrc)) return hrc;
15269 }
15270 else
15271 {
15272 /* The HD controller is the same as DVD: */
15273 hdController = dvdController;
15274 }
15275
15276 /* Limit the AHCI port count if it's used because windows has trouble with
15277 * too many ports and other guest (OS X in particular) may take extra long
15278 * boot: */
15279
15280 // pParent = static_cast<Medium*>(aP)
15281 IStorageController *temp = hdController;
15282 ComObjPtr<StorageController> storageController;
15283 storageController = static_cast<StorageController *>(temp);
15284
15285 // tempHDController = aHDController;
15286 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15287 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15288 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15289 storageController->COMSETTER(PortCount)(1);
15290
15291 /* VirtioSCSI configures only one port per default -- set two ports here, one for HDD and one for DVD drive. */
15292 if (hdStorageControllerType == StorageControllerType_VirtioSCSI)
15293 {
15294 hrc = storageController->COMSETTER(PortCount)(2);
15295 if (FAILED(hrc)) return hrc;
15296 }
15297
15298 /* USB stuff */
15299
15300 bool ohciEnabled = false;
15301
15302 ComPtr<IUSBController> usbController;
15303 BOOL recommendedUSB3;
15304 BOOL recommendedUSB;
15305 BOOL usbProxyAvailable;
15306
15307 getUSBProxyAvailable(&usbProxyAvailable);
15308 if (FAILED(hrc)) return hrc;
15309
15310 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15311 if (FAILED(hrc)) return hrc;
15312 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15313 if (FAILED(hrc)) return hrc;
15314
15315 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15316 {
15317 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15318 if (FAILED(hrc)) return hrc;
15319
15320 /* xHci includes OHCI */
15321 ohciEnabled = true;
15322 }
15323 if ( !ohciEnabled
15324 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15325 {
15326 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15327 if (FAILED(hrc)) return hrc;
15328 ohciEnabled = true;
15329
15330 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15331 if (FAILED(hrc)) return hrc;
15332 }
15333
15334 /* Set recommended human interface device types: */
15335 BOOL recommendedUSBHID;
15336 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15337 if (FAILED(hrc)) return hrc;
15338
15339 if (recommendedUSBHID)
15340 {
15341 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15342 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15343 if (!ohciEnabled && !usbDeviceFilters.isNull())
15344 {
15345 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15346 if (FAILED(hrc)) return hrc;
15347 }
15348 }
15349
15350 BOOL recommendedUSBTablet;
15351 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15352 if (FAILED(hrc)) return hrc;
15353
15354 if (recommendedUSBTablet)
15355 {
15356 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15357 if (!ohciEnabled && !usbDeviceFilters.isNull())
15358 {
15359 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15360 if (FAILED(hrc)) return hrc;
15361 }
15362 }
15363
15364 /* Enable the VMMDev testing feature for bootsector VMs: */
15365 if (osTypeId == GUEST_OS_ID_STR_X64("VBoxBS"))
15366 {
15367 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15368 if (FAILED(hrc))
15369 return hrc;
15370 }
15371
15372 return S_OK;
15373}
15374
15375#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15376/**
15377 * Task record for change encryption settins.
15378 */
15379class Machine::ChangeEncryptionTask
15380 : public Machine::Task
15381{
15382public:
15383 ChangeEncryptionTask(Machine *m,
15384 Progress *p,
15385 const Utf8Str &t,
15386 const com::Utf8Str &aCurrentPassword,
15387 const com::Utf8Str &aCipher,
15388 const com::Utf8Str &aNewPassword,
15389 const com::Utf8Str &aNewPasswordId,
15390 const BOOL aForce,
15391 const MediaList &llMedia)
15392 : Task(m, p, t),
15393 mstrNewPassword(aNewPassword),
15394 mstrCurrentPassword(aCurrentPassword),
15395 mstrCipher(aCipher),
15396 mstrNewPasswordId(aNewPasswordId),
15397 mForce(aForce),
15398 mllMedia(llMedia)
15399 {}
15400
15401 ~ChangeEncryptionTask()
15402 {
15403 if (mstrNewPassword.length())
15404 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15405 if (mstrCurrentPassword.length())
15406 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15407 if (m_pCryptoIf)
15408 {
15409 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15410 m_pCryptoIf = NULL;
15411 }
15412 }
15413
15414 Utf8Str mstrNewPassword;
15415 Utf8Str mstrCurrentPassword;
15416 Utf8Str mstrCipher;
15417 Utf8Str mstrNewPasswordId;
15418 BOOL mForce;
15419 MediaList mllMedia;
15420 PCVBOXCRYPTOIF m_pCryptoIf;
15421private:
15422 void handler()
15423 {
15424 try
15425 {
15426 m_pMachine->i_changeEncryptionHandler(*this);
15427 }
15428 catch (...)
15429 {
15430 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
15431 }
15432 }
15433
15434 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
15435};
15436
15437/**
15438 * Scans specified directory and fills list by files found
15439 *
15440 * @returns VBox status code.
15441 * @param lstFiles
15442 * @param strDir
15443 * @param filePattern
15444 */
15445int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
15446 const com::Utf8Str &strPattern)
15447{
15448 /* To get all entries including subdirectories. */
15449 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
15450 if (!pszFilePattern)
15451 return VERR_NO_STR_MEMORY;
15452
15453 PRTDIRENTRYEX pDirEntry = NULL;
15454 RTDIR hDir;
15455 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
15456 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
15457 if (RT_SUCCESS(vrc))
15458 {
15459 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
15460 if (pDirEntry)
15461 {
15462 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
15463 != VERR_NO_MORE_FILES)
15464 {
15465 char *pszFilePath = NULL;
15466
15467 if (vrc == VERR_BUFFER_OVERFLOW)
15468 {
15469 /* allocate new buffer. */
15470 RTMemFree(pDirEntry);
15471 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
15472 if (!pDirEntry)
15473 {
15474 vrc = VERR_NO_MEMORY;
15475 break;
15476 }
15477 /* Retry. */
15478 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
15479 if (RT_FAILURE(vrc))
15480 break;
15481 }
15482 else if (RT_FAILURE(vrc))
15483 break;
15484
15485 /* Exclude . and .. */
15486 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
15487 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
15488 continue;
15489 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
15490 {
15491 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15492 if (!pszSubDirPath)
15493 {
15494 vrc = VERR_NO_STR_MEMORY;
15495 break;
15496 }
15497 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
15498 RTMemFree(pszSubDirPath);
15499 if (RT_FAILURE(vrc))
15500 break;
15501 continue;
15502 }
15503
15504 /* We got the new entry. */
15505 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
15506 continue;
15507
15508 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
15509 continue;
15510
15511 /* Prepend the path to the libraries. */
15512 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15513 if (!pszFilePath)
15514 {
15515 vrc = VERR_NO_STR_MEMORY;
15516 break;
15517 }
15518
15519 lstFiles.push_back(pszFilePath);
15520 RTStrFree(pszFilePath);
15521 }
15522
15523 RTMemFree(pDirEntry);
15524 }
15525 else
15526 vrc = VERR_NO_MEMORY;
15527
15528 RTDirClose(hDir);
15529 }
15530 else
15531 {
15532 /* On Windows the above immediately signals that there are no
15533 * files matching, while on other platforms enumerating the
15534 * files below fails. Either way: stop searching. */
15535 }
15536
15537 if ( vrc == VERR_NO_MORE_FILES
15538 || vrc == VERR_FILE_NOT_FOUND
15539 || vrc == VERR_PATH_NOT_FOUND)
15540 vrc = VINF_SUCCESS;
15541 RTStrFree(pszFilePattern);
15542 return vrc;
15543}
15544
15545/**
15546 * Helper to set up an I/O stream to read or write a possibly encrypted file.
15547 *
15548 * @returns VBox status code.
15549 * @param pszFilename The file to open.
15550 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
15551 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
15552 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
15553 * @param fOpen The open flags for the file.
15554 * @param phVfsIos Where to store the handle to the I/O stream on success.
15555 */
15556int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
15557 const char *pszKeyStore, const char *pszPassword,
15558 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
15559{
15560 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
15561 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
15562 if (RT_SUCCESS(vrc))
15563 {
15564 if (pCryptoIf)
15565 {
15566 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
15567 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
15568 if (RT_SUCCESS(vrc))
15569 {
15570 RTVfsFileRelease(hVfsFile);
15571 hVfsFile = hVfsFileCrypto;
15572 }
15573 }
15574
15575 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
15576 RTVfsFileRelease(hVfsFile);
15577 }
15578
15579 return vrc;
15580}
15581
15582/**
15583 * Helper function processing all actions for one component (saved state files,
15584 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
15585 *
15586 * @param task
15587 * @param strDirectory
15588 * @param strFilePattern
15589 * @param strMagic
15590 * @param strKeyStore
15591 * @param strKeyId
15592 * @return
15593 */
15594HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
15595 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
15596 com::Utf8Str &strKeyId, int iCipherMode)
15597{
15598 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
15599 && task.mstrCipher.isEmpty()
15600 && task.mstrNewPassword.isEmpty()
15601 && task.mstrNewPasswordId.isEmpty();
15602 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
15603 && task.mstrCipher.isNotEmpty()
15604 && task.mstrNewPassword.isNotEmpty()
15605 && task.mstrNewPasswordId.isNotEmpty();
15606
15607 /* check if the cipher is changed which causes the reencryption*/
15608
15609 const char *pszTaskCipher = NULL;
15610 if (task.mstrCipher.isNotEmpty())
15611 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
15612
15613 if (!task.mForce && !fDecrypt && !fEncrypt)
15614 {
15615 char *pszCipher = NULL;
15616 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
15617 NULL /*pszPassword*/,
15618 NULL /*ppbKey*/,
15619 NULL /*pcbKey*/,
15620 &pszCipher);
15621 if (RT_SUCCESS(vrc))
15622 {
15623 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
15624 RTMemFree(pszCipher);
15625 }
15626 else
15627 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
15628 strFilePattern.c_str(), vrc);
15629 }
15630
15631 /* Only the password needs to be changed */
15632 if (!task.mForce && !fDecrypt && !fEncrypt)
15633 {
15634 Assert(task.m_pCryptoIf);
15635
15636 VBOXCRYPTOCTX hCryptoCtx;
15637 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
15638 if (RT_FAILURE(vrc))
15639 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
15640 strFilePattern.c_str(), vrc);
15641 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15642 if (RT_FAILURE(vrc))
15643 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
15644 strFilePattern.c_str(), vrc);
15645
15646 char *pszKeyStore = NULL;
15647 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15648 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15649 if (RT_FAILURE(vrc))
15650 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
15651 strFilePattern.c_str(), vrc);
15652 strKeyStore = pszKeyStore;
15653 RTMemFree(pszKeyStore);
15654 strKeyId = task.mstrNewPasswordId;
15655 return S_OK;
15656 }
15657
15658 /* Reencryption required */
15659 HRESULT hrc = S_OK;
15660 int vrc = VINF_SUCCESS;
15661
15662 std::list<com::Utf8Str> lstFiles;
15663 if (SUCCEEDED(hrc))
15664 {
15665 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
15666 if (RT_FAILURE(vrc))
15667 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
15668 }
15669 com::Utf8Str strNewKeyStore;
15670 if (SUCCEEDED(hrc))
15671 {
15672 if (!fDecrypt)
15673 {
15674 VBOXCRYPTOCTX hCryptoCtx;
15675 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
15676 if (RT_FAILURE(vrc))
15677 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
15678 strFilePattern.c_str(), vrc);
15679
15680 char *pszKeyStore = NULL;
15681 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15682 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15683 if (RT_FAILURE(vrc))
15684 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
15685 strFilePattern.c_str(), vrc);
15686 strNewKeyStore = pszKeyStore;
15687 RTMemFree(pszKeyStore);
15688 }
15689
15690 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15691 it != lstFiles.end();
15692 ++it)
15693 {
15694 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
15695 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
15696
15697 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
15698 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
15699
15700 vrc = i_createIoStreamForFile((*it).c_str(),
15701 fEncrypt ? NULL : task.m_pCryptoIf,
15702 fEncrypt ? NULL : strKeyStore.c_str(),
15703 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
15704 fOpenForRead, &hVfsIosOld);
15705 if (RT_SUCCESS(vrc))
15706 {
15707 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
15708 fDecrypt ? NULL : task.m_pCryptoIf,
15709 fDecrypt ? NULL : strNewKeyStore.c_str(),
15710 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
15711 fOpenForWrite, &hVfsIosNew);
15712 if (RT_FAILURE(vrc))
15713 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
15714 (*it + ".tmp").c_str(), vrc);
15715 }
15716 else
15717 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
15718
15719 if (RT_SUCCESS(vrc))
15720 {
15721 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
15722 if (RT_FAILURE(vrc))
15723 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
15724 (*it).c_str(), vrc);
15725 }
15726
15727 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
15728 RTVfsIoStrmRelease(hVfsIosOld);
15729 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
15730 RTVfsIoStrmRelease(hVfsIosNew);
15731 }
15732 }
15733
15734 if (SUCCEEDED(hrc))
15735 {
15736 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15737 it != lstFiles.end();
15738 ++it)
15739 {
15740 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
15741 if (RT_FAILURE(vrc))
15742 {
15743 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
15744 break;
15745 }
15746 }
15747 }
15748
15749 if (SUCCEEDED(hrc))
15750 {
15751 strKeyStore = strNewKeyStore;
15752 strKeyId = task.mstrNewPasswordId;
15753 }
15754
15755 return hrc;
15756}
15757
15758/**
15759 * Task thread implementation for Machine::changeEncryption(), called from
15760 * Machine::taskHandler().
15761 *
15762 * @note Locks this object for writing.
15763 *
15764 * @param task
15765 * @return
15766 */
15767void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
15768{
15769 LogFlowThisFuncEnter();
15770
15771 AutoCaller autoCaller(this);
15772 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
15773 if (FAILED(autoCaller.hrc()))
15774 {
15775 /* we might have been uninitialized because the session was accidentally
15776 * closed by the client, so don't assert */
15777 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
15778 task.m_pProgress->i_notifyComplete(hrc);
15779 LogFlowThisFuncLeave();
15780 return;
15781 }
15782
15783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15784
15785 HRESULT hrc = S_OK;
15786 com::Utf8Str strOldKeyId = mData->mstrKeyId;
15787 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
15788 try
15789 {
15790 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
15791 if (FAILED(hrc))
15792 throw hrc;
15793
15794 if (task.mstrCurrentPassword.isEmpty())
15795 {
15796 if (mData->mstrKeyStore.isNotEmpty())
15797 throw setError(VBOX_E_PASSWORD_INCORRECT,
15798 tr("The password given for the encrypted VM is incorrect"));
15799 }
15800 else
15801 {
15802 if (mData->mstrKeyStore.isEmpty())
15803 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15804 tr("The VM is not configured for encryption"));
15805 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
15806 if (hrc == VBOX_E_PASSWORD_INCORRECT)
15807 throw setError(VBOX_E_PASSWORD_INCORRECT,
15808 tr("The password to decrypt the VM is incorrect"));
15809 }
15810
15811 if (task.mstrCipher.isNotEmpty())
15812 {
15813 if ( task.mstrNewPassword.isEmpty()
15814 && task.mstrNewPasswordId.isEmpty()
15815 && task.mstrCurrentPassword.isNotEmpty())
15816 {
15817 /* An empty password and password ID will default to the current password. */
15818 task.mstrNewPassword = task.mstrCurrentPassword;
15819 }
15820 else if (task.mstrNewPassword.isEmpty())
15821 throw setError(VBOX_E_OBJECT_NOT_FOUND,
15822 tr("A password must be given for the VM encryption"));
15823 else if (task.mstrNewPasswordId.isEmpty())
15824 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15825 tr("A valid identifier for the password must be given"));
15826 }
15827 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
15828 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15829 tr("The password and password identifier must be empty if the output should be unencrypted"));
15830
15831 /*
15832 * Save config.
15833 * Must be first operation to prevent making encrypted copies
15834 * for old version of the config file.
15835 */
15836 int fSave = Machine::SaveS_Force;
15837 if (task.mstrNewPassword.isNotEmpty())
15838 {
15839 VBOXCRYPTOCTX hCryptoCtx;
15840
15841 int vrc = VINF_SUCCESS;
15842 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
15843 {
15844 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
15845 task.mstrNewPassword.c_str(), &hCryptoCtx);
15846 if (RT_FAILURE(vrc))
15847 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
15848 }
15849 else
15850 {
15851 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
15852 task.mstrCurrentPassword.c_str(),
15853 &hCryptoCtx);
15854 if (RT_FAILURE(vrc))
15855 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
15856 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15857 if (RT_FAILURE(vrc))
15858 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
15859 }
15860
15861 char *pszKeyStore;
15862 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15863 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15864 if (RT_FAILURE(vrc))
15865 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
15866 mData->mstrKeyStore = pszKeyStore;
15867 RTStrFree(pszKeyStore);
15868 mData->mstrKeyId = task.mstrNewPasswordId;
15869 size_t cbPassword = task.mstrNewPassword.length() + 1;
15870 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
15871 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
15872 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
15873 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
15874
15875 /*
15876 * Remove backuped config after saving because it can contain
15877 * unencrypted version of the config
15878 */
15879 fSave |= Machine::SaveS_RemoveBackup;
15880 }
15881 else
15882 {
15883 mData->mstrKeyId.setNull();
15884 mData->mstrKeyStore.setNull();
15885 }
15886
15887 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
15888 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
15889 Bstr bstrNewPassword(task.mstrNewPassword);
15890 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
15891 /* encrypt media */
15892 alock.release();
15893 for (MediaList::iterator it = task.mllMedia.begin();
15894 it != task.mllMedia.end();
15895 ++it)
15896 {
15897 ComPtr<IProgress> pProgress1;
15898 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
15899 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
15900 pProgress1.asOutParam());
15901 if (FAILED(hrc)) throw hrc;
15902 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
15903 if (FAILED(hrc)) throw hrc;
15904 }
15905 alock.acquire();
15906
15907 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
15908
15909 Utf8Str strFullSnapshotFolder;
15910 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
15911
15912 /* .sav files (main and snapshots) */
15913 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
15914 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
15915 if (FAILED(hrc))
15916 /* the helper function already sets error object */
15917 throw hrc;
15918
15919 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
15920
15921 /* .nvram files */
15922 com::Utf8Str strNVRAMKeyId;
15923 com::Utf8Str strNVRAMKeyStore;
15924 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
15925 if (FAILED(hrc))
15926 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
15927
15928 Utf8Str strMachineFolder;
15929 i_calculateFullPath(".", strMachineFolder);
15930
15931 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
15932 if (FAILED(hrc))
15933 /* the helper function already sets error object */
15934 throw hrc;
15935
15936 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
15937 if (FAILED(hrc))
15938 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
15939
15940 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
15941
15942 /* .log files */
15943 com::Utf8Str strLogFolder;
15944 i_getLogFolder(strLogFolder);
15945 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
15946 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
15947 if (FAILED(hrc))
15948 /* the helper function already sets error object */
15949 throw hrc;
15950
15951 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
15952
15953 i_saveSettings(NULL, alock, fSave);
15954 }
15955 catch (HRESULT hrcXcpt)
15956 {
15957 hrc = hrcXcpt;
15958 mData->mstrKeyId = strOldKeyId;
15959 mData->mstrKeyStore = strOldKeyStore;
15960 }
15961
15962 task.m_pProgress->i_notifyComplete(hrc);
15963
15964 LogFlowThisFuncLeave();
15965}
15966#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
15967
15968HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
15969 const com::Utf8Str &aCipher,
15970 const com::Utf8Str &aNewPassword,
15971 const com::Utf8Str &aNewPasswordId,
15972 BOOL aForce,
15973 ComPtr<IProgress> &aProgress)
15974{
15975 LogFlowFuncEnter();
15976
15977#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15978 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15979 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15980#else
15981 /* make the VM accessible */
15982 if (!mData->mAccessible)
15983 {
15984 if ( aCurrentPassword.isEmpty()
15985 || mData->mstrKeyId.isEmpty())
15986 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
15987
15988 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
15989 if (FAILED(hrc))
15990 return hrc;
15991 }
15992
15993 AutoLimitedCaller autoCaller(this);
15994 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15995
15996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15997
15998 /* define media to be change encryption */
15999
16000 MediaList llMedia;
16001 for (MediumAttachmentList::iterator
16002 it = mMediumAttachments->begin();
16003 it != mMediumAttachments->end();
16004 ++it)
16005 {
16006 ComObjPtr<MediumAttachment> &pAttach = *it;
16007 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16008
16009 if (!pMedium.isNull())
16010 {
16011 AutoCaller mac(pMedium);
16012 if (FAILED(mac.hrc())) return mac.hrc();
16013 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16014 DeviceType_T devType = pMedium->i_getDeviceType();
16015 if (devType == DeviceType_HardDisk)
16016 {
16017 /*
16018 * We need to move to last child because the Medium::changeEncryption
16019 * encrypts all chain of specified medium with its parents.
16020 * Also we perform cheking of back reference and children for
16021 * all media in the chain to raise error before we start any action.
16022 * So, we first move into root parent and then we will move to last child
16023 * keeping latter in the list for encryption.
16024 */
16025
16026 /* move to root parent */
16027 ComObjPtr<Medium> pTmpMedium = pMedium;
16028 while (pTmpMedium.isNotNull())
16029 {
16030 AutoCaller mediumAC(pTmpMedium);
16031 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16032 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16033
16034 /* Cannot encrypt media which are attached to more than one virtual machine. */
16035 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16036 if (cBackRefs > 1)
16037 return setError(VBOX_E_INVALID_OBJECT_STATE,
16038 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16039 pTmpMedium->i_getName().c_str(), cBackRefs);
16040
16041 size_t cChildren = pTmpMedium->i_getChildren().size();
16042 if (cChildren > 1)
16043 return setError(VBOX_E_INVALID_OBJECT_STATE,
16044 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16045 pTmpMedium->i_getName().c_str(), cChildren);
16046
16047 pTmpMedium = pTmpMedium->i_getParent();
16048 }
16049 /* move to last child */
16050 pTmpMedium = pMedium;
16051 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16052 {
16053 AutoCaller mediumAC(pTmpMedium);
16054 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16055 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16056
16057 /* Cannot encrypt media which are attached to more than one virtual machine. */
16058 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16059 if (cBackRefs > 1)
16060 return setError(VBOX_E_INVALID_OBJECT_STATE,
16061 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16062 pTmpMedium->i_getName().c_str(), cBackRefs);
16063
16064 size_t cChildren = pTmpMedium->i_getChildren().size();
16065 if (cChildren > 1)
16066 return setError(VBOX_E_INVALID_OBJECT_STATE,
16067 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16068 pTmpMedium->i_getName().c_str(), cChildren);
16069
16070 pTmpMedium = pTmpMedium->i_getChildren().front();
16071 }
16072 llMedia.push_back(pTmpMedium);
16073 }
16074 }
16075 }
16076
16077 ComObjPtr<Progress> pProgress;
16078 pProgress.createObject();
16079 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16080 static_cast<IMachine*>(this) /* aInitiator */,
16081 tr("Change encryption"),
16082 TRUE /* fCancellable */,
16083 (ULONG)(4 + + llMedia.size()), // cOperations
16084 tr("Change encryption of the mediuma"));
16085 if (FAILED(hrc))
16086 return hrc;
16087
16088 /* create and start the task on a separate thread (note that it will not
16089 * start working until we release alock) */
16090 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16091 aCurrentPassword, aCipher, aNewPassword,
16092 aNewPasswordId, aForce, llMedia);
16093 hrc = pTask->createThread();
16094 pTask = NULL;
16095 if (FAILED(hrc))
16096 return hrc;
16097
16098 pProgress.queryInterfaceTo(aProgress.asOutParam());
16099
16100 LogFlowFuncLeave();
16101
16102 return S_OK;
16103#endif
16104}
16105
16106HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16107 com::Utf8Str &aPasswordId)
16108{
16109#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16110 RT_NOREF(aCipher, aPasswordId);
16111 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16112#else
16113 AutoLimitedCaller autoCaller(this);
16114 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16115
16116 PCVBOXCRYPTOIF pCryptoIf = NULL;
16117 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16118 if (FAILED(hrc)) return hrc; /* Error is set */
16119
16120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16121
16122 if (mData->mstrKeyStore.isNotEmpty())
16123 {
16124 char *pszCipher = NULL;
16125 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16126 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16127 if (RT_SUCCESS(vrc))
16128 {
16129 aCipher = getCipherStringWithoutMode(pszCipher);
16130 RTStrFree(pszCipher);
16131 aPasswordId = mData->mstrKeyId;
16132 }
16133 else
16134 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16135 tr("Failed to query the encryption settings with %Rrc"),
16136 vrc);
16137 }
16138 else
16139 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16140
16141 mParent->i_releaseCryptoIf(pCryptoIf);
16142
16143 return hrc;
16144#endif
16145}
16146
16147HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16148{
16149#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16150 RT_NOREF(aPassword);
16151 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16152#else
16153 AutoLimitedCaller autoCaller(this);
16154 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16155
16156 PCVBOXCRYPTOIF pCryptoIf = NULL;
16157 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16158 if (FAILED(hrc)) return hrc; /* Error is set */
16159
16160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16161
16162 if (mData->mstrKeyStore.isNotEmpty())
16163 {
16164 char *pszCipher = NULL;
16165 uint8_t *pbDek = NULL;
16166 size_t cbDek = 0;
16167 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16168 &pbDek, &cbDek, &pszCipher);
16169 if (RT_SUCCESS(vrc))
16170 {
16171 RTStrFree(pszCipher);
16172 RTMemSaferFree(pbDek, cbDek);
16173 }
16174 else
16175 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16176 tr("The password supplied for the encrypted machine is incorrect"));
16177 }
16178 else
16179 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16180
16181 mParent->i_releaseCryptoIf(pCryptoIf);
16182
16183 return hrc;
16184#endif
16185}
16186
16187HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16188 const com::Utf8Str &aPassword)
16189{
16190#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16191 RT_NOREF(aId, aPassword);
16192 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16193#else
16194 AutoLimitedCaller autoCaller(this);
16195 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16196
16197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16198
16199 size_t cbPassword = aPassword.length() + 1;
16200 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16201
16202 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16203
16204 if ( mData->mAccessible
16205 && mData->mSession.mState == SessionState_Locked
16206 && mData->mSession.mLockType == LockType_VM
16207 && mData->mSession.mDirectControl != NULL)
16208 {
16209 /* get the console from the direct session */
16210 ComPtr<IConsole> console;
16211 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16212 ComAssertComRC(hrc);
16213 /* send passsword to console */
16214 console->AddEncryptionPassword(Bstr(aId).raw(),
16215 Bstr(aPassword).raw(),
16216 TRUE);
16217 }
16218
16219 if (mData->mstrKeyId == aId)
16220 {
16221 HRESULT hrc = checkEncryptionPassword(aPassword);
16222 if (FAILED(hrc))
16223 return hrc;
16224
16225 if (SUCCEEDED(hrc))
16226 {
16227 /*
16228 * Encryption is used and password is correct,
16229 * Reinit the machine if required.
16230 */
16231 BOOL fAccessible;
16232 alock.release();
16233 getAccessible(&fAccessible);
16234 alock.acquire();
16235 }
16236 }
16237
16238 /*
16239 * Add the password into the NvramStore only after
16240 * the machine becomes accessible and the NvramStore
16241 * contains key id and key store.
16242 */
16243 if (mNvramStore.isNotNull())
16244 mNvramStore->i_addPassword(aId, aPassword);
16245
16246 return S_OK;
16247#endif
16248}
16249
16250HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16251 const std::vector<com::Utf8Str> &aPasswords)
16252{
16253#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16254 RT_NOREF(aIds, aPasswords);
16255 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16256#else
16257 if (aIds.size() != aPasswords.size())
16258 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16259
16260 HRESULT hrc = S_OK;
16261 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16262 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16263
16264 return hrc;
16265#endif
16266}
16267
16268HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16269{
16270#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16271 RT_NOREF(autoCaller, aId);
16272 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16273#else
16274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16275
16276 if ( mData->mAccessible
16277 && mData->mSession.mState == SessionState_Locked
16278 && mData->mSession.mLockType == LockType_VM
16279 && mData->mSession.mDirectControl != NULL)
16280 {
16281 /* get the console from the direct session */
16282 ComPtr<IConsole> console;
16283 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16284 ComAssertComRC(hrc);
16285 /* send passsword to console */
16286 console->RemoveEncryptionPassword(Bstr(aId).raw());
16287 }
16288
16289 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16290 {
16291 if (Global::IsOnlineOrTransient(mData->mMachineState))
16292 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16293 alock.release();
16294 autoCaller.release();
16295 /* return because all passwords are purged when machine becomes inaccessible; */
16296 return i_setInaccessible();
16297 }
16298
16299 if (mNvramStore.isNotNull())
16300 mNvramStore->i_removePassword(aId);
16301 mData->mpKeyStore->deleteSecretKey(aId);
16302 return S_OK;
16303#endif
16304}
16305
16306HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16307{
16308#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16309 RT_NOREF(autoCaller);
16310 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16311#else
16312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16313
16314 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16315 {
16316 if (Global::IsOnlineOrTransient(mData->mMachineState))
16317 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16318 alock.release();
16319 autoCaller.release();
16320 /* return because all passwords are purged when machine becomes inaccessible; */
16321 return i_setInaccessible();
16322 }
16323
16324 mNvramStore->i_removeAllPasswords();
16325 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16326 return S_OK;
16327#endif
16328}
16329
16330#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16331HRESULT Machine::i_setInaccessible()
16332{
16333 if (!mData->mAccessible)
16334 return S_OK;
16335
16336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16337 VirtualBox *pParent = mParent;
16338 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16339 Guid id(i_getId());
16340
16341 alock.release();
16342
16343 uninit();
16344 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16345
16346 alock.acquire();
16347 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16348 return hrc;
16349}
16350#endif
16351
16352/* This isn't handled entirely by the wrapper generator yet. */
16353#ifdef VBOX_WITH_XPCOM
16354NS_DECL_CLASSINFO(SessionMachine)
16355NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16356
16357NS_DECL_CLASSINFO(SnapshotMachine)
16358NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16359#endif
Note: See TracBrowser for help on using the repository browser.

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