VirtualBox

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

Last change on this file was 103085, checked in by vboxsync, 4 months ago

Main,FE/VBoxManage,FE/VirtualBox,ValidationKit: Allow setting the primary VM execution engine to make it easier to force particular engine for testing, bugref:10583

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

© 2023 Oracle
ContactPrivacy policyTerms of Use