VirtualBox

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

Last change on this file since 106971 was 106832, checked in by vboxsync, 7 months ago

Main/{Machine,Medium,Snapshot}: Address broken reference counting of
medium back references when snapshots are involved which could cause
back references to get removed while the medium was still in use. Also
resolved some locking issues for various failure scenarios when creating
or deleting snapshots.

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

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