VirtualBox

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

Last change on this file since 108628 was 108566, checked in by vboxsync, 2 months ago

bugref:10806. Excluded Machine, Medium, Session support. Only Progress support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 585.2 KB
Line 
1/* $Id: MachineImpl.cpp 108566 2025-03-17 05:35:54Z 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# define VRDE_AUTO_GENENERATED_CERT_FILENAME "VRDEAutoGeneratedCert.pem"
125# define VRDE_AUTO_GENENERATED_PKEY_FILENAME "VRDEAutoGeneratedPrivateKey.pem"
126
127// defines / prototypes
128/////////////////////////////////////////////////////////////////////////////
129
130#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
131# define BUF_DATA_SIZE _64K
132
133enum CipherMode
134{
135 CipherModeGcm = 0,
136 CipherModeCtr,
137 CipherModeXts,
138 CipherModeMax
139};
140
141enum AesSize
142{
143 Aes128 = 0,
144 Aes256,
145 AesMax
146};
147
148const char *g_apszCipher[AesMax][CipherModeMax] =
149{
150 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
151 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
152};
153const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
154
155static const char *getCipherString(const char *pszAlgo, const int iMode)
156{
157 if (iMode >= CipherModeMax)
158 return pszAlgo;
159
160 for (int i = 0; i < AesMax; i++)
161 {
162 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
163 return g_apszCipher[i][iMode];
164 }
165 return pszAlgo;
166}
167
168static const char *getCipherStringWithoutMode(const char *pszAlgo)
169{
170 for (int i = 0; i < AesMax; i++)
171 {
172 for (int j = 0; j < CipherModeMax; j++)
173 {
174 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
175 return g_apszCipherAlgo[i];
176 }
177 }
178 return pszAlgo;
179}
180#endif
181
182/////////////////////////////////////////////////////////////////////////////
183// Machine::Data structure
184/////////////////////////////////////////////////////////////////////////////
185
186Machine::Data::Data()
187{
188 mRegistered = FALSE;
189 pMachineConfigFile = NULL;
190 /* Contains hints on what has changed when the user is using the VM (config
191 * changes, running the VM, ...). This is used to decide if a config needs
192 * to be written to disk. */
193 flModifications = 0;
194 /* VM modification usually also trigger setting the current state to
195 * "Modified". Although this is not always the case. An e.g. is the VM
196 * initialization phase or when snapshot related data is changed. The
197 * actually behavior is controlled by the following flag. */
198 m_fAllowStateModification = false;
199 mAccessible = FALSE;
200 /* mUuid is initialized in Machine::init() */
201
202 mMachineState = MachineState_PoweredOff;
203 RTTimeNow(&mLastStateChange);
204
205 mMachineStateDeps = 0;
206 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
207 mMachineStateChangePending = 0;
208
209 mCurrentStateModified = TRUE;
210 mGuestPropertiesModified = FALSE;
211
212 mSession.mPID = NIL_RTPROCESS;
213 mSession.mLockType = LockType_Null;
214 mSession.mState = SessionState_Unlocked;
215
216#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
217 mpKeyStore = NULL;
218 fEncrypted = false;
219#endif
220}
221
222Machine::Data::~Data()
223{
224 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
225 {
226 RTSemEventMultiDestroy(mMachineStateDepsSem);
227 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
228 }
229 if (pMachineConfigFile)
230 {
231 delete pMachineConfigFile;
232 pMachineConfigFile = NULL;
233 }
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine::HWData structure
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::HWData::HWData()
241{
242 /* default values for a newly created machine for x86. */
243 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
244 mMemorySize = 128;
245 mCPUCount = 1;
246 mCPUHotPlugEnabled = false;
247 mMemoryBalloonSize = 0;
248 mPageFusionEnabled = false;
249 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
250 mCpuIdPortabilityLevel = 0;
251 mExecEngine = VMExecutionEngine_NotSet;
252 mCpuProfile = "host";
253
254 /* default boot order: floppy - DVD - HDD */
255 mBootOrder[0] = DeviceType_Floppy;
256 mBootOrder[1] = DeviceType_DVD;
257 mBootOrder[2] = DeviceType_HardDisk;
258 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
259 mBootOrder[i] = DeviceType_Null;
260
261 mClipboardMode = ClipboardMode_Disabled;
262 mClipboardFileTransfersEnabled = FALSE;
263
264 mDnDMode = DnDMode_Disabled;
265
266 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
267 mPointingHIDType = PointingHIDType_PS2Mouse;
268 mParavirtProvider = ParavirtProvider_Default;
269 mEmulatedUSBCardReaderEnabled = FALSE;
270
271 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
272 mCPUAttached[i] = false;
273
274 mIOCacheEnabled = true;
275 mIOCacheSize = 5; /* 5MB */
276}
277
278Machine::HWData::~HWData()
279{
280}
281
282/////////////////////////////////////////////////////////////////////////////
283// Machine class
284/////////////////////////////////////////////////////////////////////////////
285
286// constructor / destructor
287/////////////////////////////////////////////////////////////////////////////
288
289Machine::Machine() :
290#ifdef VBOX_WITH_RESOURCE_USAGE_API
291 mCollectorGuest(NULL),
292#endif
293 mPeer(NULL),
294 mParent(NULL),
295 mSerialPorts(),
296 mParallelPorts(),
297 uRegistryNeedsSaving(0)
298{}
299
300Machine::~Machine()
301{}
302
303HRESULT Machine::FinalConstruct()
304{
305 LogFlowThisFunc(("\n"));
306 return BaseFinalConstruct();
307}
308
309void Machine::FinalRelease()
310{
311 LogFlowThisFunc(("\n"));
312 uninit();
313 BaseFinalRelease();
314}
315
316/**
317 * Initializes a new machine instance; this init() variant creates a new, empty machine.
318 * This gets called from VirtualBox::CreateMachine().
319 *
320 * @param aParent Associated parent object.
321 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
322 * @param strName Name for the machine.
323 * @param aArchitecture Architecture to use for the machine.
324 * If a valid guest OS type is set via \a aOsType, the guest OS' type will be used instead then.
325 * @param llGroups list of groups for the machine.
326 * @param strOsType OS Type string (stored as is if aOsType is NULL).
327 * @param aOsType OS Type of this machine or NULL.
328 * @param aId UUID for the new machine.
329 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
330 * @param fDirectoryIncludesUUID
331 * Whether the use a special VM directory naming scheme (includes the UUID).
332 * @param aCipher The cipher to encrypt the VM with.
333 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
334 * @param aPassword The password to encrypt the VM with.
335 *
336 * @return Success indicator. if not S_OK, the machine object is invalid.
337 */
338HRESULT Machine::init(VirtualBox *aParent,
339 const Utf8Str &strConfigFile,
340 const Utf8Str &strName,
341 PlatformArchitecture_T aArchitecture,
342 const StringsList &llGroups,
343 const Utf8Str &strOsType,
344 GuestOSType *aOsType,
345 const Guid &aId,
346 bool fForceOverwrite,
347 bool fDirectoryIncludesUUID,
348 const com::Utf8Str &aCipher,
349 const com::Utf8Str &aPasswordId,
350 const com::Utf8Str &aPassword)
351{
352 LogFlowThisFuncEnter();
353 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
354
355#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
356 RT_NOREF(aCipher);
357 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
358 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
359#endif
360
361 /* Enclose the state transition NotReady->InInit->Ready */
362 AutoInitSpan autoInitSpan(this);
363 AssertReturn(autoInitSpan.isOk(), E_FAIL);
364
365 HRESULT hrc = initImpl(aParent, strConfigFile);
366 if (FAILED(hrc)) return hrc;
367
368#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
369 com::Utf8Str strSsmKeyId;
370 com::Utf8Str strSsmKeyStore;
371 com::Utf8Str strNVRAMKeyId;
372 com::Utf8Str strNVRAMKeyStore;
373
374 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
375 {
376 /* Resolve the cryptographic interface. */
377 PCVBOXCRYPTOIF pCryptoIf = NULL;
378 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
379 if (SUCCEEDED(hrc))
380 {
381 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
382 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
383 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
384
385 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
386 {
387 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
388 if (!pszCipher)
389 {
390 hrc = setError(VBOX_E_NOT_SUPPORTED,
391 tr("The cipher '%s' is not supported"), aCipher.c_str());
392 break;
393 }
394
395 VBOXCRYPTOCTX hCryptoCtx;
396 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
397 if (RT_FAILURE(vrc))
398 {
399 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
400 break;
401 }
402
403 char *pszKeyStore;
404 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
405 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
406 AssertRC(vrc2);
407
408 if (RT_FAILURE(vrc))
409 {
410 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
411 break;
412 }
413
414 *(astrKeyStore[i]) = pszKeyStore;
415 RTMemFree(pszKeyStore);
416 *(astrKeyId[i]) = aPasswordId;
417 }
418
419 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
420 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
421
422 if (FAILED(hrc))
423 return hrc; /* Error is set. */
424 }
425 else
426 return hrc; /* Error is set. */
427 }
428#endif
429
430 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
431 if (FAILED(hrc)) return hrc;
432
433 if (SUCCEEDED(hrc))
434 {
435 // create an empty machine config
436 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
437
438 hrc = initDataAndChildObjects();
439 }
440
441 if (SUCCEEDED(hrc))
442 {
443#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
444 mSSData->strStateKeyId = strSsmKeyId;
445 mSSData->strStateKeyStore = strSsmKeyStore;
446#endif
447
448 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
449 mData->mAccessible = TRUE;
450
451 unconst(mData->mUuid) = aId;
452
453 mUserData->s.strName = strName;
454
455 if (llGroups.size())
456 mUserData->s.llGroups = llGroups;
457
458 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
459 // the "name sync" flag determines whether the machine directory gets renamed along
460 // with the machine file; say so if the settings file name is the same as the
461 // settings file parent directory (machine directory)
462 mUserData->s.fNameSync = i_isInOwnDir();
463
464 // initialize the default snapshots folder
465 hrc = COMSETTER(SnapshotFolder)(NULL);
466 AssertComRC(hrc);
467
468 /* Use the platform architecture which was handed-in by default. */
469 PlatformArchitecture_T enmPlatformArch = aArchitecture;
470
471 if (aOsType)
472 {
473 /* Store OS type */
474 mUserData->s.strOsType = aOsType->i_id();
475
476 /* Use the platform architecture of the found guest OS type. */
477 enmPlatformArch = aOsType->i_platformArchitecture();
478 }
479 else if (!strOsType.isEmpty())
480 {
481 /* Store OS type */
482 mUserData->s.strOsType = strOsType;
483 }
484
485 /* Set the platform architecture first before applying the defaults below. */
486 hrc = mPlatform->i_initArchitecture(enmPlatformArch);
487 if (FAILED(hrc)) return hrc;
488
489 /* Apply platform defaults. */
490 mPlatform->i_applyDefaults(aOsType);
491 i_platformPropertiesUpdate();
492
493 /* Apply firmware defaults. */
494 mFirmwareSettings->i_applyDefaults(aOsType);
495
496 /* Apply TPM defaults. */
497 mTrustedPlatformModule->i_applyDefaults(aOsType);
498
499 /* Apply recording defaults. */
500 mRecordingSettings->i_applyDefaults();
501
502 /* Apply network adapters defaults */
503 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
504 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
505
506 /* Apply serial port defaults */
507 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
508 mSerialPorts[slot]->i_applyDefaults(aOsType);
509
510 /* Apply parallel port defaults */
511 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
512 mParallelPorts[slot]->i_applyDefaults();
513
514 /* Enable the VMMDev testing feature for bootsector VMs: */
515 if (aOsType && aOsType->i_id() == GUEST_OS_ID_STR_X64("VBoxBS"))
516 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
517
518#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
519 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
520#endif
521 if (SUCCEEDED(hrc))
522 {
523 /* At this point the changing of the current state modification
524 * flag is allowed. */
525 i_allowStateModification();
526
527 /* commit all changes made during the initialization */
528 i_commit();
529 }
530 }
531
532 /* Confirm a successful initialization when it's the case */
533 if (SUCCEEDED(hrc))
534 {
535#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
536 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
537 {
538 size_t cbPassword = aPassword.length() + 1;
539 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
540 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
541 }
542#endif
543
544 if (mData->mAccessible)
545 autoInitSpan.setSucceeded();
546 else
547 autoInitSpan.setLimited();
548 }
549
550 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
551 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
552 mData->mRegistered,
553 mData->mAccessible,
554 hrc));
555
556 LogFlowThisFuncLeave();
557
558 return hrc;
559}
560
561/**
562 * Initializes a new instance with data from machine XML (formerly Init_Registered).
563 * Gets called in two modes:
564 *
565 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
566 * UUID is specified and we mark the machine as "registered";
567 *
568 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
569 * and the machine remains unregistered until RegisterMachine() is called.
570 *
571 * @param aParent Associated parent object
572 * @param strConfigFile Local file system path to the VM settings file (can
573 * be relative to the VirtualBox config directory).
574 * @param aId UUID of the machine or NULL (see above).
575 * @param strPassword Password for decrypting the config
576 *
577 * @return Success indicator. if not S_OK, the machine object is invalid
578 */
579HRESULT Machine::initFromSettings(VirtualBox *aParent,
580 const Utf8Str &strConfigFile,
581 const Guid *aId,
582 const com::Utf8Str &strPassword)
583{
584 LogFlowThisFuncEnter();
585 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
586
587 PCVBOXCRYPTOIF pCryptoIf = NULL;
588#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
589 if (strPassword.isNotEmpty())
590 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
591#else
592 if (strPassword.isNotEmpty())
593 {
594 /* Get at the crpytographic interface. */
595 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
596 if (FAILED(hrc))
597 return hrc; /* Error is set. */
598 }
599#endif
600
601 /* Enclose the state transition NotReady->InInit->Ready */
602 AutoInitSpan autoInitSpan(this);
603 AssertReturn(autoInitSpan.isOk(), E_FAIL);
604
605 HRESULT hrc = initImpl(aParent, strConfigFile);
606 if (FAILED(hrc)) return hrc;
607
608 if (aId)
609 {
610 // loading a registered VM:
611 unconst(mData->mUuid) = *aId;
612 mData->mRegistered = TRUE;
613 // now load the settings from XML:
614 hrc = i_registeredInit();
615 // this calls initDataAndChildObjects() and loadSettings()
616 }
617 else
618 {
619 // opening an unregistered VM (VirtualBox::OpenMachine()):
620 hrc = initDataAndChildObjects();
621 if (SUCCEEDED(hrc))
622 {
623 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
624 mData->mAccessible = TRUE;
625
626 try
627 {
628 // load and parse machine XML; this will throw on XML or logic errors
629 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
630 pCryptoIf,
631 strPassword.c_str());
632
633 // reject VM UUID duplicates, they can happen if someone
634 // tries to register an already known VM config again
635 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
636 true /* fPermitInaccessible */,
637 false /* aDoSetError */,
638 NULL) != VBOX_E_OBJECT_NOT_FOUND)
639 {
640 throw setError(E_FAIL,
641 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
642 mData->m_strConfigFile.c_str());
643 }
644
645 // use UUID from machine config
646 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
647
648#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
649 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
650 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
651 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
652 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
653#endif
654
655 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
656 {
657 // We just set the inaccessible state and fill the error info allowing the caller
658 // to register the machine with encrypted config even if the password is incorrect
659 mData->mAccessible = FALSE;
660
661 /* fetch the current error info */
662 mData->mAccessError = com::ErrorInfo();
663
664 setError(VBOX_E_PASSWORD_INCORRECT,
665 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
666 mData->pMachineConfigFile->uuid.raw());
667 }
668 else
669 {
670#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
671 if (strPassword.isNotEmpty())
672 {
673 size_t cbKey = strPassword.length() + 1; /* Include terminator */
674 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
675 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
676 }
677#endif
678
679 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
680 if (FAILED(hrc)) throw hrc;
681
682 /* At this point the changing of the current state modification
683 * flag is allowed. */
684 i_allowStateModification();
685
686 i_commit();
687 }
688 }
689 catch (HRESULT err)
690 {
691 /* we assume that error info is set by the thrower */
692 hrc = err;
693 }
694 catch (...)
695 {
696 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
697 }
698 }
699 }
700
701 /* Confirm a successful initialization when it's the case */
702 if (SUCCEEDED(hrc))
703 {
704 if (mData->mAccessible)
705 autoInitSpan.setSucceeded();
706 else
707 {
708 autoInitSpan.setLimited();
709
710 // uninit media from this machine's media registry, or else
711 // reloading the settings will fail
712 mParent->i_unregisterMachineMedia(i_getId());
713 }
714 }
715
716#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
717 if (pCryptoIf)
718 {
719 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
720 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
721 }
722#endif
723
724 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
725 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
726
727 LogFlowThisFuncLeave();
728
729 return hrc;
730}
731
732/**
733 * Initializes a new instance from a machine config that is already in memory
734 * (import OVF case). Since we are importing, the UUID in the machine
735 * config is ignored and we always generate a fresh one.
736 *
737 * @param aParent Associated parent object.
738 * @param strName Name for the new machine; this overrides what is specified in config.
739 * @param strSettingsFilename File name of .vbox file.
740 * @param config Machine configuration loaded and parsed from XML.
741 *
742 * @return Success indicator. if not S_OK, the machine object is invalid
743 */
744HRESULT Machine::init(VirtualBox *aParent,
745 const Utf8Str &strName,
746 const Utf8Str &strSettingsFilename,
747 const settings::MachineConfigFile &config)
748{
749 LogFlowThisFuncEnter();
750
751 /* Enclose the state transition NotReady->InInit->Ready */
752 AutoInitSpan autoInitSpan(this);
753 AssertReturn(autoInitSpan.isOk(), E_FAIL);
754
755 HRESULT hrc = initImpl(aParent, strSettingsFilename);
756 if (FAILED(hrc)) return hrc;
757
758 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
759 if (FAILED(hrc)) return hrc;
760
761 hrc = initDataAndChildObjects();
762 if (SUCCEEDED(hrc))
763 {
764 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
765 mData->mAccessible = TRUE;
766
767 // create empty machine config for instance data
768 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
769
770 // generate fresh UUID, ignore machine config
771 unconst(mData->mUuid).create();
772
773 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
774
775 // override VM name as well, it may be different
776 mUserData->s.strName = strName;
777
778 if (SUCCEEDED(hrc))
779 {
780 /* At this point the changing of the current state modification
781 * flag is allowed. */
782 i_allowStateModification();
783
784 /* commit all changes made during the initialization */
785 i_commit();
786 }
787 }
788
789 /* Confirm a successful initialization when it's the case */
790 if (SUCCEEDED(hrc))
791 {
792 if (mData->mAccessible)
793 autoInitSpan.setSucceeded();
794 else
795 {
796 /* Ignore all errors from unregistering, they would destroy
797- * the more interesting error information we already have,
798- * pinpointing the issue with the VM config. */
799 ErrorInfoKeeper eik;
800
801 autoInitSpan.setLimited();
802
803 // uninit media from this machine's media registry, or else
804 // reloading the settings will fail
805 mParent->i_unregisterMachineMedia(i_getId());
806 }
807 }
808
809 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
810 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
811
812 LogFlowThisFuncLeave();
813
814 return hrc;
815}
816
817/**
818 * Shared code between the various init() implementations.
819 *
820 * @returns HRESULT
821 * @param aParent The VirtualBox object.
822 * @param strConfigFile Settings file.
823 */
824HRESULT Machine::initImpl(VirtualBox *aParent,
825 const Utf8Str &strConfigFile)
826{
827 LogFlowThisFuncEnter();
828
829 AssertReturn(aParent, E_INVALIDARG);
830 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
831
832 HRESULT hrc = S_OK;
833
834 /* share the parent weakly */
835 unconst(mParent) = aParent;
836
837 /* allocate the essential machine data structure (the rest will be
838 * allocated later by initDataAndChildObjects() */
839 mData.allocate();
840
841 /* memorize the config file name (as provided) */
842 mData->m_strConfigFile = strConfigFile;
843
844 /* get the full file name */
845 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
846 if (RT_FAILURE(vrc1))
847 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
848 tr("Invalid machine settings file name '%s' (%Rrc)"),
849 strConfigFile.c_str(),
850 vrc1);
851
852#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
853 /** @todo Only create when the machine is going to be encrypted. */
854 /* Non-pageable memory is not accessible for non-VM process */
855 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
856 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
857#endif
858
859 LogFlowThisFuncLeave();
860
861 return hrc;
862}
863
864/**
865 * Tries to create a machine settings file in the path stored in the machine
866 * instance data. Used when a new machine is created to fail gracefully if
867 * the settings file could not be written (e.g. because machine dir is read-only).
868 * @return
869 */
870HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
871{
872 HRESULT hrc = S_OK;
873
874 // when we create a new machine, we must be able to create the settings file
875 RTFILE f = NIL_RTFILE;
876 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
877 if ( RT_SUCCESS(vrc)
878 || vrc == VERR_SHARING_VIOLATION
879 )
880 {
881 if (RT_SUCCESS(vrc))
882 RTFileClose(f);
883 if (!fForceOverwrite)
884 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
885 else
886 {
887 /* try to delete the config file, as otherwise the creation
888 * of a new settings file will fail. */
889 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
890 }
891 }
892 else if ( vrc != VERR_FILE_NOT_FOUND
893 && vrc != VERR_PATH_NOT_FOUND
894 )
895 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
896 mData->m_strConfigFileFull.c_str(), vrc);
897 return hrc;
898}
899
900/**
901 * Initializes the registered machine by loading the settings file.
902 * This method is separated from #init() in order to make it possible to
903 * retry the operation after VirtualBox startup instead of refusing to
904 * startup the whole VirtualBox server in case if the settings file of some
905 * registered VM is invalid or inaccessible.
906 *
907 * @note Must be always called from this object's write lock
908 * (unless called from #init() that doesn't need any locking).
909 * @note Locks the mUSBController method for writing.
910 * @note Subclasses must not call this method.
911 */
912HRESULT Machine::i_registeredInit()
913{
914 AssertReturn(!i_isSessionMachine(), E_FAIL);
915 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
916 AssertReturn(mData->mUuid.isValid(), E_FAIL);
917 AssertReturn(!mData->mAccessible, E_FAIL);
918
919 HRESULT hrc = initDataAndChildObjects();
920 if (SUCCEEDED(hrc))
921 {
922 /* Temporarily reset the registered flag in order to let setters
923 * potentially called from loadSettings() succeed (isMutable() used in
924 * all setters will return FALSE for a Machine instance if mRegistered
925 * is TRUE). */
926 mData->mRegistered = FALSE;
927
928 PCVBOXCRYPTOIF pCryptoIf = NULL;
929 SecretKey *pKey = NULL;
930 const char *pszPassword = NULL;
931#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
932 /* Resolve password and cryptographic support interface if machine is encrypted. */
933 if (mData->mstrKeyId.isNotEmpty())
934 {
935 /* Get at the crpytographic interface. */
936 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
937 if (SUCCEEDED(hrc))
938 {
939 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
940 if (RT_SUCCESS(vrc))
941 pszPassword = (const char *)pKey->getKeyBuffer();
942 else
943 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
944 mData->mstrKeyId.c_str(), vrc);
945 }
946 }
947#else
948 RT_NOREF(pKey);
949#endif
950
951 if (SUCCEEDED(hrc))
952 {
953 try
954 {
955 // load and parse machine XML; this will throw on XML or logic errors
956 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
957 pCryptoIf, pszPassword);
958
959 if (mData->mUuid != mData->pMachineConfigFile->uuid)
960 throw setError(E_FAIL,
961 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
962 mData->pMachineConfigFile->uuid.raw(),
963 mData->m_strConfigFileFull.c_str(),
964 mData->mUuid.toString().c_str(),
965 mParent->i_settingsFilePath().c_str());
966
967#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
968 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
969 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
970 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
971 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
972
973 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
974 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
975 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
976 mData->pMachineConfigFile->uuid.raw());
977 else
978#endif
979 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
980 if (FAILED(hrc)) throw hrc;
981 }
982 catch (HRESULT err)
983 {
984 /* we assume that error info is set by the thrower */
985 hrc = err;
986 }
987 catch (...)
988 {
989 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
990 }
991
992 /* Restore the registered flag (even on failure) */
993 mData->mRegistered = TRUE;
994 }
995
996#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
997 if (pCryptoIf)
998 mParent->i_releaseCryptoIf(pCryptoIf);
999 if (pKey)
1000 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1001#endif
1002 }
1003
1004 if (SUCCEEDED(hrc))
1005 {
1006 /* Set mAccessible to TRUE only if we successfully locked and loaded
1007 * the settings file */
1008 mData->mAccessible = TRUE;
1009
1010 /* commit all changes made during loading the settings file */
1011 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1012 /// @todo r=klaus for some reason the settings loading logic backs up
1013 // the settings, and therefore a commit is needed. Should probably be changed.
1014 }
1015 else
1016 {
1017 /* If the machine is registered, then, instead of returning a
1018 * failure, we mark it as inaccessible and set the result to
1019 * success to give it a try later */
1020
1021 /* fetch the current error info */
1022 mData->mAccessError = com::ErrorInfo();
1023 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1024
1025 /* rollback all changes */
1026 i_rollback(false /* aNotify */);
1027
1028 // uninit media from this machine's media registry, or else
1029 // reloading the settings will fail
1030 mParent->i_unregisterMachineMedia(i_getId());
1031
1032 /* uninitialize the common part to make sure all data is reset to
1033 * default (null) values */
1034 uninitDataAndChildObjects();
1035
1036 hrc = S_OK;
1037 }
1038
1039 return hrc;
1040}
1041
1042/**
1043 * Uninitializes the instance.
1044 * Called either from FinalRelease() or by the parent when it gets destroyed.
1045 *
1046 * @note The caller of this method must make sure that this object
1047 * a) doesn't have active callers on the current thread and b) is not locked
1048 * by the current thread; otherwise uninit() will hang either a) due to
1049 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1050 * a dead-lock caused by this thread waiting for all callers on the other
1051 * threads are done but preventing them from doing so by holding a lock.
1052 */
1053void Machine::uninit()
1054{
1055 LogFlowThisFuncEnter();
1056
1057 Assert(!isWriteLockOnCurrentThread());
1058
1059 Assert(!uRegistryNeedsSaving);
1060 if (uRegistryNeedsSaving)
1061 {
1062 AutoCaller autoCaller(this);
1063 if (SUCCEEDED(autoCaller.hrc()))
1064 {
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1067 }
1068 }
1069
1070 /* Enclose the state transition Ready->InUninit->NotReady */
1071 AutoUninitSpan autoUninitSpan(this);
1072 if (autoUninitSpan.uninitDone())
1073 return;
1074
1075 Assert(!i_isSnapshotMachine());
1076 Assert(!i_isSessionMachine());
1077 Assert(!!mData);
1078
1079 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1080 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1081
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 if (!mData->mSession.mMachine.isNull())
1085 {
1086 /* Theoretically, this can only happen if the VirtualBox server has been
1087 * terminated while there were clients running that owned open direct
1088 * sessions. Since in this case we are definitely called by
1089 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1090 * won't happen on the client watcher thread (because it has a
1091 * VirtualBox caller for the duration of the
1092 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1093 * cannot happen until the VirtualBox caller is released). This is
1094 * important, because SessionMachine::uninit() cannot correctly operate
1095 * after we return from this method (it expects the Machine instance is
1096 * still valid). We'll call it ourselves below.
1097 */
1098 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1099 (SessionMachine*)mData->mSession.mMachine));
1100
1101 if (Global::IsOnlineOrTransient(mData->mMachineState))
1102 {
1103 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1104 /* set machine state using SessionMachine reimplementation */
1105 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1106 }
1107
1108 /*
1109 * Uninitialize SessionMachine using public uninit() to indicate
1110 * an unexpected uninitialization.
1111 */
1112 mData->mSession.mMachine->uninit();
1113 /* SessionMachine::uninit() must set mSession.mMachine to null */
1114 Assert(mData->mSession.mMachine.isNull());
1115 }
1116
1117 // uninit media from this machine's media registry, if they're still there
1118 Guid uuidMachine(i_getId());
1119
1120 /* the lock is no more necessary (SessionMachine is uninitialized) */
1121 alock.release();
1122
1123 /* XXX This will fail with
1124 * "cannot be closed because it is still attached to 1 virtual machines"
1125 * because at this point we did not call uninitDataAndChildObjects() yet
1126 * and therefore also removeBackReference() for all these media was not called! */
1127
1128 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1129 mParent->i_unregisterMachineMedia(uuidMachine);
1130
1131 // has machine been modified?
1132 if (mData->flModifications)
1133 {
1134 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1135 i_rollback(false /* aNotify */);
1136 }
1137
1138 if (mData->mAccessible)
1139 uninitDataAndChildObjects();
1140
1141#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1142 if (mData->mpKeyStore != NULL)
1143 delete mData->mpKeyStore;
1144#endif
1145
1146 /* free the essential data structure last */
1147 mData.free();
1148
1149 LogFlowThisFuncLeave();
1150}
1151
1152// Wrapped IMachine properties
1153/////////////////////////////////////////////////////////////////////////////
1154HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1155{
1156 /* mParent is constant during life time, no need to lock */
1157 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1158 aParent = pVirtualBox;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1164{
1165 /* mPlatform is constant during life time, no need to lock */
1166 ComObjPtr<Platform> pPlatform(mPlatform);
1167 aPlatform = pPlatform;
1168
1169 return S_OK;
1170}
1171
1172HRESULT Machine::getAccessible(BOOL *aAccessible)
1173{
1174 /* In some cases (medium registry related), it is necessary to be able to
1175 * go through the list of all machines. Happens when an inaccessible VM
1176 * has a sensible medium registry. */
1177 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 HRESULT hrc = S_OK;
1181
1182 if (!mData->mAccessible)
1183 {
1184 /* try to initialize the VM once more if not accessible */
1185
1186 AutoReinitSpan autoReinitSpan(this);
1187 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1188
1189#ifdef DEBUG
1190 LogFlowThisFunc(("Dumping media backreferences\n"));
1191 mParent->i_dumpAllBackRefs();
1192#endif
1193
1194 if (mData->pMachineConfigFile)
1195 {
1196 // reset the XML file to force loadSettings() (called from i_registeredInit())
1197 // to parse it again; the file might have changed
1198 delete mData->pMachineConfigFile;
1199 mData->pMachineConfigFile = NULL;
1200 }
1201
1202 hrc = i_registeredInit();
1203
1204 if (SUCCEEDED(hrc) && mData->mAccessible)
1205 {
1206 autoReinitSpan.setSucceeded();
1207
1208 /* make sure interesting parties will notice the accessibility
1209 * state change */
1210 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1211 mParent->i_onMachineDataChanged(mData->mUuid);
1212 }
1213 }
1214
1215 if (SUCCEEDED(hrc))
1216 *aAccessible = mData->mAccessible;
1217
1218 LogFlowThisFuncLeave();
1219
1220 return hrc;
1221}
1222
1223HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1224{
1225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1226
1227 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1228 {
1229 /* return shortly */
1230 aAccessError = NULL;
1231 return S_OK;
1232 }
1233
1234 HRESULT hrc = S_OK;
1235
1236 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1237 hrc = errorInfo.createObject();
1238 if (SUCCEEDED(hrc))
1239 {
1240 errorInfo->init(mData->mAccessError.getResultCode(),
1241 mData->mAccessError.getInterfaceID().ref(),
1242 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1243 Utf8Str(mData->mAccessError.getText()));
1244 aAccessError = errorInfo;
1245 }
1246
1247 return hrc;
1248}
1249
1250HRESULT Machine::getName(com::Utf8Str &aName)
1251{
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 aName = mUserData->s.strName;
1255
1256 return S_OK;
1257}
1258
1259HRESULT Machine::setName(const com::Utf8Str &aName)
1260{
1261 // prohibit setting a UUID only as the machine name, or else it can
1262 // never be found by findMachine()
1263 Guid test(aName);
1264
1265 if (test.isValid())
1266 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1267
1268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1271 if (FAILED(hrc)) return hrc;
1272
1273 i_setModified(IsModified_MachineData);
1274 mUserData.backup();
1275 mUserData->s.strName = aName;
1276
1277 return S_OK;
1278}
1279
1280HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1281{
1282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1283
1284 aDescription = mUserData->s.strDescription;
1285
1286 return S_OK;
1287}
1288
1289HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1290{
1291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1292
1293 // this can be done in principle in any state as it doesn't affect the VM
1294 // significantly, but play safe by not messing around while complex
1295 // activities are going on
1296 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1297 if (FAILED(hrc)) return hrc;
1298
1299 i_setModified(IsModified_MachineData);
1300 mUserData.backup();
1301 mUserData->s.strDescription = aDescription;
1302
1303 return S_OK;
1304}
1305
1306HRESULT Machine::getId(com::Guid &aId)
1307{
1308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1309
1310 aId = mData->mUuid;
1311
1312 return S_OK;
1313}
1314
1315HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1316{
1317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1318 aGroups.resize(mUserData->s.llGroups.size());
1319 size_t i = 0;
1320 for (StringsList::const_iterator
1321 it = mUserData->s.llGroups.begin();
1322 it != mUserData->s.llGroups.end();
1323 ++it, ++i)
1324 aGroups[i] = (*it);
1325
1326 return S_OK;
1327}
1328
1329HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1330{
1331 StringsList llGroups;
1332 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1333 if (FAILED(hrc))
1334 return hrc;
1335
1336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1339 if (FAILED(hrc)) return hrc;
1340
1341 i_setModified(IsModified_MachineData);
1342 mUserData.backup();
1343 mUserData->s.llGroups = llGroups;
1344
1345 mParent->i_onMachineGroupsChanged(mData->mUuid);
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 aOSTypeId = mUserData->s.strOsType;
1354
1355 return S_OK;
1356}
1357
1358HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1359{
1360 /* look up the object by Id to check it is valid */
1361 ComObjPtr<GuestOSType> pGuestOSType;
1362 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1363
1364 /* when setting, always use the "etalon" value for consistency -- lookup
1365 * by ID is case-insensitive and the input value may have different case */
1366 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1367
1368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1369
1370 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1371 if (FAILED(hrc)) return hrc;
1372
1373 i_setModified(IsModified_MachineData);
1374 mUserData.backup();
1375 mUserData->s.strOsType = osTypeId;
1376
1377 return S_OK;
1378}
1379
1380HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1381{
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1390{
1391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1394 if (FAILED(hrc)) return hrc;
1395
1396 i_setModified(IsModified_MachineData);
1397 mHWData.backup();
1398 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1404{
1405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 *aPointingHIDType = mHWData->mPointingHIDType;
1408
1409 return S_OK;
1410}
1411
1412HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1413{
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1417 if (FAILED(hrc)) return hrc;
1418
1419 i_setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mPointingHIDType = aPointingHIDType;
1422
1423 return S_OK;
1424}
1425
1426HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1427{
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 aParavirtDebug = mHWData->mParavirtDebug;
1431 return S_OK;
1432}
1433
1434HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1435{
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1439 if (FAILED(hrc)) return hrc;
1440
1441 /** @todo Parse/validate options? */
1442 if (aParavirtDebug != mHWData->mParavirtDebug)
1443 {
1444 i_setModified(IsModified_MachineData);
1445 mHWData.backup();
1446 mHWData->mParavirtDebug = aParavirtDebug;
1447 }
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aParavirtProvider = mHWData->mParavirtProvider;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1462{
1463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1464
1465 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1466 if (FAILED(hrc)) return hrc;
1467
1468 if (aParavirtProvider != mHWData->mParavirtProvider)
1469 {
1470 i_setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 mHWData->mParavirtProvider = aParavirtProvider;
1473 }
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1479{
1480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 *aParavirtProvider = mHWData->mParavirtProvider;
1483 switch (mHWData->mParavirtProvider)
1484 {
1485 case ParavirtProvider_None:
1486 case ParavirtProvider_HyperV:
1487 case ParavirtProvider_KVM:
1488 case ParavirtProvider_Minimal:
1489 break;
1490
1491 /* Resolve dynamic provider types to the effective types. */
1492 default:
1493 {
1494 ComObjPtr<GuestOSType> pGuestOSType;
1495 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1496 pGuestOSType);
1497 if (FAILED(hrc2) || pGuestOSType.isNull())
1498 {
1499 *aParavirtProvider = ParavirtProvider_None;
1500 break;
1501 }
1502
1503 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1504 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1505
1506 switch (mHWData->mParavirtProvider)
1507 {
1508 case ParavirtProvider_Legacy:
1509 {
1510 if (fOsXGuest)
1511 *aParavirtProvider = ParavirtProvider_Minimal;
1512 else
1513 *aParavirtProvider = ParavirtProvider_None;
1514 break;
1515 }
1516
1517 case ParavirtProvider_Default:
1518 {
1519 Assert(strlen(GUEST_OS_ID_STR_X64("")) > 0);
1520 if (fOsXGuest)
1521 *aParavirtProvider = ParavirtProvider_Minimal;
1522 else if ( mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows11")
1523 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows10")
1524 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows10")
1525 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows81")
1526 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows81")
1527 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows8")
1528 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows8")
1529 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows7")
1530 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows7")
1531 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("WindowsVista")
1532 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("WindowsVista")
1533 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1534 || mUserData->s.strOsType.startsWith("Windows201"))
1535 && mUserData->s.strOsType.endsWith(GUEST_OS_ID_STR_X64("")))
1536 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2012")
1537 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2012")
1538 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2008")
1539 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2008"))
1540 *aParavirtProvider = ParavirtProvider_HyperV;
1541 else if ( guestTypeFamilyId == "Linux"
1542 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux22") // Linux22 and Linux24{_64} excluded as they're too old
1543 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux24") // to have any KVM paravirtualization support.
1544 && mUserData->s.strOsType != GUEST_OS_ID_STR_X64("Linux24"))
1545 *aParavirtProvider = ParavirtProvider_KVM;
1546 else
1547 *aParavirtProvider = ParavirtProvider_None;
1548 break;
1549 }
1550
1551 default: AssertFailedBreak(); /* Shut up MSC. */
1552 }
1553 break;
1554 }
1555 }
1556
1557 Assert( *aParavirtProvider == ParavirtProvider_None
1558 || *aParavirtProvider == ParavirtProvider_Minimal
1559 || *aParavirtProvider == ParavirtProvider_HyperV
1560 || *aParavirtProvider == ParavirtProvider_KVM);
1561 return S_OK;
1562}
1563
1564HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1565{
1566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 aHardwareVersion = mHWData->mHWVersion;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1574{
1575 /* check known version */
1576 Utf8Str hwVersion = aHardwareVersion;
1577 if ( hwVersion.compare("1") != 0
1578 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1579 return setError(E_INVALIDARG,
1580 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1581
1582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1585 if (FAILED(hrc)) return hrc;
1586
1587 i_setModified(IsModified_MachineData);
1588 mHWData.backup();
1589 mHWData->mHWVersion = aHardwareVersion;
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1595{
1596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 if (!mHWData->mHardwareUUID.isZero())
1599 aHardwareUUID = mHWData->mHardwareUUID;
1600 else
1601 aHardwareUUID = mData->mUuid;
1602
1603 return S_OK;
1604}
1605
1606HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1607{
1608 if (!aHardwareUUID.isValid())
1609 return E_INVALIDARG;
1610
1611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1612
1613 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1614 if (FAILED(hrc)) return hrc;
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618 if (aHardwareUUID == mData->mUuid)
1619 mHWData->mHardwareUUID.clear();
1620 else
1621 mHWData->mHardwareUUID = aHardwareUUID;
1622
1623 return S_OK;
1624}
1625
1626HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1627{
1628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 *aMemorySize = mHWData->mMemorySize;
1631
1632 return S_OK;
1633}
1634
1635HRESULT Machine::setMemorySize(ULONG aMemorySize)
1636{
1637 /* check RAM limits */
1638 if ( aMemorySize < MM_RAM_MIN_IN_MB
1639 || aMemorySize > MM_RAM_MAX_IN_MB
1640 )
1641 return setError(E_INVALIDARG,
1642 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1643 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1644
1645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (FAILED(hrc)) return hrc;
1649
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 mHWData->mMemorySize = aMemorySize;
1653
1654 return S_OK;
1655}
1656
1657HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1658{
1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1660
1661 *aCPUCount = mHWData->mCPUCount;
1662
1663 return S_OK;
1664}
1665
1666HRESULT Machine::setCPUCount(ULONG aCPUCount)
1667{
1668 /* check CPU limits */
1669 if ( aCPUCount < SchemaDefs::MinCPUCount
1670 || aCPUCount > SchemaDefs::MaxCPUCount
1671 )
1672 return setError(E_INVALIDARG,
1673 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1674 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1675
1676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1679 if (mHWData->mCPUHotPlugEnabled)
1680 {
1681 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1682 {
1683 if (mHWData->mCPUAttached[idx])
1684 return setError(E_INVALIDARG,
1685 tr("There is still a CPU attached to socket %lu."
1686 "Detach the CPU before removing the socket"),
1687 aCPUCount, idx+1);
1688 }
1689 }
1690
1691 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1692 if (FAILED(hrc)) return hrc;
1693
1694 i_setModified(IsModified_MachineData);
1695 mHWData.backup();
1696 mHWData->mCPUCount = aCPUCount;
1697
1698 return S_OK;
1699}
1700
1701HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1702{
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1706
1707 return S_OK;
1708}
1709
1710HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1711{
1712 /* check throttle limits */
1713 if ( aCPUExecutionCap < 1
1714 || aCPUExecutionCap > 100
1715 )
1716 return setError(E_INVALIDARG,
1717 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1718 aCPUExecutionCap, 1, 100);
1719
1720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1723 if (FAILED(hrc)) return hrc;
1724
1725 alock.release();
1726 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1727 alock.acquire();
1728 if (FAILED(hrc)) return hrc;
1729
1730 i_setModified(IsModified_MachineData);
1731 mHWData.backup();
1732 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1733
1734 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1735 if (Global::IsOnline(mData->mMachineState))
1736 i_saveSettings(NULL, alock);
1737
1738 return S_OK;
1739}
1740
1741HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1742{
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1746
1747 return S_OK;
1748}
1749
1750HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1751{
1752 HRESULT hrc = S_OK;
1753
1754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1755
1756 hrc = i_checkStateDependency(MutableStateDep);
1757 if (FAILED(hrc)) return hrc;
1758
1759 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1760 {
1761 if (aCPUHotPlugEnabled)
1762 {
1763 i_setModified(IsModified_MachineData);
1764 mHWData.backup();
1765
1766 /* Add the amount of CPUs currently attached */
1767 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1768 mHWData->mCPUAttached[i] = true;
1769 }
1770 else
1771 {
1772 /*
1773 * We can disable hotplug only if the amount of maximum CPUs is equal
1774 * to the amount of attached CPUs
1775 */
1776 unsigned cCpusAttached = 0;
1777 unsigned iHighestId = 0;
1778
1779 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1780 {
1781 if (mHWData->mCPUAttached[i])
1782 {
1783 cCpusAttached++;
1784 iHighestId = i;
1785 }
1786 }
1787
1788 if ( (cCpusAttached != mHWData->mCPUCount)
1789 || (iHighestId >= mHWData->mCPUCount))
1790 return setError(E_INVALIDARG,
1791 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1792
1793 i_setModified(IsModified_MachineData);
1794 mHWData.backup();
1795 }
1796 }
1797
1798 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1799
1800 return hrc;
1801}
1802
1803HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1804{
1805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1808
1809 return S_OK;
1810}
1811
1812HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1813{
1814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1815
1816 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1817 if (SUCCEEDED(hrc))
1818 {
1819 i_setModified(IsModified_MachineData);
1820 mHWData.backup();
1821 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1822 }
1823 return hrc;
1824}
1825
1826HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1827{
1828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1829 aCPUProfile = mHWData->mCpuProfile;
1830 return S_OK;
1831}
1832
1833HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1834{
1835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1836 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1837 if (SUCCEEDED(hrc))
1838 {
1839 i_setModified(IsModified_MachineData);
1840 mHWData.backup();
1841 /* Empty equals 'host'. */
1842 if (aCPUProfile.isNotEmpty())
1843 mHWData->mCpuProfile = aCPUProfile;
1844 else
1845 mHWData->mCpuProfile = "host";
1846 }
1847 return hrc;
1848}
1849
1850HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1851{
1852#ifdef VBOX_WITH_USB_CARDREADER
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854
1855 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1856
1857 return S_OK;
1858#else
1859 NOREF(aEmulatedUSBCardReaderEnabled);
1860 return E_NOTIMPL;
1861#endif
1862}
1863
1864HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1865{
1866#ifdef VBOX_WITH_USB_CARDREADER
1867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1870 if (FAILED(hrc)) return hrc;
1871
1872 i_setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1875
1876 return S_OK;
1877#else
1878 NOREF(aEmulatedUSBCardReaderEnabled);
1879 return E_NOTIMPL;
1880#endif
1881}
1882
1883/** @todo this method should not be public */
1884HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1889
1890 return S_OK;
1891}
1892
1893/**
1894 * Set the memory balloon size.
1895 *
1896 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1897 * we have to make sure that we never call IGuest from here.
1898 */
1899HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1900{
1901 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1902#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1903 /* check limits */
1904 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1905 return setError(E_INVALIDARG,
1906 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1907 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1908
1909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1910
1911 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1912 if (FAILED(hrc)) return hrc;
1913
1914 i_setModified(IsModified_MachineData);
1915 mHWData.backup();
1916 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1917
1918 return S_OK;
1919#else
1920 NOREF(aMemoryBalloonSize);
1921 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1922#endif
1923}
1924
1925HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1930 return S_OK;
1931}
1932
1933HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1934{
1935#ifdef VBOX_WITH_PAGE_SHARING
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1939 if (FAILED(hrc)) return hrc;
1940
1941 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1942 i_setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1945 return S_OK;
1946#else
1947 NOREF(aPageFusionEnabled);
1948 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1949#endif
1950}
1951
1952HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1953{
1954 /* mFirmwareSettings is constant during life time, no need to lock */
1955 aFirmwareSettings = mFirmwareSettings;
1956
1957 return S_OK;
1958}
1959
1960HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1961{
1962 /* mTrustedPlatformModule is constant during life time, no need to lock */
1963 aTrustedPlatformModule = mTrustedPlatformModule;
1964
1965 return S_OK;
1966}
1967
1968HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1969{
1970 /* mNvramStore is constant during life time, no need to lock */
1971 aNvramStore = mNvramStore;
1972
1973 return S_OK;
1974}
1975
1976HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1977{
1978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1979
1980 aRecordingSettings = mRecordingSettings;
1981
1982 return S_OK;
1983}
1984
1985HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1986{
1987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1988
1989 aGraphicsAdapter = mGraphicsAdapter;
1990
1991 return S_OK;
1992}
1993
1994HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
1995{
1996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
1999
2000 return S_OK;
2001}
2002
2003HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2004{
2005 /** @todo (r=dmik):
2006 * 1. Allow to change the name of the snapshot folder containing snapshots
2007 * 2. Rename the folder on disk instead of just changing the property
2008 * value (to be smart and not to leave garbage). Note that it cannot be
2009 * done here because the change may be rolled back. Thus, the right
2010 * place is #saveSettings().
2011 */
2012
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(hrc)) return hrc;
2017
2018 if (!mData->mCurrentSnapshot.isNull())
2019 return setError(E_FAIL,
2020 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2021
2022 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2023
2024 if (strSnapshotFolder.isEmpty())
2025 strSnapshotFolder = "Snapshots";
2026 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2027 if (RT_FAILURE(vrc))
2028 return setErrorBoth(E_FAIL, vrc,
2029 tr("Invalid snapshot folder '%s' (%Rrc)"),
2030 strSnapshotFolder.c_str(), vrc);
2031
2032 i_setModified(IsModified_MachineData);
2033 mUserData.backup();
2034
2035 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2036
2037 return S_OK;
2038}
2039
2040HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2041{
2042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 aMediumAttachments.resize(mMediumAttachments->size());
2045 size_t i = 0;
2046 for (MediumAttachmentList::const_iterator
2047 it = mMediumAttachments->begin();
2048 it != mMediumAttachments->end();
2049 ++it, ++i)
2050 aMediumAttachments[i] = *it;
2051
2052 return S_OK;
2053}
2054
2055HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 Assert(!!mVRDEServer);
2060
2061 aVRDEServer = mVRDEServer;
2062
2063 return S_OK;
2064}
2065
2066HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2067{
2068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 aAudioSettings = mAudioSettings;
2071
2072 return S_OK;
2073}
2074
2075HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2076{
2077#ifdef VBOX_WITH_VUSB
2078 clearError();
2079 MultiResult hrcMult(S_OK);
2080
2081# ifdef VBOX_WITH_USB
2082 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2083 if (FAILED(hrcMult)) return hrcMult;
2084# endif
2085
2086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 aUSBControllers.resize(mUSBControllers->size());
2089 size_t i = 0;
2090 for (USBControllerList::const_iterator
2091 it = mUSBControllers->begin();
2092 it != mUSBControllers->end();
2093 ++it, ++i)
2094 aUSBControllers[i] = *it;
2095
2096 return S_OK;
2097#else
2098 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2099 * extended error info to indicate that USB is simply not available
2100 * (w/o treating it as a failure), for example, as in OSE */
2101 NOREF(aUSBControllers);
2102 ReturnComNotImplemented();
2103#endif /* VBOX_WITH_VUSB */
2104}
2105
2106HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2107{
2108#ifdef VBOX_WITH_VUSB
2109 clearError();
2110 MultiResult hrcMult(S_OK);
2111
2112# ifdef VBOX_WITH_USB
2113 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2114 if (FAILED(hrcMult)) return hrcMult;
2115# endif
2116
2117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2118
2119 aUSBDeviceFilters = mUSBDeviceFilters;
2120 return hrcMult;
2121#else
2122 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2123 * extended error info to indicate that USB is simply not available
2124 * (w/o treating it as a failure), for example, as in OSE */
2125 NOREF(aUSBDeviceFilters);
2126 ReturnComNotImplemented();
2127#endif /* VBOX_WITH_VUSB */
2128}
2129
2130HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2131{
2132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 aSettingsFilePath = mData->m_strConfigFileFull;
2135
2136 return S_OK;
2137}
2138
2139HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2140{
2141 RT_NOREF(aSettingsFilePath);
2142 ReturnComNotImplemented();
2143}
2144
2145HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2150 if (FAILED(hrc)) return hrc;
2151
2152 if (!mData->pMachineConfigFile->fileExists())
2153 // this is a new machine, and no config file exists yet:
2154 *aSettingsModified = TRUE;
2155 else
2156 *aSettingsModified = (mData->flModifications != 0);
2157
2158 return S_OK;
2159}
2160
2161HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2162{
2163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 *aSessionState = mData->mSession.mState;
2166
2167 return S_OK;
2168}
2169
2170HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2171{
2172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 aSessionName = mData->mSession.mName;
2175
2176 return S_OK;
2177}
2178
2179HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2180{
2181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2182
2183 *aSessionPID = mData->mSession.mPID;
2184
2185 return S_OK;
2186}
2187
2188HRESULT Machine::getState(MachineState_T *aState)
2189{
2190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2191
2192 *aState = mData->mMachineState;
2193 Assert(mData->mMachineState != MachineState_Null);
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2199{
2200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2203
2204 return S_OK;
2205}
2206
2207HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2208{
2209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 aStateFilePath = mSSData->strStateFilePath;
2212
2213 return S_OK;
2214}
2215
2216HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2217{
2218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2219
2220 i_getLogFolder(aLogFolder);
2221
2222 return S_OK;
2223}
2224
2225HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2226{
2227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2228
2229 aCurrentSnapshot = mData->mCurrentSnapshot;
2230
2231 return S_OK;
2232}
2233
2234HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2235{
2236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2237
2238 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2239 ? 0
2240 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2241
2242 return S_OK;
2243}
2244
2245HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2246{
2247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2248
2249 /* Note: for machines with no snapshots, we always return FALSE
2250 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2251 * reasons :) */
2252
2253 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2254 ? FALSE
2255 : mData->mCurrentStateModified;
2256
2257 return S_OK;
2258}
2259
2260HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2261{
2262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2263
2264 aSharedFolders.resize(mHWData->mSharedFolders.size());
2265 size_t i = 0;
2266 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2267 it = mHWData->mSharedFolders.begin();
2268 it != mHWData->mSharedFolders.end();
2269 ++it, ++i)
2270 aSharedFolders[i] = *it;
2271
2272 return S_OK;
2273}
2274
2275HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2276{
2277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2278
2279 *aClipboardMode = mHWData->mClipboardMode;
2280
2281 return S_OK;
2282}
2283
2284HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2285{
2286 HRESULT hrc = S_OK;
2287
2288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2289
2290 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2291 if (FAILED(hrc)) return hrc;
2292
2293 alock.release();
2294 hrc = i_onClipboardModeChange(aClipboardMode);
2295 alock.acquire();
2296 if (FAILED(hrc)) return hrc;
2297
2298 i_setModified(IsModified_MachineData);
2299 mHWData.backup();
2300 mHWData->mClipboardMode = aClipboardMode;
2301
2302 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2303 if (Global::IsOnline(mData->mMachineState))
2304 i_saveSettings(NULL, alock);
2305
2306 return S_OK;
2307}
2308
2309HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2310{
2311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2312
2313 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2314
2315 return S_OK;
2316}
2317
2318HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2319{
2320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2321
2322 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2323 if (FAILED(hrc)) return hrc;
2324
2325 alock.release();
2326 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2327 alock.acquire();
2328 if (FAILED(hrc)) return hrc;
2329
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2333
2334 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2335 if (Global::IsOnline(mData->mMachineState))
2336 i_saveSettings(NULL, alock);
2337
2338 return S_OK;
2339}
2340
2341HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2342{
2343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 *aDnDMode = mHWData->mDnDMode;
2346
2347 return S_OK;
2348}
2349
2350HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2351{
2352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2353
2354 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2355 if (FAILED(hrc)) return hrc;
2356
2357 alock.release();
2358 hrc = i_onDnDModeChange(aDnDMode);
2359
2360 alock.acquire();
2361 if (FAILED(hrc)) return hrc;
2362
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 mHWData->mDnDMode = aDnDMode;
2366
2367 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2368 if (Global::IsOnline(mData->mMachineState))
2369 i_saveSettings(NULL, alock);
2370
2371 return S_OK;
2372}
2373
2374HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2375{
2376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2377
2378 aStorageControllers.resize(mStorageControllers->size());
2379 size_t i = 0;
2380 for (StorageControllerList::const_iterator
2381 it = mStorageControllers->begin();
2382 it != mStorageControllers->end();
2383 ++it, ++i)
2384 aStorageControllers[i] = *it;
2385
2386 return S_OK;
2387}
2388
2389HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2390{
2391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2392
2393 *aEnabled = mUserData->s.fTeleporterEnabled;
2394
2395 return S_OK;
2396}
2397
2398HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2399{
2400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2401
2402 /* Only allow it to be set to true when PoweredOff or Aborted.
2403 (Clearing it is always permitted.) */
2404 if ( aTeleporterEnabled
2405 && mData->mRegistered
2406 && ( !i_isSessionMachine()
2407 || ( mData->mMachineState != MachineState_PoweredOff
2408 && mData->mMachineState != MachineState_Teleported
2409 && mData->mMachineState != MachineState_Aborted
2410 )
2411 )
2412 )
2413 return setError(VBOX_E_INVALID_VM_STATE,
2414 tr("The machine is not powered off (state is %s)"),
2415 Global::stringifyMachineState(mData->mMachineState));
2416
2417 i_setModified(IsModified_MachineData);
2418 mUserData.backup();
2419 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2420
2421 return S_OK;
2422}
2423
2424HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2425{
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2429
2430 return S_OK;
2431}
2432
2433HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2434{
2435 if (aTeleporterPort >= _64K)
2436 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2437
2438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2439
2440 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2441 if (FAILED(hrc)) return hrc;
2442
2443 i_setModified(IsModified_MachineData);
2444 mUserData.backup();
2445 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2446
2447 return S_OK;
2448}
2449
2450HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2455
2456 return S_OK;
2457}
2458
2459HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2460{
2461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2462
2463 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2464 if (FAILED(hrc)) return hrc;
2465
2466 i_setModified(IsModified_MachineData);
2467 mUserData.backup();
2468 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2469
2470 return S_OK;
2471}
2472
2473HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2474{
2475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2476 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2477
2478 return S_OK;
2479}
2480
2481HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2482{
2483 /*
2484 * Hash the password first.
2485 */
2486 com::Utf8Str aT = aTeleporterPassword;
2487
2488 if (!aT.isEmpty())
2489 {
2490 if (VBoxIsPasswordHashed(&aT))
2491 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2492 VBoxHashPassword(&aT);
2493 }
2494
2495 /*
2496 * Do the update.
2497 */
2498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2499 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2500 if (SUCCEEDED(hrc))
2501 {
2502 i_setModified(IsModified_MachineData);
2503 mUserData.backup();
2504 mUserData->s.strTeleporterPassword = aT;
2505 }
2506
2507 return hrc;
2508}
2509
2510HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2515
2516 return S_OK;
2517}
2518
2519HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2520{
2521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2522
2523 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2524 if (FAILED(hrc)) return hrc;
2525
2526 i_setModified(IsModified_MachineData);
2527 mHWData.backup();
2528 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2529
2530 return S_OK;
2531}
2532
2533HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 *aIOCacheSize = mHWData->mIOCacheSize;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2543{
2544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2547 if (FAILED(hrc)) return hrc;
2548
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mIOCacheSize = aIOCacheSize;
2552
2553 return S_OK;
2554}
2555
2556HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2557{
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2561 aKeyId = mSSData->strStateKeyId;
2562#else
2563 aKeyId = com::Utf8Str::Empty;
2564#endif
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2574 aKeyStore = mSSData->strStateKeyStore;
2575#else
2576 aKeyStore = com::Utf8Str::Empty;
2577#endif
2578
2579 return S_OK;
2580}
2581
2582HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2583{
2584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2585
2586#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2587 aKeyId = mData->mstrLogKeyId;
2588#else
2589 aKeyId = com::Utf8Str::Empty;
2590#endif
2591
2592 return S_OK;
2593}
2594
2595HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2596{
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2600 aKeyStore = mData->mstrLogKeyStore;
2601#else
2602 aKeyStore = com::Utf8Str::Empty;
2603#endif
2604
2605 return S_OK;
2606}
2607
2608HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2609{
2610 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2611
2612 return S_OK;
2613}
2614
2615
2616/**
2617 * @note Locks objects!
2618 */
2619HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2620 LockType_T aLockType)
2621{
2622 /* check the session state */
2623 SessionState_T state;
2624 HRESULT hrc = aSession->COMGETTER(State)(&state);
2625 if (FAILED(hrc)) return hrc;
2626
2627 if (state != SessionState_Unlocked)
2628 return setError(VBOX_E_INVALID_OBJECT_STATE,
2629 tr("The given session is busy"));
2630
2631 // get the client's IInternalSessionControl interface
2632 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2633 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2634 E_INVALIDARG);
2635
2636 // session name (only used in some code paths)
2637 Utf8Str strSessionName;
2638
2639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 if (!mData->mRegistered)
2642 return setError(E_UNEXPECTED,
2643 tr("The machine '%s' is not registered"),
2644 mUserData->s.strName.c_str());
2645
2646 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2647
2648 SessionState_T oldState = mData->mSession.mState;
2649 /* Hack: in case the session is closing and there is a progress object
2650 * which allows waiting for the session to be closed, take the opportunity
2651 * and do a limited wait (max. 1 second). This helps a lot when the system
2652 * is busy and thus session closing can take a little while. */
2653 if ( mData->mSession.mState == SessionState_Unlocking
2654 && mData->mSession.mProgress)
2655 {
2656 alock.release();
2657 mData->mSession.mProgress->WaitForCompletion(1000);
2658 alock.acquire();
2659 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2660 }
2661
2662 // try again now
2663 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2664 // (i.e. session machine exists)
2665 && (aLockType == LockType_Shared) // caller wants a shared link to the
2666 // existing session that holds the write lock:
2667 )
2668 {
2669 // OK, share the session... we are now dealing with three processes:
2670 // 1) VBoxSVC (where this code runs);
2671 // 2) process C: the caller's client process (who wants a shared session);
2672 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2673
2674 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2675 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2676 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2677 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2678 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2679
2680 /*
2681 * Release the lock before calling the client process. It's safe here
2682 * since the only thing to do after we get the lock again is to add
2683 * the remote control to the list (which doesn't directly influence
2684 * anything).
2685 */
2686 alock.release();
2687
2688 // get the console of the session holding the write lock (this is a remote call)
2689 ComPtr<IConsole> pConsoleW;
2690 if (mData->mSession.mLockType == LockType_VM)
2691 {
2692 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2693 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2694 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2695 if (FAILED(hrc))
2696 // the failure may occur w/o any error info (from RPC), so provide one
2697 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2698 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2699 }
2700
2701 // share the session machine and W's console with the caller's session
2702 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2703 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2704 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2705
2706 if (FAILED(hrc))
2707 // the failure may occur w/o any error info (from RPC), so provide one
2708 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2709 alock.acquire();
2710
2711 // need to revalidate the state after acquiring the lock again
2712 if (mData->mSession.mState != SessionState_Locked)
2713 {
2714 pSessionControl->Uninitialize();
2715 return setError(VBOX_E_INVALID_SESSION_STATE,
2716 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2717 mUserData->s.strName.c_str());
2718 }
2719
2720 // add the caller's session to the list
2721 mData->mSession.mRemoteControls.push_back(pSessionControl);
2722 }
2723 else if ( mData->mSession.mState == SessionState_Locked
2724 || mData->mSession.mState == SessionState_Unlocking
2725 )
2726 {
2727 // sharing not permitted, or machine still unlocking:
2728 return setError(VBOX_E_INVALID_OBJECT_STATE,
2729 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2730 mUserData->s.strName.c_str());
2731 }
2732 else
2733 {
2734 // machine is not locked: then write-lock the machine (create the session machine)
2735
2736 // must not be busy
2737 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2738
2739 // get the caller's session PID
2740 RTPROCESS pid = NIL_RTPROCESS;
2741 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2742 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2743 Assert(pid != NIL_RTPROCESS);
2744
2745 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2746
2747 if (fLaunchingVMProcess)
2748 {
2749 if (mData->mSession.mPID == NIL_RTPROCESS)
2750 {
2751 // two or more clients racing for a lock, the one which set the
2752 // session state to Spawning will win, the others will get an
2753 // error as we can't decide here if waiting a little would help
2754 // (only for shared locks this would avoid an error)
2755 return setError(VBOX_E_INVALID_OBJECT_STATE,
2756 tr("The machine '%s' already has a lock request pending"),
2757 mUserData->s.strName.c_str());
2758 }
2759
2760 // this machine is awaiting for a spawning session to be opened:
2761 // then the calling process must be the one that got started by
2762 // LaunchVMProcess()
2763
2764 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2765 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2766
2767#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2768 /* Hardened windows builds spawns three processes when a VM is
2769 launched, the 3rd one is the one that will end up here. */
2770 RTPROCESS pidParent;
2771 int vrc = RTProcQueryParent(pid, &pidParent);
2772 if (RT_SUCCESS(vrc))
2773 vrc = RTProcQueryParent(pidParent, &pidParent);
2774 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2775 || vrc == VERR_ACCESS_DENIED)
2776 {
2777 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2778 mData->mSession.mPID = pid;
2779 }
2780#endif
2781
2782 if (mData->mSession.mPID != pid)
2783 return setError(E_ACCESSDENIED,
2784 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2785 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2786 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2787 }
2788
2789 // create the mutable SessionMachine from the current machine
2790 ComObjPtr<SessionMachine> sessionMachine;
2791 sessionMachine.createObject();
2792 hrc = sessionMachine->init(this);
2793 AssertComRC(hrc);
2794
2795 /* NOTE: doing return from this function after this point but
2796 * before the end is forbidden since it may call SessionMachine::uninit()
2797 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2798 * lock while still holding the Machine lock in alock so that a deadlock
2799 * is possible due to the wrong lock order. */
2800
2801 if (SUCCEEDED(hrc))
2802 {
2803 /*
2804 * Set the session state to Spawning to protect against subsequent
2805 * attempts to open a session and to unregister the machine after
2806 * we release the lock.
2807 */
2808 SessionState_T origState = mData->mSession.mState;
2809 mData->mSession.mState = SessionState_Spawning;
2810
2811#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2812 /* Get the client token ID to be passed to the client process */
2813 Utf8Str strTokenId;
2814 sessionMachine->i_getTokenId(strTokenId);
2815 Assert(!strTokenId.isEmpty());
2816#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2817 /* Get the client token to be passed to the client process */
2818 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2819 /* The token is now "owned" by pToken, fix refcount */
2820 if (!pToken.isNull())
2821 pToken->Release();
2822#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2823
2824 /*
2825 * Release the lock before calling the client process -- it will call
2826 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2827 * because the state is Spawning, so that LaunchVMProcess() and
2828 * LockMachine() calls will fail. This method, called before we
2829 * acquire the lock again, will fail because of the wrong PID.
2830 *
2831 * Note that mData->mSession.mRemoteControls accessed outside
2832 * the lock may not be modified when state is Spawning, so it's safe.
2833 */
2834 alock.release();
2835
2836 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2837#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2838 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2839#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2840 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2841 /* Now the token is owned by the client process. */
2842 pToken.setNull();
2843#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2844 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2845
2846 /* The failure may occur w/o any error info (from RPC), so provide one */
2847 if (FAILED(hrc))
2848 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2849
2850 // get session name, either to remember or to compare against
2851 // the already known session name.
2852 {
2853 Bstr bstrSessionName;
2854 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2855 if (SUCCEEDED(hrc2))
2856 strSessionName = bstrSessionName;
2857 }
2858
2859 if ( SUCCEEDED(hrc)
2860 && fLaunchingVMProcess
2861 )
2862 {
2863 /* complete the remote session initialization */
2864
2865 /* get the console from the direct session */
2866 ComPtr<IConsole> console;
2867 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2868 ComAssertComRC(hrc);
2869
2870 if (SUCCEEDED(hrc) && !console)
2871 {
2872 ComAssert(!!console);
2873 hrc = E_FAIL;
2874 }
2875
2876 /* assign machine & console to the remote session */
2877 if (SUCCEEDED(hrc))
2878 {
2879 /*
2880 * after LaunchVMProcess(), the first and the only
2881 * entry in remoteControls is that remote session
2882 */
2883 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2884 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2885 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2886
2887 /* The failure may occur w/o any error info (from RPC), so provide one */
2888 if (FAILED(hrc))
2889 setError(VBOX_E_VM_ERROR,
2890 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2891 }
2892
2893 if (FAILED(hrc))
2894 pSessionControl->Uninitialize();
2895 }
2896
2897 /* acquire the lock again */
2898 alock.acquire();
2899
2900 /* Restore the session state */
2901 mData->mSession.mState = origState;
2902 }
2903
2904 // finalize spawning anyway (this is why we don't return on errors above)
2905 if (fLaunchingVMProcess)
2906 {
2907 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2908 /* Note that the progress object is finalized later */
2909 /** @todo Consider checking mData->mSession.mProgress for cancellation
2910 * around here. */
2911
2912 /* We don't reset mSession.mPID here because it is necessary for
2913 * SessionMachine::uninit() to reap the child process later. */
2914
2915 if (FAILED(hrc))
2916 {
2917 /* Close the remote session, remove the remote control from the list
2918 * and reset session state to Closed (@note keep the code in sync
2919 * with the relevant part in checkForSpawnFailure()). */
2920
2921 Assert(mData->mSession.mRemoteControls.size() == 1);
2922 if (mData->mSession.mRemoteControls.size() == 1)
2923 {
2924 ErrorInfoKeeper eik;
2925 mData->mSession.mRemoteControls.front()->Uninitialize();
2926 }
2927
2928 mData->mSession.mRemoteControls.clear();
2929 mData->mSession.mState = SessionState_Unlocked;
2930 }
2931 }
2932 else
2933 {
2934 /* memorize PID of the directly opened session */
2935 if (SUCCEEDED(hrc))
2936 mData->mSession.mPID = pid;
2937 }
2938
2939 if (SUCCEEDED(hrc))
2940 {
2941 mData->mSession.mLockType = aLockType;
2942 /* memorize the direct session control and cache IUnknown for it */
2943 mData->mSession.mDirectControl = pSessionControl;
2944 mData->mSession.mState = SessionState_Locked;
2945 if (!fLaunchingVMProcess)
2946 mData->mSession.mName = strSessionName;
2947 /* associate the SessionMachine with this Machine */
2948 mData->mSession.mMachine = sessionMachine;
2949
2950 /* request an IUnknown pointer early from the remote party for later
2951 * identity checks (it will be internally cached within mDirectControl
2952 * at least on XPCOM) */
2953 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2954 NOREF(unk);
2955
2956#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2957 if (aLockType == LockType_VM)
2958 {
2959 /* get the console from the direct session */
2960 ComPtr<IConsole> console;
2961 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2962 ComAssertComRC(hrc);
2963 /* send passswords to console */
2964 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2965 it != mData->mpKeyStore->end();
2966 ++it)
2967 {
2968 SecretKey *pKey = it->second;
2969 pKey->retain();
2970 console->AddEncryptionPassword(Bstr(it->first).raw(),
2971 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2972 TRUE);
2973 pKey->release();
2974 }
2975
2976 }
2977#endif
2978 }
2979
2980 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2981 * would break the lock order */
2982 alock.release();
2983
2984 /* uninitialize the created session machine on failure */
2985 if (FAILED(hrc))
2986 sessionMachine->uninit();
2987 }
2988
2989 if (SUCCEEDED(hrc))
2990 {
2991 /*
2992 * tell the client watcher thread to update the set of
2993 * machines that have open sessions
2994 */
2995 mParent->i_updateClientWatcher();
2996
2997 if (oldState != SessionState_Locked)
2998 /* fire an event */
2999 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3000 }
3001
3002 return hrc;
3003}
3004
3005/**
3006 * @note Locks objects!
3007 */
3008HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3009 const com::Utf8Str &aName,
3010 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3011 ComPtr<IProgress> &aProgress)
3012{
3013 Utf8Str strFrontend(aName);
3014 /* "emergencystop" doesn't need the session, so skip the checks/interface
3015 * retrieval. This code doesn't quite fit in here, but introducing a
3016 * special API method would be even more effort, and would require explicit
3017 * support by every API client. It's better to hide the feature a bit. */
3018 if (strFrontend != "emergencystop")
3019 CheckComArgNotNull(aSession);
3020
3021 HRESULT hrc = S_OK;
3022 if (strFrontend.isEmpty())
3023 {
3024 Bstr bstrFrontend;
3025 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3026 if (FAILED(hrc))
3027 return hrc;
3028 strFrontend = bstrFrontend;
3029 if (strFrontend.isEmpty())
3030 {
3031 ComPtr<ISystemProperties> systemProperties;
3032 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3033 if (FAILED(hrc))
3034 return hrc;
3035 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3036 if (FAILED(hrc))
3037 return hrc;
3038 strFrontend = bstrFrontend;
3039 }
3040 /* paranoia - emergencystop is not a valid default */
3041 if (strFrontend == "emergencystop")
3042 strFrontend = Utf8Str::Empty;
3043 }
3044 /* default frontend: Qt GUI */
3045 if (strFrontend.isEmpty())
3046 strFrontend = "GUI/Qt";
3047
3048 if (strFrontend != "emergencystop")
3049 {
3050 /* check the session state */
3051 SessionState_T state;
3052 hrc = aSession->COMGETTER(State)(&state);
3053 if (FAILED(hrc))
3054 return hrc;
3055
3056 if (state != SessionState_Unlocked)
3057 return setError(VBOX_E_INVALID_OBJECT_STATE,
3058 tr("The given session is busy"));
3059
3060 /* get the IInternalSessionControl interface */
3061 ComPtr<IInternalSessionControl> control(aSession);
3062 ComAssertMsgRet(!control.isNull(),
3063 ("No IInternalSessionControl interface"),
3064 E_INVALIDARG);
3065
3066 /* get the teleporter enable state for the progress object init. */
3067 BOOL fTeleporterEnabled;
3068 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3069 if (FAILED(hrc))
3070 return hrc;
3071
3072 /* create a progress object */
3073 ComObjPtr<ProgressProxy> progress;
3074 progress.createObject();
3075 hrc = progress->init(mParent,
3076 static_cast<IMachine*>(this),
3077 Bstr(tr("Starting VM")).raw(),
3078 TRUE /* aCancelable */,
3079 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3080 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3081 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3082 2 /* uFirstOperationWeight */,
3083 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3084 if (SUCCEEDED(hrc))
3085 {
3086 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3087 if (SUCCEEDED(hrc))
3088 {
3089 aProgress = progress;
3090
3091 /* signal the client watcher thread */
3092 mParent->i_updateClientWatcher();
3093
3094 /* fire an event */
3095 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3096 }
3097 }
3098 }
3099 else
3100 {
3101 /* no progress object - either instant success or failure */
3102 aProgress = NULL;
3103
3104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3105
3106 if (mData->mSession.mState != SessionState_Locked)
3107 return setError(VBOX_E_INVALID_OBJECT_STATE,
3108 tr("The machine '%s' is not locked by a session"),
3109 mUserData->s.strName.c_str());
3110
3111 /* must have a VM process associated - do not kill normal API clients
3112 * with an open session */
3113 if (!Global::IsOnline(mData->mMachineState))
3114 return setError(VBOX_E_INVALID_OBJECT_STATE,
3115 tr("The machine '%s' does not have a VM process"),
3116 mUserData->s.strName.c_str());
3117
3118 /* forcibly terminate the VM process */
3119 if (mData->mSession.mPID != NIL_RTPROCESS)
3120 RTProcTerminate(mData->mSession.mPID);
3121
3122 /* signal the client watcher thread, as most likely the client has
3123 * been terminated */
3124 mParent->i_updateClientWatcher();
3125 }
3126
3127 return hrc;
3128}
3129
3130HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3131{
3132 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3133 return setError(E_INVALIDARG,
3134 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3135 aPosition, SchemaDefs::MaxBootPosition);
3136
3137 if (aDevice == DeviceType_USB)
3138 return setError(E_NOTIMPL,
3139 tr("Booting from USB device is currently not supported"));
3140
3141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3142
3143 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3144 if (FAILED(hrc)) return hrc;
3145
3146 i_setModified(IsModified_MachineData);
3147 mHWData.backup();
3148 mHWData->mBootOrder[aPosition - 1] = aDevice;
3149
3150 return S_OK;
3151}
3152
3153HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3154{
3155 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3156 return setError(E_INVALIDARG,
3157 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3158 aPosition, SchemaDefs::MaxBootPosition);
3159
3160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 *aDevice = mHWData->mBootOrder[aPosition - 1];
3163
3164 return S_OK;
3165}
3166
3167HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3168 LONG aControllerPort,
3169 LONG aDevice,
3170 DeviceType_T aType,
3171 const ComPtr<IMedium> &aMedium)
3172{
3173 IMedium *aM = aMedium;
3174 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3175 aName.c_str(), aControllerPort, aDevice, aType, aM));
3176
3177 // request the host lock first, since might be calling Host methods for getting host drives;
3178 // next, protect the media tree all the while we're in here, as well as our member variables
3179 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3180 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3183 if (FAILED(hrc)) return hrc;
3184
3185 /// @todo NEWMEDIA implicit machine registration
3186 if (!mData->mRegistered)
3187 return setError(VBOX_E_INVALID_OBJECT_STATE,
3188 tr("Cannot attach storage devices to an unregistered machine"));
3189
3190 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3191
3192 /* Check for an existing controller. */
3193 ComObjPtr<StorageController> ctl;
3194 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3195 if (FAILED(hrc)) return hrc;
3196
3197 StorageControllerType_T ctrlType;
3198 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3199 if (FAILED(hrc))
3200 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3201
3202 bool fSilent = false;
3203
3204 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3205 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3206 if ( mData->mMachineState == MachineState_Paused
3207 && strReconfig == "1")
3208 fSilent = true;
3209
3210 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3211 bool fHotplug = false;
3212 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3213 fHotplug = true;
3214
3215 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3216 return setError(VBOX_E_INVALID_VM_STATE,
3217 tr("Controller '%s' does not support hot-plugging"),
3218 aName.c_str());
3219
3220 // check that the port and device are not out of range
3221 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3222 if (FAILED(hrc)) return hrc;
3223
3224 /* check if the device slot is already busy */
3225 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3226 aName,
3227 aControllerPort,
3228 aDevice);
3229 if (pAttachTemp)
3230 {
3231 Medium *pMedium = pAttachTemp->i_getMedium();
3232 if (pMedium)
3233 {
3234 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3235 return setError(VBOX_E_OBJECT_IN_USE,
3236 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3237 pMedium->i_getLocationFull().c_str(),
3238 aControllerPort,
3239 aDevice,
3240 aName.c_str());
3241 }
3242 else
3243 return setError(VBOX_E_OBJECT_IN_USE,
3244 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3245 aControllerPort, aDevice, aName.c_str());
3246 }
3247
3248 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3249 if (aMedium && medium.isNull())
3250 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3251
3252 AutoCaller mediumCaller(medium);
3253 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3254
3255 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3256
3257 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3258 if ( pAttachTemp
3259 && !medium.isNull()
3260 && ( medium->i_getType() != MediumType_Readonly
3261 || medium->i_getDeviceType() != DeviceType_DVD)
3262 )
3263 return setError(VBOX_E_OBJECT_IN_USE,
3264 tr("Medium '%s' is already attached to this virtual machine"),
3265 medium->i_getLocationFull().c_str());
3266
3267 if (!medium.isNull())
3268 {
3269 MediumType_T mtype = medium->i_getType();
3270 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3271 // For DVDs it's not written to the config file, so needs no global config
3272 // version bump. For floppies it's a new attribute "type", which is ignored
3273 // by older VirtualBox version, so needs no global config version bump either.
3274 // For hard disks this type is not accepted.
3275 if (mtype == MediumType_MultiAttach)
3276 {
3277 // This type is new with VirtualBox 4.0 and therefore requires settings
3278 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3279 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3280 // two reasons: The medium type is a property of the media registry tree, which
3281 // can reside in the global config file (for pre-4.0 media); we would therefore
3282 // possibly need to bump the global config version. We don't want to do that though
3283 // because that might make downgrading to pre-4.0 impossible.
3284 // As a result, we can only use these two new types if the medium is NOT in the
3285 // global registry:
3286 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3287 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3288 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3289 )
3290 return setError(VBOX_E_INVALID_OBJECT_STATE,
3291 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3292 "to machines that were created with VirtualBox 4.0 or later"),
3293 medium->i_getLocationFull().c_str());
3294 }
3295 }
3296
3297 bool fIndirect = false;
3298 if (!medium.isNull())
3299 fIndirect = medium->i_isReadOnly();
3300 bool associate = true;
3301
3302 do
3303 {
3304 if ( aType == DeviceType_HardDisk
3305 && mMediumAttachments.isBackedUp())
3306 {
3307 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3308
3309 /* check if the medium was attached to the VM before we started
3310 * changing attachments in which case the attachment just needs to
3311 * be restored */
3312 pAttachTemp = i_findAttachment(oldAtts, medium);
3313 if (pAttachTemp)
3314 {
3315 AssertReturn(!fIndirect, E_FAIL);
3316
3317 /* see if it's the same bus/channel/device */
3318 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3319 {
3320 /* the simplest case: restore the whole attachment
3321 * and return, nothing else to do */
3322 mMediumAttachments->push_back(pAttachTemp);
3323
3324 /* Reattach the medium to the VM. */
3325 if (fHotplug || fSilent)
3326 {
3327 mediumLock.release();
3328 treeLock.release();
3329 alock.release();
3330
3331 MediumLockList *pMediumLockList(new MediumLockList());
3332
3333 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3334 medium /* pToLockWrite */,
3335 false /* fMediumLockWriteAll */,
3336 NULL,
3337 *pMediumLockList);
3338 alock.acquire();
3339 if (FAILED(hrc))
3340 delete pMediumLockList;
3341 else
3342 {
3343 Assert(mData->mSession.mLockedMedia.IsLocked());
3344 mData->mSession.mLockedMedia.Unlock();
3345 alock.release();
3346 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3347 mData->mSession.mLockedMedia.Lock();
3348 alock.acquire();
3349 }
3350 alock.release();
3351
3352 if (SUCCEEDED(hrc))
3353 {
3354 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3355 /* Remove lock list in case of error. */
3356 if (FAILED(hrc))
3357 {
3358 mData->mSession.mLockedMedia.Unlock();
3359 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3360 mData->mSession.mLockedMedia.Lock();
3361 }
3362 }
3363 }
3364
3365 return S_OK;
3366 }
3367
3368 /* bus/channel/device differ; we need a new attachment object,
3369 * but don't try to associate it again */
3370 associate = false;
3371 break;
3372 }
3373 }
3374
3375 /* go further only if the attachment is to be indirect */
3376 if (!fIndirect)
3377 break;
3378
3379 /* perform the so called smart attachment logic for indirect
3380 * attachments. Note that smart attachment is only applicable to base
3381 * hard disks. */
3382
3383 if (medium->i_getParent().isNull())
3384 {
3385 /* first, investigate the backup copy of the current hard disk
3386 * attachments to make it possible to re-attach existing diffs to
3387 * another device slot w/o losing their contents */
3388 if (mMediumAttachments.isBackedUp())
3389 {
3390 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3391
3392 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3393 uint32_t foundLevel = 0;
3394
3395 for (MediumAttachmentList::const_iterator
3396 it = oldAtts.begin();
3397 it != oldAtts.end();
3398 ++it)
3399 {
3400 uint32_t level = 0;
3401 MediumAttachment *pAttach = *it;
3402 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3403 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3404 if (pMedium.isNull())
3405 continue;
3406
3407 if (pMedium->i_getBase(&level) == medium)
3408 {
3409 /* skip the hard disk if its currently attached (we
3410 * cannot attach the same hard disk twice) */
3411 if (i_findAttachment(*mMediumAttachments.data(),
3412 pMedium))
3413 continue;
3414
3415 /* matched device, channel and bus (i.e. attached to the
3416 * same place) will win and immediately stop the search;
3417 * otherwise the attachment that has the youngest
3418 * descendant of medium will be used
3419 */
3420 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3421 {
3422 /* the simplest case: restore the whole attachment
3423 * and return, nothing else to do */
3424 mMediumAttachments->push_back(*it);
3425
3426 /* Reattach the medium to the VM. */
3427 if (fHotplug || fSilent)
3428 {
3429 mediumLock.release();
3430 treeLock.release();
3431 alock.release();
3432
3433 MediumLockList *pMediumLockList(new MediumLockList());
3434
3435 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3436 medium /* pToLockWrite */,
3437 false /* fMediumLockWriteAll */,
3438 NULL,
3439 *pMediumLockList);
3440 alock.acquire();
3441 if (FAILED(hrc))
3442 delete pMediumLockList;
3443 else
3444 {
3445 Assert(mData->mSession.mLockedMedia.IsLocked());
3446 mData->mSession.mLockedMedia.Unlock();
3447 alock.release();
3448 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3449 mData->mSession.mLockedMedia.Lock();
3450 alock.acquire();
3451 }
3452 alock.release();
3453
3454 if (SUCCEEDED(hrc))
3455 {
3456 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3457 /* Remove lock list in case of error. */
3458 if (FAILED(hrc))
3459 {
3460 mData->mSession.mLockedMedia.Unlock();
3461 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3462 mData->mSession.mLockedMedia.Lock();
3463 }
3464 }
3465 }
3466
3467 return S_OK;
3468 }
3469 else if ( foundIt == oldAtts.end()
3470 || level > foundLevel /* prefer younger */
3471 )
3472 {
3473 foundIt = it;
3474 foundLevel = level;
3475 }
3476 }
3477 }
3478
3479 if (foundIt != oldAtts.end())
3480 {
3481 /* use the previously attached hard disk */
3482 medium = (*foundIt)->i_getMedium();
3483 mediumCaller.attach(medium);
3484 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3485 mediumLock.attach(medium);
3486 /* not implicit, doesn't require association with this VM */
3487 fIndirect = false;
3488 associate = false;
3489 /* go right to the MediumAttachment creation */
3490 break;
3491 }
3492 }
3493
3494 /* must give up the medium lock and medium tree lock as below we
3495 * go over snapshots, which needs a lock with higher lock order. */
3496 mediumLock.release();
3497 treeLock.release();
3498
3499 /* then, search through snapshots for the best diff in the given
3500 * hard disk's chain to base the new diff on */
3501
3502 ComObjPtr<Medium> base;
3503 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3504 while (snap)
3505 {
3506 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3507
3508 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3509
3510 MediumAttachment *pAttachFound = NULL;
3511 uint32_t foundLevel = 0;
3512
3513 for (MediumAttachmentList::const_iterator
3514 it = snapAtts.begin();
3515 it != snapAtts.end();
3516 ++it)
3517 {
3518 MediumAttachment *pAttach = *it;
3519 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3520 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3521 if (pMedium.isNull())
3522 continue;
3523
3524 uint32_t level = 0;
3525 if (pMedium->i_getBase(&level) == medium)
3526 {
3527 /* matched device, channel and bus (i.e. attached to the
3528 * same place) will win and immediately stop the search;
3529 * otherwise the attachment that has the youngest
3530 * descendant of medium will be used
3531 */
3532 if ( pAttach->i_getDevice() == aDevice
3533 && pAttach->i_getPort() == aControllerPort
3534 && pAttach->i_getControllerName() == aName
3535 )
3536 {
3537 pAttachFound = pAttach;
3538 break;
3539 }
3540 else if ( !pAttachFound
3541 || level > foundLevel /* prefer younger */
3542 )
3543 {
3544 pAttachFound = pAttach;
3545 foundLevel = level;
3546 }
3547 }
3548 }
3549
3550 if (pAttachFound)
3551 {
3552 base = pAttachFound->i_getMedium();
3553 break;
3554 }
3555
3556 snap = snap->i_getParent();
3557 }
3558
3559 /* re-lock medium tree and the medium, as we need it below */
3560 treeLock.acquire();
3561 mediumLock.acquire();
3562
3563 /* found a suitable diff, use it as a base */
3564 if (!base.isNull())
3565 {
3566 medium = base;
3567 mediumCaller.attach(medium);
3568 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3569 mediumLock.attach(medium);
3570 }
3571 }
3572
3573 Utf8Str strFullSnapshotFolder;
3574 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3575
3576 ComObjPtr<Medium> diff;
3577 diff.createObject();
3578 // store this diff in the same registry as the parent
3579 Guid uuidRegistryParent;
3580 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3581 {
3582 // parent image has no registry: this can happen if we're attaching a new immutable
3583 // image that has not yet been attached (medium then points to the base and we're
3584 // creating the diff image for the immutable, and the parent is not yet registered);
3585 // put the parent in the machine registry then
3586 mediumLock.release();
3587 treeLock.release();
3588 alock.release();
3589 i_addMediumToRegistry(medium);
3590 alock.acquire();
3591 treeLock.acquire();
3592 mediumLock.acquire();
3593 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3594 }
3595 hrc = diff->init(mParent,
3596 medium->i_getPreferredDiffFormat(),
3597 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3598 uuidRegistryParent,
3599 DeviceType_HardDisk);
3600 if (FAILED(hrc)) return hrc;
3601
3602 /* Apply the normal locking logic to the entire chain. */
3603 MediumLockList *pMediumLockList(new MediumLockList());
3604 mediumLock.release();
3605 treeLock.release();
3606 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3607 diff /* pToLockWrite */,
3608 false /* fMediumLockWriteAll */,
3609 medium,
3610 *pMediumLockList);
3611 treeLock.acquire();
3612 mediumLock.acquire();
3613 if (SUCCEEDED(hrc))
3614 {
3615 mediumLock.release();
3616 treeLock.release();
3617 hrc = pMediumLockList->Lock();
3618 treeLock.acquire();
3619 mediumLock.acquire();
3620 if (FAILED(hrc))
3621 setError(hrc,
3622 tr("Could not lock medium when creating diff '%s'"),
3623 diff->i_getLocationFull().c_str());
3624 else
3625 {
3626 /* will release the lock before the potentially lengthy
3627 * operation, so protect with the special state */
3628 MachineState_T oldState = mData->mMachineState;
3629 i_setMachineState(MachineState_SettingUp);
3630
3631 mediumLock.release();
3632 treeLock.release();
3633 alock.release();
3634
3635 hrc = medium->i_createDiffStorage(diff,
3636 medium->i_getPreferredDiffVariant(),
3637 pMediumLockList,
3638 NULL /* aProgress */,
3639 true /* aWait */,
3640 false /* aNotify */);
3641
3642 alock.acquire();
3643 treeLock.acquire();
3644 mediumLock.acquire();
3645
3646 i_setMachineState(oldState);
3647 }
3648 }
3649
3650 /* Unlock the media and free the associated memory. */
3651 delete pMediumLockList;
3652
3653 if (FAILED(hrc)) return hrc;
3654
3655 /* use the created diff for the actual attachment */
3656 medium = diff;
3657 mediumCaller.attach(medium);
3658 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3659 mediumLock.attach(medium);
3660 }
3661 while (0);
3662
3663 ComObjPtr<MediumAttachment> attachment;
3664 attachment.createObject();
3665 hrc = attachment->init(this,
3666 medium,
3667 aName,
3668 aControllerPort,
3669 aDevice,
3670 aType,
3671 fIndirect,
3672 false /* fPassthrough */,
3673 false /* fTempEject */,
3674 false /* fNonRotational */,
3675 false /* fDiscard */,
3676 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3677 Utf8Str::Empty);
3678 if (FAILED(hrc)) return hrc;
3679
3680 if (associate && !medium.isNull())
3681 {
3682 // as the last step, associate the medium to the VM
3683 hrc = medium->i_addBackReference(mData->mUuid);
3684 // here we can fail because of Deleting, or being in process of creating a Diff
3685 if (FAILED(hrc)) return hrc;
3686
3687 mediumLock.release();
3688 treeLock.release();
3689 alock.release();
3690 i_addMediumToRegistry(medium);
3691 alock.acquire();
3692 treeLock.acquire();
3693 mediumLock.acquire();
3694 }
3695
3696 /* success: finally remember the attachment */
3697 i_setModified(IsModified_Storage);
3698 mMediumAttachments.backup();
3699 mMediumAttachments->push_back(attachment);
3700
3701 mediumLock.release();
3702 treeLock.release();
3703 alock.release();
3704
3705 if (fHotplug || fSilent)
3706 {
3707 if (!medium.isNull())
3708 {
3709 MediumLockList *pMediumLockList(new MediumLockList());
3710
3711 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3712 medium /* pToLockWrite */,
3713 false /* fMediumLockWriteAll */,
3714 NULL,
3715 *pMediumLockList);
3716 alock.acquire();
3717 if (FAILED(hrc))
3718 delete pMediumLockList;
3719 else
3720 {
3721 Assert(mData->mSession.mLockedMedia.IsLocked());
3722 mData->mSession.mLockedMedia.Unlock();
3723 alock.release();
3724 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3725 mData->mSession.mLockedMedia.Lock();
3726 alock.acquire();
3727 }
3728 alock.release();
3729 }
3730
3731 if (SUCCEEDED(hrc))
3732 {
3733 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3734 /* Remove lock list in case of error. */
3735 if (FAILED(hrc))
3736 {
3737 mData->mSession.mLockedMedia.Unlock();
3738 mData->mSession.mLockedMedia.Remove(attachment);
3739 mData->mSession.mLockedMedia.Lock();
3740 }
3741 }
3742 }
3743
3744 /* Save modified registries, but skip this machine as it's the caller's
3745 * job to save its settings like all other settings changes. */
3746 mParent->i_unmarkRegistryModified(i_getId());
3747 mParent->i_saveModifiedRegistries();
3748
3749 if (SUCCEEDED(hrc))
3750 {
3751 if (fIndirect && medium != aM)
3752 mParent->i_onMediumConfigChanged(medium);
3753 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3754 }
3755
3756 return hrc;
3757}
3758
3759HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3760 LONG aDevice)
3761{
3762 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3763
3764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3765
3766 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3767 if (FAILED(hrc)) return hrc;
3768
3769 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3770
3771 /* Check for an existing controller. */
3772 ComObjPtr<StorageController> ctl;
3773 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3774 if (FAILED(hrc)) return hrc;
3775
3776 StorageControllerType_T ctrlType;
3777 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3778 if (FAILED(hrc))
3779 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3780
3781 bool fSilent = false;
3782 Utf8Str strReconfig;
3783
3784 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3785 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3786 if ( mData->mMachineState == MachineState_Paused
3787 && strReconfig == "1")
3788 fSilent = true;
3789
3790 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3791 bool fHotplug = false;
3792 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3793 fHotplug = true;
3794
3795 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3796 return setError(VBOX_E_INVALID_VM_STATE,
3797 tr("Controller '%s' does not support hot-plugging"),
3798 aName.c_str());
3799
3800 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3801 aName,
3802 aControllerPort,
3803 aDevice);
3804 if (!pAttach)
3805 return setError(VBOX_E_OBJECT_NOT_FOUND,
3806 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3807 aDevice, aControllerPort, aName.c_str());
3808
3809 if (fHotplug && !pAttach->i_getHotPluggable())
3810 return setError(VBOX_E_NOT_SUPPORTED,
3811 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3812 aDevice, aControllerPort, aName.c_str());
3813
3814 /*
3815 * The VM has to detach the device before we delete any implicit diffs.
3816 * If this fails we can roll back without loosing data.
3817 */
3818 if (fHotplug || fSilent)
3819 {
3820 alock.release();
3821 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3822 alock.acquire();
3823 }
3824 if (FAILED(hrc)) return hrc;
3825
3826 /* If we are here everything went well and we can delete the implicit now. */
3827 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3828
3829 alock.release();
3830
3831 /* Save modified registries, but skip this machine as it's the caller's
3832 * job to save its settings like all other settings changes. */
3833 mParent->i_unmarkRegistryModified(i_getId());
3834 mParent->i_saveModifiedRegistries();
3835
3836 if (SUCCEEDED(hrc))
3837 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3838
3839 return hrc;
3840}
3841
3842HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3843 LONG aDevice, BOOL aPassthrough)
3844{
3845 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3846 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3847
3848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3849
3850 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3851 if (FAILED(hrc)) return hrc;
3852
3853 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3854
3855 /* Check for an existing controller. */
3856 ComObjPtr<StorageController> ctl;
3857 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3858 if (FAILED(hrc)) return hrc;
3859
3860 StorageControllerType_T ctrlType;
3861 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3862 if (FAILED(hrc))
3863 return setError(E_FAIL,
3864 tr("Could not get type of controller '%s'"),
3865 aName.c_str());
3866
3867 bool fSilent = false;
3868 Utf8Str strReconfig;
3869
3870 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3871 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3872 if ( mData->mMachineState == MachineState_Paused
3873 && strReconfig == "1")
3874 fSilent = true;
3875
3876 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3877 bool fHotplug = false;
3878 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3879 fHotplug = true;
3880
3881 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3882 return setError(VBOX_E_INVALID_VM_STATE,
3883 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3884 aName.c_str());
3885
3886 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3887 aName,
3888 aControllerPort,
3889 aDevice);
3890 if (!pAttach)
3891 return setError(VBOX_E_OBJECT_NOT_FOUND,
3892 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3893 aDevice, aControllerPort, aName.c_str());
3894
3895
3896 i_setModified(IsModified_Storage);
3897 mMediumAttachments.backup();
3898
3899 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3900
3901 if (pAttach->i_getType() != DeviceType_DVD)
3902 return setError(E_INVALIDARG,
3903 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3904 aDevice, aControllerPort, aName.c_str());
3905
3906 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3907
3908 pAttach->i_updatePassthrough(!!aPassthrough);
3909
3910 attLock.release();
3911 alock.release();
3912 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3913 if (SUCCEEDED(hrc) && fValueChanged)
3914 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3915
3916 return hrc;
3917}
3918
3919HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3920 LONG aDevice, BOOL aTemporaryEject)
3921{
3922
3923 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3924 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3925
3926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3927
3928 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3929 if (FAILED(hrc)) return hrc;
3930
3931 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3932 aName,
3933 aControllerPort,
3934 aDevice);
3935 if (!pAttach)
3936 return setError(VBOX_E_OBJECT_NOT_FOUND,
3937 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3938 aDevice, aControllerPort, aName.c_str());
3939
3940
3941 i_setModified(IsModified_Storage);
3942 mMediumAttachments.backup();
3943
3944 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3945
3946 if (pAttach->i_getType() != DeviceType_DVD)
3947 return setError(E_INVALIDARG,
3948 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3949 aDevice, aControllerPort, aName.c_str());
3950 pAttach->i_updateTempEject(!!aTemporaryEject);
3951
3952 return S_OK;
3953}
3954
3955HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3956 LONG aDevice, BOOL aNonRotational)
3957{
3958
3959 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3960 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3961
3962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3963
3964 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3965 if (FAILED(hrc)) return hrc;
3966
3967 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3968
3969 if (Global::IsOnlineOrTransient(mData->mMachineState))
3970 return setError(VBOX_E_INVALID_VM_STATE,
3971 tr("Invalid machine state: %s"),
3972 Global::stringifyMachineState(mData->mMachineState));
3973
3974 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3975 aName,
3976 aControllerPort,
3977 aDevice);
3978 if (!pAttach)
3979 return setError(VBOX_E_OBJECT_NOT_FOUND,
3980 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3981 aDevice, aControllerPort, aName.c_str());
3982
3983
3984 i_setModified(IsModified_Storage);
3985 mMediumAttachments.backup();
3986
3987 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3988
3989 if (pAttach->i_getType() != DeviceType_HardDisk)
3990 return setError(E_INVALIDARG,
3991 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"),
3992 aDevice, aControllerPort, aName.c_str());
3993 pAttach->i_updateNonRotational(!!aNonRotational);
3994
3995 return S_OK;
3996}
3997
3998HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
3999 LONG aDevice, BOOL aDiscard)
4000{
4001
4002 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4003 aName.c_str(), aControllerPort, aDevice, aDiscard));
4004
4005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4006
4007 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4008 if (FAILED(hrc)) return hrc;
4009
4010 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4011
4012 if (Global::IsOnlineOrTransient(mData->mMachineState))
4013 return setError(VBOX_E_INVALID_VM_STATE,
4014 tr("Invalid machine state: %s"),
4015 Global::stringifyMachineState(mData->mMachineState));
4016
4017 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4018 aName,
4019 aControllerPort,
4020 aDevice);
4021 if (!pAttach)
4022 return setError(VBOX_E_OBJECT_NOT_FOUND,
4023 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4024 aDevice, aControllerPort, aName.c_str());
4025
4026
4027 i_setModified(IsModified_Storage);
4028 mMediumAttachments.backup();
4029
4030 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4031
4032 if (pAttach->i_getType() != DeviceType_HardDisk)
4033 return setError(E_INVALIDARG,
4034 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"),
4035 aDevice, aControllerPort, aName.c_str());
4036 pAttach->i_updateDiscard(!!aDiscard);
4037
4038 return S_OK;
4039}
4040
4041HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4042 LONG aDevice, BOOL aHotPluggable)
4043{
4044 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4045 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4046
4047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4048
4049 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4050 if (FAILED(hrc)) return hrc;
4051
4052 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4053
4054 if (Global::IsOnlineOrTransient(mData->mMachineState))
4055 return setError(VBOX_E_INVALID_VM_STATE,
4056 tr("Invalid machine state: %s"),
4057 Global::stringifyMachineState(mData->mMachineState));
4058
4059 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4060 aName,
4061 aControllerPort,
4062 aDevice);
4063 if (!pAttach)
4064 return setError(VBOX_E_OBJECT_NOT_FOUND,
4065 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4066 aDevice, aControllerPort, aName.c_str());
4067
4068 /* Check for an existing controller. */
4069 ComObjPtr<StorageController> ctl;
4070 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4071 if (FAILED(hrc)) return hrc;
4072
4073 StorageControllerType_T ctrlType;
4074 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4075 if (FAILED(hrc))
4076 return setError(E_FAIL,
4077 tr("Could not get type of controller '%s'"),
4078 aName.c_str());
4079
4080 if (!i_isControllerHotplugCapable(ctrlType))
4081 return setError(VBOX_E_NOT_SUPPORTED,
4082 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4083 aName.c_str());
4084
4085 /* silently ignore attempts to modify the hot-plug status of USB devices */
4086 if (ctrlType == StorageControllerType_USB)
4087 return S_OK;
4088
4089 i_setModified(IsModified_Storage);
4090 mMediumAttachments.backup();
4091
4092 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4093
4094 if (pAttach->i_getType() == DeviceType_Floppy)
4095 return setError(E_INVALIDARG,
4096 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"),
4097 aDevice, aControllerPort, aName.c_str());
4098 pAttach->i_updateHotPluggable(!!aHotPluggable);
4099
4100 return S_OK;
4101}
4102
4103HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4104 LONG aDevice)
4105{
4106 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4107 aName.c_str(), aControllerPort, aDevice));
4108
4109 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4110}
4111
4112HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4113 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4114{
4115 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4116 aName.c_str(), aControllerPort, aDevice));
4117
4118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4119
4120 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4121 if (FAILED(hrc)) return hrc;
4122
4123 if (Global::IsOnlineOrTransient(mData->mMachineState))
4124 return setError(VBOX_E_INVALID_VM_STATE,
4125 tr("Invalid machine state: %s"),
4126 Global::stringifyMachineState(mData->mMachineState));
4127
4128 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4129 aName,
4130 aControllerPort,
4131 aDevice);
4132 if (!pAttach)
4133 return setError(VBOX_E_OBJECT_NOT_FOUND,
4134 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4135 aDevice, aControllerPort, aName.c_str());
4136
4137
4138 i_setModified(IsModified_Storage);
4139 mMediumAttachments.backup();
4140
4141 IBandwidthGroup *iB = aBandwidthGroup;
4142 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4143 if (aBandwidthGroup && group.isNull())
4144 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4145
4146 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4147
4148 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4149 if (strBandwidthGroupOld.isNotEmpty())
4150 {
4151 /* Get the bandwidth group object and release it - this must not fail. */
4152 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4153 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4154 Assert(SUCCEEDED(hrc));
4155
4156 pBandwidthGroupOld->i_release();
4157 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4158 }
4159
4160 if (!group.isNull())
4161 {
4162 group->i_reference();
4163 pAttach->i_updateBandwidthGroup(group->i_getName());
4164 }
4165
4166 return S_OK;
4167}
4168
4169HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4170 LONG aControllerPort,
4171 LONG aDevice,
4172 DeviceType_T aType)
4173{
4174 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4175 aName.c_str(), aControllerPort, aDevice, aType));
4176
4177 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4178}
4179
4180
4181HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4182 LONG aControllerPort,
4183 LONG aDevice,
4184 BOOL aForce)
4185{
4186 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4187 aName.c_str(), aControllerPort, aForce));
4188
4189 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4190}
4191
4192HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4193 LONG aControllerPort,
4194 LONG aDevice,
4195 const ComPtr<IMedium> &aMedium,
4196 BOOL aForce)
4197{
4198 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4199 aName.c_str(), aControllerPort, aDevice, aForce));
4200
4201 // request the host lock first, since might be calling Host methods for getting host drives;
4202 // next, protect the media tree all the while we're in here, as well as our member variables
4203 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4204 this->lockHandle(),
4205 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4206
4207 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4208 if (FAILED(hrc)) return hrc;
4209
4210 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4211 aName,
4212 aControllerPort,
4213 aDevice);
4214 if (pAttach.isNull())
4215 return setError(VBOX_E_OBJECT_NOT_FOUND,
4216 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4217 aDevice, aControllerPort, aName.c_str());
4218
4219 /* Remember previously mounted medium. The medium before taking the
4220 * backup is not necessarily the same thing. */
4221 ComObjPtr<Medium> oldmedium;
4222 oldmedium = pAttach->i_getMedium();
4223
4224 IMedium *iM = aMedium;
4225 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4226 if (aMedium && pMedium.isNull())
4227 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4228
4229 /* Check if potential medium is already mounted */
4230 if (pMedium == oldmedium)
4231 return S_OK;
4232
4233 AutoCaller mediumCaller(pMedium);
4234 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4235
4236 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4237 if (pMedium)
4238 {
4239 DeviceType_T mediumType = pAttach->i_getType();
4240 switch (mediumType)
4241 {
4242 case DeviceType_DVD:
4243 case DeviceType_Floppy:
4244 break;
4245
4246 default:
4247 return setError(VBOX_E_INVALID_OBJECT_STATE,
4248 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4249 aControllerPort,
4250 aDevice,
4251 aName.c_str());
4252 }
4253 }
4254
4255 i_setModified(IsModified_Storage);
4256 mMediumAttachments.backup();
4257
4258 {
4259 // The backup operation makes the pAttach reference point to the
4260 // old settings. Re-get the correct reference.
4261 pAttach = i_findAttachment(*mMediumAttachments.data(),
4262 aName,
4263 aControllerPort,
4264 aDevice);
4265 if (!oldmedium.isNull())
4266 oldmedium->i_removeBackReference(mData->mUuid);
4267 if (!pMedium.isNull())
4268 {
4269 pMedium->i_addBackReference(mData->mUuid);
4270
4271 mediumLock.release();
4272 multiLock.release();
4273 i_addMediumToRegistry(pMedium);
4274 multiLock.acquire();
4275 mediumLock.acquire();
4276 }
4277
4278 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4279 pAttach->i_updateMedium(pMedium);
4280 }
4281
4282 i_setModified(IsModified_Storage);
4283
4284 mediumLock.release();
4285 multiLock.release();
4286 hrc = i_onMediumChange(pAttach, aForce);
4287 multiLock.acquire();
4288 mediumLock.acquire();
4289
4290 /* On error roll back this change only. */
4291 if (FAILED(hrc))
4292 {
4293 if (!pMedium.isNull())
4294 pMedium->i_removeBackReference(mData->mUuid);
4295 pAttach = i_findAttachment(*mMediumAttachments.data(),
4296 aName,
4297 aControllerPort,
4298 aDevice);
4299 /* If the attachment is gone in the meantime, bail out. */
4300 if (pAttach.isNull())
4301 return hrc;
4302 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4303 if (!oldmedium.isNull())
4304 oldmedium->i_addBackReference(mData->mUuid);
4305 pAttach->i_updateMedium(oldmedium);
4306 }
4307
4308 mediumLock.release();
4309 multiLock.release();
4310
4311 /* Save modified registries, but skip this machine as it's the caller's
4312 * job to save its settings like all other settings changes. */
4313 mParent->i_unmarkRegistryModified(i_getId());
4314 mParent->i_saveModifiedRegistries();
4315
4316 return hrc;
4317}
4318HRESULT Machine::getMedium(const com::Utf8Str &aName,
4319 LONG aControllerPort,
4320 LONG aDevice,
4321 ComPtr<IMedium> &aMedium)
4322{
4323 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4324 aName.c_str(), aControllerPort, aDevice));
4325
4326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4327
4328 aMedium = NULL;
4329
4330 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4331 aName,
4332 aControllerPort,
4333 aDevice);
4334 if (pAttach.isNull())
4335 return setError(VBOX_E_OBJECT_NOT_FOUND,
4336 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4337 aDevice, aControllerPort, aName.c_str());
4338
4339 aMedium = pAttach->i_getMedium();
4340
4341 return S_OK;
4342}
4343
4344HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4345{
4346 if (aSlot < RT_ELEMENTS(mSerialPorts))
4347 {
4348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4349 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4350 return S_OK;
4351 }
4352 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4353}
4354
4355HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4356{
4357 if (aSlot < RT_ELEMENTS(mParallelPorts))
4358 {
4359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4360 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4361 return S_OK;
4362 }
4363 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4364}
4365
4366
4367HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4368{
4369 /* Do not assert if slot is out of range, just return the advertised
4370 status. testdriver/vbox.py triggers this in logVmInfo. */
4371 if (aSlot >= mNetworkAdapters.size())
4372 return setError(E_INVALIDARG,
4373 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4374 aSlot, mNetworkAdapters.size());
4375
4376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4377
4378 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4379
4380 return S_OK;
4381}
4382
4383HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4384{
4385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4386
4387 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4388 size_t i = 0;
4389 for (settings::StringsMap::const_iterator
4390 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4391 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4392 ++it, ++i)
4393 aKeys[i] = it->first;
4394
4395 return S_OK;
4396}
4397
4398 /**
4399 * @note Locks this object for reading.
4400 */
4401HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4402 com::Utf8Str &aValue)
4403{
4404 /* start with nothing found */
4405 aValue = "";
4406
4407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4408
4409 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4410 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4411 // found:
4412 aValue = it->second; // source is a Utf8Str
4413
4414 /* return the result to caller (may be empty) */
4415 return S_OK;
4416}
4417
4418 /**
4419 * @note Locks mParent for writing + this object for writing.
4420 */
4421HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4422{
4423 /* Because control characters in aKey have caused problems in the settings
4424 * they are rejected unless the key should be deleted. */
4425 if (!aValue.isEmpty())
4426 {
4427 for (size_t i = 0; i < aKey.length(); ++i)
4428 {
4429 char ch = aKey[i];
4430 if (RTLocCIsCntrl(ch))
4431 return E_INVALIDARG;
4432 }
4433 }
4434
4435 Utf8Str strOldValue; // empty
4436
4437 // locking note: we only hold the read lock briefly to look up the old value,
4438 // then release it and call the onExtraCanChange callbacks. There is a small
4439 // chance of a race insofar as the callback might be called twice if two callers
4440 // change the same key at the same time, but that's a much better solution
4441 // than the deadlock we had here before. The actual changing of the extradata
4442 // is then performed under the write lock and race-free.
4443
4444 // look up the old value first; if nothing has changed then we need not do anything
4445 {
4446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4447
4448 // For snapshots don't even think about allowing changes, extradata
4449 // is global for a machine, so there is nothing snapshot specific.
4450 if (i_isSnapshotMachine())
4451 return setError(VBOX_E_INVALID_VM_STATE,
4452 tr("Cannot set extradata for a snapshot"));
4453
4454 // check if the right IMachine instance is used
4455 if (mData->mRegistered && !i_isSessionMachine())
4456 return setError(VBOX_E_INVALID_VM_STATE,
4457 tr("Cannot set extradata for an immutable machine"));
4458
4459 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4460 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4461 strOldValue = it->second;
4462 }
4463
4464 bool fChanged;
4465 if ((fChanged = (strOldValue != aValue)))
4466 {
4467 // ask for permission from all listeners outside the locks;
4468 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4469 // lock to copy the list of callbacks to invoke
4470 Bstr bstrError;
4471 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4472 {
4473 const char *sep = bstrError.isEmpty() ? "" : ": ";
4474 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4475 return setError(E_ACCESSDENIED,
4476 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4477 aKey.c_str(),
4478 aValue.c_str(),
4479 sep,
4480 bstrError.raw());
4481 }
4482
4483 // data is changing and change not vetoed: then write it out under the lock
4484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4485
4486 if (aValue.isEmpty())
4487 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4488 else
4489 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4490 // creates a new key if needed
4491
4492 bool fNeedsGlobalSaveSettings = false;
4493 // This saving of settings is tricky: there is no "old state" for the
4494 // extradata items at all (unlike all other settings), so the old/new
4495 // settings comparison would give a wrong result!
4496 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4497
4498 if (fNeedsGlobalSaveSettings)
4499 {
4500 // save the global settings; for that we should hold only the VirtualBox lock
4501 alock.release();
4502 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4503 mParent->i_saveSettings();
4504 }
4505 }
4506
4507 // fire notification outside the lock
4508 if (fChanged)
4509 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4510
4511 return S_OK;
4512}
4513
4514HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4515{
4516 aProgress = NULL;
4517 NOREF(aSettingsFilePath);
4518 ReturnComNotImplemented();
4519}
4520
4521HRESULT Machine::saveSettings()
4522{
4523 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4524
4525 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4526 if (FAILED(hrc)) return hrc;
4527
4528 /* the settings file path may never be null */
4529 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4530
4531 /* save all VM data excluding snapshots */
4532 bool fNeedsGlobalSaveSettings = false;
4533 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4534 mlock.release();
4535
4536 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4537 {
4538 // save the global settings; for that we should hold only the VirtualBox lock
4539 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4540 hrc = mParent->i_saveSettings();
4541 }
4542
4543 return hrc;
4544}
4545
4546
4547HRESULT Machine::discardSettings()
4548{
4549 /*
4550 * We need to take the machine list lock here as well as the machine one
4551 * or we'll get into trouble should any media stuff require rolling back.
4552 *
4553 * Details:
4554 *
4555 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4556 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4557 * 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]
4558 * 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
4559 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4560 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4561 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4562 * 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
4563 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4564 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4565 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4566 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4567 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4568 * 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]
4569 * 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] (*)
4570 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4571 * 0:005> k
4572 * # Child-SP RetAddr Call Site
4573 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4574 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4575 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4576 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4577 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4578 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4579 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4580 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4581 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4582 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4583 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4584 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4585 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4586 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4587 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4588 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4589 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4590 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4591 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4592 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4593 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4594 *
4595 */
4596 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4598
4599 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4600 if (FAILED(hrc)) return hrc;
4601
4602 /*
4603 * during this rollback, the session will be notified if data has
4604 * been actually changed
4605 */
4606 i_rollback(true /* aNotify */);
4607
4608 return S_OK;
4609}
4610
4611/** @note Locks objects! */
4612HRESULT Machine::unregister(AutoCaller &autoCaller,
4613 CleanupMode_T aCleanupMode,
4614 std::vector<ComPtr<IMedium> > &aMedia)
4615{
4616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4617
4618 Guid id(i_getId());
4619
4620 if (mData->mSession.mState != SessionState_Unlocked)
4621 return setError(VBOX_E_INVALID_OBJECT_STATE,
4622 tr("Cannot unregister the machine '%s' while it is locked"),
4623 mUserData->s.strName.c_str());
4624
4625 // wait for state dependents to drop to zero
4626 i_ensureNoStateDependencies(alock);
4627
4628 if (!mData->mAccessible)
4629 {
4630 // inaccessible machines can only be unregistered; uninitialize ourselves
4631 // here because currently there may be no unregistered that are inaccessible
4632 // (this state combination is not supported). Note releasing the caller and
4633 // leaving the lock before calling uninit()
4634 alock.release();
4635 autoCaller.release();
4636
4637 uninit();
4638
4639 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4640 // calls VirtualBox::i_saveSettings()
4641
4642 return S_OK;
4643 }
4644
4645 HRESULT hrc = S_OK;
4646 mData->llFilesToDelete.clear();
4647
4648 if (!mSSData->strStateFilePath.isEmpty())
4649 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4650
4651 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4652 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4653 mData->llFilesToDelete.push_back(strNVRAMFile);
4654
4655 Utf8Str strServerCertificate(VRDE_AUTO_GENENERATED_CERT_FILENAME);
4656 i_calculateFullPath(strServerCertificate, strServerCertificate);
4657
4658 Utf8Str strServerPrivateKey(VRDE_AUTO_GENENERATED_PKEY_FILENAME);
4659 i_calculateFullPath(strServerPrivateKey, strServerPrivateKey);
4660
4661 if (RTFileExists(strServerPrivateKey.c_str()))
4662 mData->llFilesToDelete.push_back(strServerPrivateKey);
4663 if (RTFileExists(strServerCertificate.c_str()))
4664 mData->llFilesToDelete.push_back(strServerCertificate);
4665
4666 // This list collects the medium objects from all medium attachments
4667 // which we will detach from the machine and its snapshots, in a specific
4668 // order which allows for closing all media without getting "media in use"
4669 // errors, simply by going through the list from the front to the back:
4670 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4671 // and must be closed before the parent media from the snapshots, or closing the parents
4672 // will fail because they still have children);
4673 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4674 // the root ("first") snapshot of the machine.
4675 MediaList llMedia;
4676
4677 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4678 && mMediumAttachments->size()
4679 )
4680 {
4681 // we have media attachments: detach them all and add the Medium objects to our list
4682 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4683 }
4684
4685 if (mData->mFirstSnapshot)
4686 {
4687 // add the media from the medium attachments of the snapshots to
4688 // llMedia as well, after the "main" machine media;
4689 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4690 // snapshot machine, depth first.
4691
4692 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4693 MachineState_T oldState = mData->mMachineState;
4694 mData->mMachineState = MachineState_DeletingSnapshot;
4695
4696 // make a copy of the first snapshot reference so the refcount does not
4697 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4698 // (would hang due to the AutoCaller voodoo)
4699 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4700
4701 // GO!
4702 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4703
4704 mData->mMachineState = oldState;
4705 }
4706
4707 if (FAILED(hrc))
4708 {
4709 i_rollbackMedia();
4710 return hrc;
4711 }
4712
4713 // commit all the media changes made above
4714 i_commitMedia();
4715
4716 mData->mRegistered = false;
4717
4718 // machine lock no longer needed
4719 alock.release();
4720
4721 /* Make sure that the settings of the current VM are not saved, because
4722 * they are rather crippled at this point to meet the cleanup expectations
4723 * and there's no point destroying the VM config on disk just because. */
4724 mParent->i_unmarkRegistryModified(id);
4725
4726 // return media to caller
4727 aMedia.resize(llMedia.size());
4728 size_t i = 0;
4729 for (MediaList::const_iterator
4730 it = llMedia.begin();
4731 it != llMedia.end();
4732 ++it, ++i)
4733 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4734
4735 mParent->i_unregisterMachine(this, aCleanupMode, id);
4736 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4737
4738 return S_OK;
4739}
4740
4741/**
4742 * Task record for deleting a machine config.
4743 */
4744class Machine::DeleteConfigTask
4745 : public Machine::Task
4746{
4747public:
4748 DeleteConfigTask(Machine *m,
4749 Progress *p,
4750 const Utf8Str &t,
4751 const RTCList<ComPtr<IMedium> > &llMedia,
4752 const StringsList &llFilesToDelete)
4753 : Task(m, p, t),
4754 m_llMedia(llMedia),
4755 m_llFilesToDelete(llFilesToDelete)
4756 {}
4757
4758private:
4759 void handler()
4760 {
4761 try
4762 {
4763 m_pMachine->i_deleteConfigHandler(*this);
4764 }
4765 catch (...)
4766 {
4767 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4768 }
4769 }
4770
4771 RTCList<ComPtr<IMedium> > m_llMedia;
4772 StringsList m_llFilesToDelete;
4773
4774 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4775};
4776
4777/**
4778 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4779 * SessionMachine::taskHandler().
4780 *
4781 * @note Locks this object for writing.
4782 *
4783 * @param task
4784 */
4785void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4786{
4787 LogFlowThisFuncEnter();
4788
4789 AutoCaller autoCaller(this);
4790 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4791 if (FAILED(autoCaller.hrc()))
4792 {
4793 /* we might have been uninitialized because the session was accidentally
4794 * closed by the client, so don't assert */
4795 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4796 task.m_pProgress->i_notifyComplete(hrc);
4797 LogFlowThisFuncLeave();
4798 return;
4799 }
4800
4801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4802
4803 HRESULT hrc;
4804 try
4805 {
4806 ULONG uLogHistoryCount = 3;
4807 ComPtr<ISystemProperties> systemProperties;
4808 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4809 if (FAILED(hrc)) throw hrc;
4810
4811 if (!systemProperties.isNull())
4812 {
4813 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4814 if (FAILED(hrc)) throw hrc;
4815 }
4816
4817 MachineState_T oldState = mData->mMachineState;
4818 i_setMachineState(MachineState_SettingUp);
4819 alock.release();
4820 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4821 {
4822 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4823 {
4824 AutoCaller mac(pMedium);
4825 if (FAILED(mac.hrc())) throw mac.hrc();
4826 Utf8Str strLocation = pMedium->i_getLocationFull();
4827 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4828 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4829 if (FAILED(hrc)) throw hrc;
4830 }
4831 if (pMedium->i_isMediumFormatFile())
4832 {
4833 ComPtr<IProgress> pProgress2;
4834 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4835 if (FAILED(hrc)) throw hrc;
4836 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4837 if (FAILED(hrc)) throw hrc;
4838 }
4839
4840 /* Close the medium, deliberately without checking the return
4841 * code, and without leaving any trace in the error info, as
4842 * a failure here is a very minor issue, which shouldn't happen
4843 * as above we even managed to delete the medium. */
4844 {
4845 ErrorInfoKeeper eik;
4846 pMedium->Close();
4847 }
4848 }
4849 i_setMachineState(oldState);
4850 alock.acquire();
4851
4852 // delete the files pushed on the task list by Machine::Delete()
4853 // (this includes saved states of the machine and snapshots and
4854 // medium storage files from the IMedium list passed in, and the
4855 // machine XML file)
4856 for (StringsList::const_iterator
4857 it = task.m_llFilesToDelete.begin();
4858 it != task.m_llFilesToDelete.end();
4859 ++it)
4860 {
4861 const Utf8Str &strFile = *it;
4862 LogFunc(("Deleting file %s\n", strFile.c_str()));
4863 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4864 if (FAILED(hrc)) throw hrc;
4865 i_deleteFile(strFile);
4866 }
4867
4868 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4869 if (FAILED(hrc)) throw hrc;
4870
4871 /* delete the settings only when the file actually exists */
4872 if (mData->pMachineConfigFile->fileExists())
4873 {
4874 /* Delete any backup or uncommitted XML files. Ignore failures.
4875 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4876 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4877 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4878 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4879 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4880 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4881
4882 /* delete the Logs folder, nothing important should be left
4883 * there (we don't check for errors because the user might have
4884 * some private files there that we don't want to delete) */
4885 Utf8Str logFolder;
4886 getLogFolder(logFolder);
4887 Assert(logFolder.length());
4888 if (RTDirExists(logFolder.c_str()))
4889 {
4890 /* Delete all VBox.log[.N] files from the Logs folder
4891 * (this must be in sync with the rotation logic in
4892 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4893 * files that may have been created by the GUI. */
4894 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4895 i_deleteFile(log, true /* fIgnoreFailures */);
4896 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4897 i_deleteFile(log, true /* fIgnoreFailures */);
4898 for (ULONG i = uLogHistoryCount; i > 0; i--)
4899 {
4900 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4901 i_deleteFile(log, true /* fIgnoreFailures */);
4902 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4903 i_deleteFile(log, true /* fIgnoreFailures */);
4904 }
4905 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4906 i_deleteFile(log, true /* fIgnoreFailures */);
4907#if defined(RT_OS_WINDOWS)
4908 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4909 i_deleteFile(log, true /* fIgnoreFailures */);
4910 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4911 i_deleteFile(log, true /* fIgnoreFailures */);
4912#endif
4913
4914 RTDirRemove(logFolder.c_str());
4915 }
4916
4917 /* delete the Snapshots folder, nothing important should be left
4918 * there (we don't check for errors because the user might have
4919 * some private files there that we don't want to delete) */
4920 Utf8Str strFullSnapshotFolder;
4921 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4922 Assert(!strFullSnapshotFolder.isEmpty());
4923 if (RTDirExists(strFullSnapshotFolder.c_str()))
4924 RTDirRemove(strFullSnapshotFolder.c_str());
4925
4926 // delete the directory that contains the settings file, but only
4927 // if it matches the VM name
4928 Utf8Str settingsDir;
4929 if (i_isInOwnDir(&settingsDir))
4930 RTDirRemove(settingsDir.c_str());
4931 }
4932
4933 alock.release();
4934
4935 mParent->i_saveModifiedRegistries();
4936 }
4937 catch (HRESULT hrcXcpt)
4938 {
4939 hrc = hrcXcpt;
4940 }
4941
4942 task.m_pProgress->i_notifyComplete(hrc);
4943
4944 LogFlowThisFuncLeave();
4945}
4946
4947HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4948{
4949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4950
4951 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4952 if (FAILED(hrc)) return hrc;
4953
4954 if (mData->mRegistered)
4955 return setError(VBOX_E_INVALID_VM_STATE,
4956 tr("Cannot delete settings of a registered machine"));
4957
4958 // collect files to delete
4959 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4960 // machine config file
4961 if (mData->pMachineConfigFile->fileExists())
4962 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4963 // backup of machine config file
4964 Utf8Str strTmp(mData->m_strConfigFileFull);
4965 strTmp.append("-prev");
4966 if (RTFileExists(strTmp.c_str()))
4967 llFilesToDelete.push_back(strTmp);
4968
4969 RTCList<ComPtr<IMedium> > llMedia;
4970 for (size_t i = 0; i < aMedia.size(); ++i)
4971 {
4972 IMedium *pIMedium(aMedia[i]);
4973 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4974 if (pMedium.isNull())
4975 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4976 SafeArray<BSTR> ids;
4977 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4978 if (FAILED(hrc)) return hrc;
4979 /* At this point the medium should not have any back references
4980 * anymore. If it has it is attached to another VM and *must* not
4981 * deleted. */
4982 if (ids.size() < 1)
4983 llMedia.append(pMedium);
4984 }
4985
4986 ComObjPtr<Progress> pProgress;
4987 pProgress.createObject();
4988 hrc = pProgress->init(i_getVirtualBox(),
4989 static_cast<IMachine*>(this) /* aInitiator */,
4990 tr("Deleting files"),
4991 true /* fCancellable */,
4992 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4993 tr("Collecting file inventory"));
4994 if (FAILED(hrc))
4995 return hrc;
4996
4997 /* create and start the task on a separate thread (note that it will not
4998 * start working until we release alock) */
4999 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
5000 hrc = pTask->createThread();
5001 pTask = NULL;
5002 if (FAILED(hrc))
5003 return hrc;
5004
5005 pProgress.queryInterfaceTo(aProgress.asOutParam());
5006
5007 LogFlowFuncLeave();
5008
5009 return S_OK;
5010}
5011
5012HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5013{
5014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5015
5016 ComObjPtr<Snapshot> pSnapshot;
5017 HRESULT hrc;
5018
5019 if (aNameOrId.isEmpty())
5020 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5021 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5022 else
5023 {
5024 Guid uuid(aNameOrId);
5025 if (uuid.isValid())
5026 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5027 else
5028 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5029 }
5030 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5031
5032 return hrc;
5033}
5034
5035HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5036 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5037{
5038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5039
5040 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5041 if (FAILED(hrc)) return hrc;
5042
5043 ComObjPtr<SharedFolder> sharedFolder;
5044 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5045 if (SUCCEEDED(hrc))
5046 return setError(VBOX_E_OBJECT_IN_USE,
5047 tr("Shared folder named '%s' already exists"),
5048 aName.c_str());
5049
5050 SymlinkPolicy_T enmSymlinkPolicy = SymlinkPolicy_None;
5051 sharedFolder.createObject();
5052 hrc = sharedFolder->init(i_getMachine(),
5053 aName,
5054 aHostPath,
5055 !!aWritable,
5056 !!aAutomount,
5057 aAutoMountPoint,
5058 true /* fFailOnError */,
5059 enmSymlinkPolicy);
5060 if (FAILED(hrc)) return hrc;
5061
5062 i_setModified(IsModified_SharedFolders);
5063 mHWData.backup();
5064 mHWData->mSharedFolders.push_back(sharedFolder);
5065
5066 /* inform the direct session if any */
5067 alock.release();
5068 i_onSharedFolderChange(FALSE);
5069
5070 return S_OK;
5071}
5072
5073HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5074{
5075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5076
5077 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5078 if (FAILED(hrc)) return hrc;
5079
5080 ComObjPtr<SharedFolder> sharedFolder;
5081 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5082 if (FAILED(hrc)) return hrc;
5083
5084 i_setModified(IsModified_SharedFolders);
5085 mHWData.backup();
5086 mHWData->mSharedFolders.remove(sharedFolder);
5087
5088 /* inform the direct session if any */
5089 alock.release();
5090 i_onSharedFolderChange(FALSE);
5091
5092 return S_OK;
5093}
5094
5095HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5096{
5097 /* start with No */
5098 *aCanShow = FALSE;
5099
5100 ComPtr<IInternalSessionControl> directControl;
5101 {
5102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5103
5104 if (mData->mSession.mState != SessionState_Locked)
5105 return setError(VBOX_E_INVALID_VM_STATE,
5106 tr("Machine is not locked for session (session state: %s)"),
5107 Global::stringifySessionState(mData->mSession.mState));
5108
5109 if (mData->mSession.mLockType == LockType_VM)
5110 directControl = mData->mSession.mDirectControl;
5111 }
5112
5113 /* ignore calls made after #OnSessionEnd() is called */
5114 if (!directControl)
5115 return S_OK;
5116
5117 LONG64 dummy;
5118 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5119}
5120
5121HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5122{
5123 ComPtr<IInternalSessionControl> directControl;
5124 {
5125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5126
5127 if (mData->mSession.mState != SessionState_Locked)
5128 return setError(E_FAIL,
5129 tr("Machine is not locked for session (session state: %s)"),
5130 Global::stringifySessionState(mData->mSession.mState));
5131
5132 if (mData->mSession.mLockType == LockType_VM)
5133 directControl = mData->mSession.mDirectControl;
5134 }
5135
5136 /* ignore calls made after #OnSessionEnd() is called */
5137 if (!directControl)
5138 return S_OK;
5139
5140 BOOL dummy;
5141 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5142}
5143
5144#ifdef VBOX_WITH_GUEST_PROPS
5145/**
5146 * Look up a guest property in VBoxSVC's internal structures.
5147 */
5148HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5149 com::Utf8Str &aValue,
5150 LONG64 *aTimestamp,
5151 com::Utf8Str &aFlags) const
5152{
5153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5156 if (it != mHWData->mGuestProperties.end())
5157 {
5158 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5159 aValue = it->second.strValue;
5160 *aTimestamp = it->second.mTimestamp;
5161 GuestPropWriteFlags(it->second.mFlags, szFlags);
5162 aFlags = Utf8Str(szFlags);
5163 }
5164
5165 return S_OK;
5166}
5167
5168/**
5169 * Query the VM that a guest property belongs to for the property.
5170 * @returns E_ACCESSDENIED if the VM process is not available or not
5171 * currently handling queries and the lookup should then be done in
5172 * VBoxSVC.
5173 */
5174HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5175 com::Utf8Str &aValue,
5176 LONG64 *aTimestamp,
5177 com::Utf8Str &aFlags) const
5178{
5179 HRESULT hrc = S_OK;
5180 Bstr bstrValue;
5181 Bstr bstrFlags;
5182
5183 ComPtr<IInternalSessionControl> directControl;
5184 {
5185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5186 if (mData->mSession.mLockType == LockType_VM)
5187 directControl = mData->mSession.mDirectControl;
5188 }
5189
5190 /* ignore calls made after #OnSessionEnd() is called */
5191 if (!directControl)
5192 hrc = E_ACCESSDENIED;
5193 else
5194 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5195 0 /* accessMode */,
5196 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5197
5198 aValue = bstrValue;
5199 aFlags = bstrFlags;
5200
5201 return hrc;
5202}
5203#endif // VBOX_WITH_GUEST_PROPS
5204
5205HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5206 com::Utf8Str &aValue,
5207 LONG64 *aTimestamp,
5208 com::Utf8Str &aFlags)
5209{
5210#ifndef VBOX_WITH_GUEST_PROPS
5211 ReturnComNotImplemented();
5212#else // VBOX_WITH_GUEST_PROPS
5213
5214 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5215
5216 if (hrc == E_ACCESSDENIED)
5217 /* The VM is not running or the service is not (yet) accessible */
5218 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5219 return hrc;
5220#endif // VBOX_WITH_GUEST_PROPS
5221}
5222
5223HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5224{
5225 LONG64 dummyTimestamp;
5226 com::Utf8Str dummyFlags;
5227 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5228
5229}
5230HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5231{
5232 com::Utf8Str dummyFlags;
5233 com::Utf8Str dummyValue;
5234 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5235}
5236
5237#ifdef VBOX_WITH_GUEST_PROPS
5238/**
5239 * Set a guest property in VBoxSVC's internal structures.
5240 */
5241HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5242 const com::Utf8Str &aFlags, bool fDelete)
5243{
5244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5245 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5246 if (FAILED(hrc)) return hrc;
5247
5248 try
5249 {
5250 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5251 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5252 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5253
5254 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5255 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5256
5257 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5258 if (it == mHWData->mGuestProperties.end())
5259 {
5260 if (!fDelete)
5261 {
5262 i_setModified(IsModified_MachineData);
5263 mHWData.backupEx();
5264
5265 RTTIMESPEC time;
5266 HWData::GuestProperty prop;
5267 prop.strValue = aValue;
5268 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5269 prop.mFlags = fFlags;
5270 mHWData->mGuestProperties[aName] = prop;
5271 }
5272 }
5273 else
5274 {
5275 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5276 {
5277 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5278 }
5279 else
5280 {
5281 i_setModified(IsModified_MachineData);
5282 mHWData.backupEx();
5283
5284 /* The backupEx() operation invalidates our iterator,
5285 * so get a new one. */
5286 it = mHWData->mGuestProperties.find(aName);
5287 Assert(it != mHWData->mGuestProperties.end());
5288
5289 if (!fDelete)
5290 {
5291 RTTIMESPEC time;
5292 it->second.strValue = aValue;
5293 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5294 it->second.mFlags = fFlags;
5295 }
5296 else
5297 mHWData->mGuestProperties.erase(it);
5298 }
5299 }
5300
5301 if (SUCCEEDED(hrc))
5302 {
5303 alock.release();
5304
5305 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5306 }
5307 }
5308 catch (std::bad_alloc &)
5309 {
5310 hrc = E_OUTOFMEMORY;
5311 }
5312
5313 return hrc;
5314}
5315
5316/**
5317 * Set a property on the VM that that property belongs to.
5318 * @returns E_ACCESSDENIED if the VM process is not available or not
5319 * currently handling queries and the setting should then be done in
5320 * VBoxSVC.
5321 */
5322HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5323 const com::Utf8Str &aFlags, bool fDelete)
5324{
5325 HRESULT hrc;
5326
5327 try
5328 {
5329 ComPtr<IInternalSessionControl> directControl;
5330 {
5331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5332 if (mData->mSession.mLockType == LockType_VM)
5333 directControl = mData->mSession.mDirectControl;
5334 }
5335
5336 Bstr dummy1; /* will not be changed (setter) */
5337 Bstr dummy2; /* will not be changed (setter) */
5338 LONG64 dummy64;
5339 if (!directControl)
5340 hrc = E_ACCESSDENIED;
5341 else
5342 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5343 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5344 fDelete ? 2 : 1 /* accessMode */,
5345 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5346 }
5347 catch (std::bad_alloc &)
5348 {
5349 hrc = E_OUTOFMEMORY;
5350 }
5351
5352 return hrc;
5353}
5354#endif // VBOX_WITH_GUEST_PROPS
5355
5356HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5357 const com::Utf8Str &aFlags)
5358{
5359#ifndef VBOX_WITH_GUEST_PROPS
5360 ReturnComNotImplemented();
5361#else // VBOX_WITH_GUEST_PROPS
5362
5363 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5364 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5365
5366 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5367 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5368
5369 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5370 if (hrc == E_ACCESSDENIED)
5371 /* The VM is not running or the service is not (yet) accessible */
5372 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5373 return hrc;
5374#endif // VBOX_WITH_GUEST_PROPS
5375}
5376
5377HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5378{
5379 return setGuestProperty(aProperty, aValue, "");
5380}
5381
5382HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5383{
5384#ifndef VBOX_WITH_GUEST_PROPS
5385 ReturnComNotImplemented();
5386#else // VBOX_WITH_GUEST_PROPS
5387 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5388 if (hrc == E_ACCESSDENIED)
5389 /* The VM is not running or the service is not (yet) accessible */
5390 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5391 return hrc;
5392#endif // VBOX_WITH_GUEST_PROPS
5393}
5394
5395#ifdef VBOX_WITH_GUEST_PROPS
5396/**
5397 * Enumerate the guest properties in VBoxSVC's internal structures.
5398 */
5399HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5400 std::vector<com::Utf8Str> &aNames,
5401 std::vector<com::Utf8Str> &aValues,
5402 std::vector<LONG64> &aTimestamps,
5403 std::vector<com::Utf8Str> &aFlags)
5404{
5405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5406 Utf8Str strPatterns(aPatterns);
5407
5408 /*
5409 * Look for matching patterns and build up a list.
5410 */
5411 HWData::GuestPropertyMap propMap;
5412 for (HWData::GuestPropertyMap::const_iterator
5413 it = mHWData->mGuestProperties.begin();
5414 it != mHWData->mGuestProperties.end();
5415 ++it)
5416 {
5417 if ( strPatterns.isEmpty()
5418 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5419 RTSTR_MAX,
5420 it->first.c_str(),
5421 RTSTR_MAX,
5422 NULL)
5423 )
5424 propMap.insert(*it);
5425 }
5426
5427 alock.release();
5428
5429 /*
5430 * And build up the arrays for returning the property information.
5431 */
5432 size_t cEntries = propMap.size();
5433
5434 aNames.resize(cEntries);
5435 aValues.resize(cEntries);
5436 aTimestamps.resize(cEntries);
5437 aFlags.resize(cEntries);
5438
5439 size_t i = 0;
5440 for (HWData::GuestPropertyMap::const_iterator
5441 it = propMap.begin();
5442 it != propMap.end();
5443 ++it, ++i)
5444 {
5445 aNames[i] = it->first;
5446 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5447 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5448
5449 aValues[i] = it->second.strValue;
5450 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5451 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5452
5453 aTimestamps[i] = it->second.mTimestamp;
5454
5455 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5456 GuestPropWriteFlags(it->second.mFlags, szFlags);
5457 aFlags[i] = szFlags;
5458 }
5459
5460 return S_OK;
5461}
5462
5463/**
5464 * Enumerate the properties managed by a VM.
5465 * @returns E_ACCESSDENIED if the VM process is not available or not
5466 * currently handling queries and the setting should then be done in
5467 * VBoxSVC.
5468 */
5469HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5470 std::vector<com::Utf8Str> &aNames,
5471 std::vector<com::Utf8Str> &aValues,
5472 std::vector<LONG64> &aTimestamps,
5473 std::vector<com::Utf8Str> &aFlags)
5474{
5475 HRESULT hrc;
5476 ComPtr<IInternalSessionControl> directControl;
5477 {
5478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5479 if (mData->mSession.mLockType == LockType_VM)
5480 directControl = mData->mSession.mDirectControl;
5481 }
5482
5483 com::SafeArray<BSTR> bNames;
5484 com::SafeArray<BSTR> bValues;
5485 com::SafeArray<LONG64> bTimestamps;
5486 com::SafeArray<BSTR> bFlags;
5487
5488 if (!directControl)
5489 hrc = E_ACCESSDENIED;
5490 else
5491 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5492 ComSafeArrayAsOutParam(bNames),
5493 ComSafeArrayAsOutParam(bValues),
5494 ComSafeArrayAsOutParam(bTimestamps),
5495 ComSafeArrayAsOutParam(bFlags));
5496 size_t i;
5497 aNames.resize(bNames.size());
5498 for (i = 0; i < bNames.size(); ++i)
5499 aNames[i] = Utf8Str(bNames[i]);
5500 aValues.resize(bValues.size());
5501 for (i = 0; i < bValues.size(); ++i)
5502 aValues[i] = Utf8Str(bValues[i]);
5503 aTimestamps.resize(bTimestamps.size());
5504 for (i = 0; i < bTimestamps.size(); ++i)
5505 aTimestamps[i] = bTimestamps[i];
5506 aFlags.resize(bFlags.size());
5507 for (i = 0; i < bFlags.size(); ++i)
5508 aFlags[i] = Utf8Str(bFlags[i]);
5509
5510 return hrc;
5511}
5512#endif // VBOX_WITH_GUEST_PROPS
5513HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5514 std::vector<com::Utf8Str> &aNames,
5515 std::vector<com::Utf8Str> &aValues,
5516 std::vector<LONG64> &aTimestamps,
5517 std::vector<com::Utf8Str> &aFlags)
5518{
5519#ifndef VBOX_WITH_GUEST_PROPS
5520 ReturnComNotImplemented();
5521#else // VBOX_WITH_GUEST_PROPS
5522
5523 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5524
5525 if (hrc == E_ACCESSDENIED)
5526 /* The VM is not running or the service is not (yet) accessible */
5527 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5528 return hrc;
5529#endif // VBOX_WITH_GUEST_PROPS
5530}
5531
5532HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5533 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5534{
5535 MediumAttachmentList atts;
5536
5537 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5538 if (FAILED(hrc)) return hrc;
5539
5540 aMediumAttachments.resize(atts.size());
5541 size_t i = 0;
5542 for (MediumAttachmentList::const_iterator
5543 it = atts.begin();
5544 it != atts.end();
5545 ++it, ++i)
5546 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5547
5548 return S_OK;
5549}
5550
5551HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5552 LONG aControllerPort,
5553 LONG aDevice,
5554 ComPtr<IMediumAttachment> &aAttachment)
5555{
5556 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5557 aName.c_str(), aControllerPort, aDevice));
5558
5559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5560
5561 aAttachment = NULL;
5562
5563 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5564 aName,
5565 aControllerPort,
5566 aDevice);
5567 if (pAttach.isNull())
5568 return setError(VBOX_E_OBJECT_NOT_FOUND,
5569 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5570 aDevice, aControllerPort, aName.c_str());
5571
5572 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5573
5574 return S_OK;
5575}
5576
5577
5578HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5579 StorageBus_T aConnectionType,
5580 ComPtr<IStorageController> &aController)
5581{
5582 if ( (aConnectionType <= StorageBus_Null)
5583 || (aConnectionType > StorageBus_VirtioSCSI))
5584 return setError(E_INVALIDARG,
5585 tr("Invalid connection type: %d"),
5586 aConnectionType);
5587
5588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5589
5590 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5591 if (FAILED(hrc)) return hrc;
5592
5593 /* try to find one with the name first. */
5594 ComObjPtr<StorageController> ctrl;
5595
5596 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5597 if (SUCCEEDED(hrc))
5598 return setError(VBOX_E_OBJECT_IN_USE,
5599 tr("Storage controller named '%s' already exists"),
5600 aName.c_str());
5601
5602 ctrl.createObject();
5603
5604 /* get a new instance number for the storage controller */
5605 ULONG ulInstance = 0;
5606 bool fBootable = true;
5607 for (StorageControllerList::const_iterator
5608 it = mStorageControllers->begin();
5609 it != mStorageControllers->end();
5610 ++it)
5611 {
5612 if ((*it)->i_getStorageBus() == aConnectionType)
5613 {
5614 ULONG ulCurInst = (*it)->i_getInstance();
5615
5616 if (ulCurInst >= ulInstance)
5617 ulInstance = ulCurInst + 1;
5618
5619 /* Only one controller of each type can be marked as bootable. */
5620 if ((*it)->i_getBootable())
5621 fBootable = false;
5622 }
5623 }
5624
5625 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5626 if (FAILED(hrc)) return hrc;
5627
5628 i_setModified(IsModified_Storage);
5629 mStorageControllers.backup();
5630 mStorageControllers->push_back(ctrl);
5631
5632 ctrl.queryInterfaceTo(aController.asOutParam());
5633
5634 /* inform the direct session if any */
5635 alock.release();
5636 i_onStorageControllerChange(i_getId(), aName);
5637
5638 return S_OK;
5639}
5640
5641HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5642 ComPtr<IStorageController> &aStorageController)
5643{
5644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5645
5646 ComObjPtr<StorageController> ctrl;
5647
5648 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5649 if (SUCCEEDED(hrc))
5650 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5651
5652 return hrc;
5653}
5654
5655HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5656 ULONG aInstance,
5657 ComPtr<IStorageController> &aStorageController)
5658{
5659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5660
5661 for (StorageControllerList::const_iterator
5662 it = mStorageControllers->begin();
5663 it != mStorageControllers->end();
5664 ++it)
5665 {
5666 if ( (*it)->i_getStorageBus() == aConnectionType
5667 && (*it)->i_getInstance() == aInstance)
5668 {
5669 (*it).queryInterfaceTo(aStorageController.asOutParam());
5670 return S_OK;
5671 }
5672 }
5673
5674 return setError(VBOX_E_OBJECT_NOT_FOUND,
5675 tr("Could not find a storage controller with instance number '%lu'"),
5676 aInstance);
5677}
5678
5679HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5680{
5681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5682
5683 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5684 if (FAILED(hrc)) return hrc;
5685
5686 ComObjPtr<StorageController> ctrl;
5687
5688 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5689 if (SUCCEEDED(hrc))
5690 {
5691 /* Ensure that only one controller of each type is marked as bootable. */
5692 if (aBootable == TRUE)
5693 {
5694 for (StorageControllerList::const_iterator
5695 it = mStorageControllers->begin();
5696 it != mStorageControllers->end();
5697 ++it)
5698 {
5699 ComObjPtr<StorageController> aCtrl = (*it);
5700
5701 if ( (aCtrl->i_getName() != aName)
5702 && aCtrl->i_getBootable() == TRUE
5703 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5704 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5705 {
5706 aCtrl->i_setBootable(FALSE);
5707 break;
5708 }
5709 }
5710 }
5711
5712 if (SUCCEEDED(hrc))
5713 {
5714 ctrl->i_setBootable(aBootable);
5715 i_setModified(IsModified_Storage);
5716 }
5717 }
5718
5719 if (SUCCEEDED(hrc))
5720 {
5721 /* inform the direct session if any */
5722 alock.release();
5723 i_onStorageControllerChange(i_getId(), aName);
5724 }
5725
5726 return hrc;
5727}
5728
5729HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5730{
5731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5732
5733 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5734 if (FAILED(hrc)) return hrc;
5735
5736 ComObjPtr<StorageController> ctrl;
5737 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5738 if (FAILED(hrc)) return hrc;
5739
5740 MediumAttachmentList llDetachedAttachments;
5741 {
5742 /* find all attached devices to the appropriate storage controller and detach them all */
5743 // make a temporary list because detachDevice invalidates iterators into
5744 // mMediumAttachments
5745 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5746
5747 for (MediumAttachmentList::const_iterator
5748 it = llAttachments2.begin();
5749 it != llAttachments2.end();
5750 ++it)
5751 {
5752 MediumAttachment *pAttachTemp = *it;
5753
5754 AutoCaller localAutoCaller(pAttachTemp);
5755 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
5756
5757 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5758
5759 if (pAttachTemp->i_getControllerName() == aName)
5760 {
5761 llDetachedAttachments.push_back(pAttachTemp);
5762 hrc = i_detachDevice(pAttachTemp, alock, NULL);
5763 if (FAILED(hrc)) return hrc;
5764 }
5765 }
5766 }
5767
5768 /* send event about detached devices before removing parent controller */
5769 for (MediumAttachmentList::const_iterator
5770 it = llDetachedAttachments.begin();
5771 it != llDetachedAttachments.end();
5772 ++it)
5773 {
5774 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5775 }
5776
5777 /* We can remove it now. */
5778 i_setModified(IsModified_Storage);
5779 mStorageControllers.backup();
5780
5781 ctrl->i_unshare();
5782
5783 mStorageControllers->remove(ctrl);
5784
5785 /* inform the direct session if any */
5786 alock.release();
5787 i_onStorageControllerChange(i_getId(), aName);
5788
5789 return S_OK;
5790}
5791
5792HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5793 ComPtr<IUSBController> &aController)
5794{
5795 if ( (aType <= USBControllerType_Null)
5796 || (aType >= USBControllerType_Last))
5797 return setError(E_INVALIDARG,
5798 tr("Invalid USB controller type: %d"),
5799 aType);
5800
5801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5802
5803 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5804 if (FAILED(hrc)) return hrc;
5805
5806 /* try to find one with the same type first. */
5807 ComObjPtr<USBController> ctrl;
5808
5809 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5810 if (SUCCEEDED(hrc))
5811 return setError(VBOX_E_OBJECT_IN_USE,
5812 tr("USB controller named '%s' already exists"),
5813 aName.c_str());
5814
5815 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5816 ChipsetType_T enmChipsetType;
5817 hrc = mPlatform->getChipsetType(&enmChipsetType);
5818 if (FAILED(hrc))
5819 return hrc;
5820
5821 ULONG maxInstances;
5822 hrc = mPlatformProperties->GetMaxInstancesOfUSBControllerType(enmChipsetType, aType, &maxInstances);
5823 if (FAILED(hrc))
5824 return hrc;
5825
5826 ULONG cInstances = i_getUSBControllerCountByType(aType);
5827 if (cInstances >= maxInstances)
5828 return setError(E_INVALIDARG,
5829 tr("Too many USB controllers of this type"));
5830
5831 ctrl.createObject();
5832
5833 hrc = ctrl->init(this, aName, aType);
5834 if (FAILED(hrc)) return hrc;
5835
5836 i_setModified(IsModified_USB);
5837 mUSBControllers.backup();
5838 mUSBControllers->push_back(ctrl);
5839
5840 ctrl.queryInterfaceTo(aController.asOutParam());
5841
5842 /* inform the direct session if any */
5843 alock.release();
5844 i_onUSBControllerChange();
5845
5846 return S_OK;
5847}
5848
5849HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
5850{
5851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5852
5853 ComObjPtr<USBController> ctrl;
5854
5855 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5856 if (SUCCEEDED(hrc))
5857 ctrl.queryInterfaceTo(aController.asOutParam());
5858
5859 return hrc;
5860}
5861
5862HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
5863 ULONG *aControllers)
5864{
5865 if ( (aType <= USBControllerType_Null)
5866 || (aType >= USBControllerType_Last))
5867 return setError(E_INVALIDARG,
5868 tr("Invalid USB controller type: %d"),
5869 aType);
5870
5871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5872
5873 ComObjPtr<USBController> ctrl;
5874
5875 *aControllers = i_getUSBControllerCountByType(aType);
5876
5877 return S_OK;
5878}
5879
5880HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
5881{
5882
5883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5884
5885 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5886 if (FAILED(hrc)) return hrc;
5887
5888 ComObjPtr<USBController> ctrl;
5889 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5890 if (FAILED(hrc)) return hrc;
5891
5892 i_setModified(IsModified_USB);
5893 mUSBControllers.backup();
5894
5895 ctrl->i_unshare();
5896
5897 mUSBControllers->remove(ctrl);
5898
5899 /* inform the direct session if any */
5900 alock.release();
5901 i_onUSBControllerChange();
5902
5903 return S_OK;
5904}
5905
5906HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
5907 ULONG *aOriginX,
5908 ULONG *aOriginY,
5909 ULONG *aWidth,
5910 ULONG *aHeight,
5911 BOOL *aEnabled)
5912{
5913 uint32_t u32OriginX= 0;
5914 uint32_t u32OriginY= 0;
5915 uint32_t u32Width = 0;
5916 uint32_t u32Height = 0;
5917 uint16_t u16Flags = 0;
5918
5919#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5920 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5921#else
5922 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5923#endif
5924 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
5925 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5926 if (RT_FAILURE(vrc))
5927 {
5928#ifdef RT_OS_WINDOWS
5929 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5930 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5931 * So just assign fEnable to TRUE again.
5932 * The right fix would be to change GUI API wrappers to make sure that parameters
5933 * are changed only if API succeeds.
5934 */
5935 *aEnabled = TRUE;
5936#endif
5937 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5938 tr("Saved guest size is not available (%Rrc)"),
5939 vrc);
5940 }
5941
5942 *aOriginX = u32OriginX;
5943 *aOriginY = u32OriginY;
5944 *aWidth = u32Width;
5945 *aHeight = u32Height;
5946 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5947
5948 return S_OK;
5949}
5950
5951HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
5952 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
5953{
5954 if (aScreenId != 0)
5955 return E_NOTIMPL;
5956
5957 if ( aBitmapFormat != BitmapFormat_BGR0
5958 && aBitmapFormat != BitmapFormat_BGRA
5959 && aBitmapFormat != BitmapFormat_RGBA
5960 && aBitmapFormat != BitmapFormat_PNG)
5961 return setError(E_NOTIMPL,
5962 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
5963
5964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5965
5966 uint8_t *pu8Data = NULL;
5967 uint32_t cbData = 0;
5968 uint32_t u32Width = 0;
5969 uint32_t u32Height = 0;
5970
5971#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5972 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5973#else
5974 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5975#endif
5976 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
5977 &pu8Data, &cbData, &u32Width, &u32Height);
5978 if (RT_FAILURE(vrc))
5979 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5980 tr("Saved thumbnail data is not available (%Rrc)"),
5981 vrc);
5982
5983 HRESULT hrc = S_OK;
5984
5985 *aWidth = u32Width;
5986 *aHeight = u32Height;
5987
5988 if (cbData > 0)
5989 {
5990 /* Convert pixels to the format expected by the API caller. */
5991 if (aBitmapFormat == BitmapFormat_BGR0)
5992 {
5993 /* [0] B, [1] G, [2] R, [3] 0. */
5994 aData.resize(cbData);
5995 memcpy(&aData.front(), pu8Data, cbData);
5996 }
5997 else if (aBitmapFormat == BitmapFormat_BGRA)
5998 {
5999 /* [0] B, [1] G, [2] R, [3] A. */
6000 aData.resize(cbData);
6001 for (uint32_t i = 0; i < cbData; i += 4)
6002 {
6003 aData[i] = pu8Data[i];
6004 aData[i + 1] = pu8Data[i + 1];
6005 aData[i + 2] = pu8Data[i + 2];
6006 aData[i + 3] = 0xff;
6007 }
6008 }
6009 else if (aBitmapFormat == BitmapFormat_RGBA)
6010 {
6011 /* [0] R, [1] G, [2] B, [3] A. */
6012 aData.resize(cbData);
6013 for (uint32_t i = 0; i < cbData; i += 4)
6014 {
6015 aData[i] = pu8Data[i + 2];
6016 aData[i + 1] = pu8Data[i + 1];
6017 aData[i + 2] = pu8Data[i];
6018 aData[i + 3] = 0xff;
6019 }
6020 }
6021 else if (aBitmapFormat == BitmapFormat_PNG)
6022 {
6023 uint8_t *pu8PNG = NULL;
6024 uint32_t cbPNG = 0;
6025 uint32_t cxPNG = 0;
6026 uint32_t cyPNG = 0;
6027
6028 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6029
6030 if (RT_SUCCESS(vrc))
6031 {
6032 aData.resize(cbPNG);
6033 if (cbPNG)
6034 memcpy(&aData.front(), pu8PNG, cbPNG);
6035 }
6036 else
6037 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6038
6039 RTMemFree(pu8PNG);
6040 }
6041 }
6042
6043 freeSavedDisplayScreenshot(pu8Data);
6044
6045 return hrc;
6046}
6047
6048HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6049 ULONG *aWidth,
6050 ULONG *aHeight,
6051 std::vector<BitmapFormat_T> &aBitmapFormats)
6052{
6053 if (aScreenId != 0)
6054 return E_NOTIMPL;
6055
6056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 uint8_t *pu8Data = NULL;
6059 uint32_t cbData = 0;
6060 uint32_t u32Width = 0;
6061 uint32_t u32Height = 0;
6062
6063#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6064 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6065#else
6066 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6067#endif
6068 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6069 &pu8Data, &cbData, &u32Width, &u32Height);
6070
6071 if (RT_FAILURE(vrc))
6072 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6073 tr("Saved screenshot data is not available (%Rrc)"),
6074 vrc);
6075
6076 *aWidth = u32Width;
6077 *aHeight = u32Height;
6078 aBitmapFormats.resize(1);
6079 aBitmapFormats[0] = BitmapFormat_PNG;
6080
6081 freeSavedDisplayScreenshot(pu8Data);
6082
6083 return S_OK;
6084}
6085
6086HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6087 BitmapFormat_T aBitmapFormat,
6088 ULONG *aWidth,
6089 ULONG *aHeight,
6090 std::vector<BYTE> &aData)
6091{
6092 if (aScreenId != 0)
6093 return E_NOTIMPL;
6094
6095 if (aBitmapFormat != BitmapFormat_PNG)
6096 return E_NOTIMPL;
6097
6098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6099
6100 uint8_t *pu8Data = NULL;
6101 uint32_t cbData = 0;
6102 uint32_t u32Width = 0;
6103 uint32_t u32Height = 0;
6104
6105#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6106 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6107#else
6108 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6109#endif
6110 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6111 &pu8Data, &cbData, &u32Width, &u32Height);
6112
6113 if (RT_FAILURE(vrc))
6114 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6115 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6116 vrc);
6117
6118 *aWidth = u32Width;
6119 *aHeight = u32Height;
6120
6121 aData.resize(cbData);
6122 if (cbData)
6123 memcpy(&aData.front(), pu8Data, cbData);
6124
6125 freeSavedDisplayScreenshot(pu8Data);
6126
6127 return S_OK;
6128}
6129
6130HRESULT Machine::hotPlugCPU(ULONG aCpu)
6131{
6132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6133
6134 if (!mHWData->mCPUHotPlugEnabled)
6135 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6136
6137 if (aCpu >= mHWData->mCPUCount)
6138 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6139
6140 if (mHWData->mCPUAttached[aCpu])
6141 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6142
6143 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6144 if (FAILED(hrc)) return hrc;
6145
6146 alock.release();
6147 hrc = i_onCPUChange(aCpu, false);
6148 alock.acquire();
6149 if (FAILED(hrc)) return hrc;
6150
6151 i_setModified(IsModified_MachineData);
6152 mHWData.backup();
6153 mHWData->mCPUAttached[aCpu] = true;
6154
6155 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6156 if (Global::IsOnline(mData->mMachineState))
6157 i_saveSettings(NULL, alock);
6158
6159 return S_OK;
6160}
6161
6162HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6163{
6164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6165
6166 if (!mHWData->mCPUHotPlugEnabled)
6167 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6168
6169 if (aCpu >= SchemaDefs::MaxCPUCount)
6170 return setError(E_INVALIDARG,
6171 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6172 SchemaDefs::MaxCPUCount);
6173
6174 if (!mHWData->mCPUAttached[aCpu])
6175 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6176
6177 /* CPU 0 can't be detached */
6178 if (aCpu == 0)
6179 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6180
6181 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6182 if (FAILED(hrc)) return hrc;
6183
6184 alock.release();
6185 hrc = i_onCPUChange(aCpu, true);
6186 alock.acquire();
6187 if (FAILED(hrc)) return hrc;
6188
6189 i_setModified(IsModified_MachineData);
6190 mHWData.backup();
6191 mHWData->mCPUAttached[aCpu] = false;
6192
6193 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6194 if (Global::IsOnline(mData->mMachineState))
6195 i_saveSettings(NULL, alock);
6196
6197 return S_OK;
6198}
6199
6200HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6201{
6202 *aAttached = false;
6203
6204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6205
6206 /* If hotplug is enabled the CPU is always enabled. */
6207 if (!mHWData->mCPUHotPlugEnabled)
6208 {
6209 if (aCpu < mHWData->mCPUCount)
6210 *aAttached = true;
6211 }
6212 else
6213 {
6214 if (aCpu < SchemaDefs::MaxCPUCount)
6215 *aAttached = mHWData->mCPUAttached[aCpu];
6216 }
6217
6218 return S_OK;
6219}
6220
6221HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6222{
6223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 Utf8Str log = i_getLogFilename(aIdx);
6226 if (!RTFileExists(log.c_str()))
6227 log.setNull();
6228 aFilename = log;
6229
6230 return S_OK;
6231}
6232
6233HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6234{
6235 if (aSize < 0)
6236 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6237
6238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6239
6240 HRESULT hrc = S_OK;
6241 Utf8Str log = i_getLogFilename(aIdx);
6242
6243 /* do not unnecessarily hold the lock while doing something which does
6244 * not need the lock and potentially takes a long time. */
6245 alock.release();
6246
6247 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6248 * keeps the SOAP reply size under 1M for the webservice (we're using
6249 * base64 encoded strings for binary data for years now, avoiding the
6250 * expansion of each byte array element to approx. 25 bytes of XML. */
6251 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6252 aData.resize(cbData);
6253
6254 int vrc = VINF_SUCCESS;
6255 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6256
6257#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6258 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6259 {
6260 PCVBOXCRYPTOIF pCryptoIf = NULL;
6261 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6262 if (SUCCEEDED(hrc))
6263 {
6264 alock.acquire();
6265
6266 SecretKey *pKey = NULL;
6267 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6268 alock.release();
6269
6270 if (RT_SUCCESS(vrc))
6271 {
6272 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6273 if (RT_SUCCESS(vrc))
6274 {
6275 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6276 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6277 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6278 if (RT_SUCCESS(vrc))
6279 {
6280 RTVfsIoStrmRelease(hVfsIosLog);
6281 hVfsIosLog = hVfsIosLogDec;
6282 }
6283 }
6284
6285 pKey->release();
6286 }
6287
6288 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6289 }
6290 }
6291 else
6292 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6293#else
6294 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6295#endif
6296 if (RT_SUCCESS(vrc))
6297 {
6298 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6299 cbData ? &aData.front() : NULL, cbData,
6300 true /*fBlocking*/, &cbData);
6301 if (RT_SUCCESS(vrc))
6302 aData.resize(cbData);
6303 else
6304 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6305
6306 RTVfsIoStrmRelease(hVfsIosLog);
6307 }
6308 else
6309 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6310
6311 if (FAILED(hrc))
6312 aData.resize(0);
6313
6314 return hrc;
6315}
6316
6317
6318/**
6319 * Currently this method doesn't attach device to the running VM,
6320 * just makes sure it's plugged on next VM start.
6321 */
6322HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6323{
6324 // lock scope
6325 {
6326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6327
6328 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6329 if (FAILED(hrc)) return hrc;
6330
6331 ChipsetType_T aChipset = ChipsetType_PIIX3;
6332 hrc = mPlatform->COMGETTER(ChipsetType)(&aChipset);
6333 if (FAILED(hrc)) return hrc;
6334
6335 if (aChipset != ChipsetType_ICH9) /** @todo BUGBUG ASSUMES x86! */
6336 {
6337 return setError(E_INVALIDARG,
6338 tr("Host PCI attachment only supported with ICH9 chipset"));
6339 }
6340
6341 // check if device with this host PCI address already attached
6342 for (HWData::PCIDeviceAssignmentList::const_iterator
6343 it = mHWData->mPCIDeviceAssignments.begin();
6344 it != mHWData->mPCIDeviceAssignments.end();
6345 ++it)
6346 {
6347 LONG iHostAddress = -1;
6348 ComPtr<PCIDeviceAttachment> pAttach;
6349 pAttach = *it;
6350 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6351 if (iHostAddress == aHostAddress)
6352 return setError(E_INVALIDARG,
6353 tr("Device with host PCI address already attached to this VM"));
6354 }
6355
6356 ComObjPtr<PCIDeviceAttachment> pda;
6357 char name[32];
6358
6359 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6360 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6361 pda.createObject();
6362 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6363 i_setModified(IsModified_MachineData);
6364 mHWData.backup();
6365 mHWData->mPCIDeviceAssignments.push_back(pda);
6366 }
6367
6368 return S_OK;
6369}
6370
6371/**
6372 * Currently this method doesn't detach device from the running VM,
6373 * just makes sure it's not plugged on next VM start.
6374 */
6375HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6376{
6377 ComObjPtr<PCIDeviceAttachment> pAttach;
6378 bool fRemoved = false;
6379 HRESULT hrc;
6380
6381 // lock scope
6382 {
6383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6384
6385 hrc = i_checkStateDependency(MutableStateDep);
6386 if (FAILED(hrc)) return hrc;
6387
6388 for (HWData::PCIDeviceAssignmentList::const_iterator
6389 it = mHWData->mPCIDeviceAssignments.begin();
6390 it != mHWData->mPCIDeviceAssignments.end();
6391 ++it)
6392 {
6393 LONG iHostAddress = -1;
6394 pAttach = *it;
6395 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6396 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6397 {
6398 i_setModified(IsModified_MachineData);
6399 mHWData.backup();
6400 mHWData->mPCIDeviceAssignments.remove(pAttach);
6401 fRemoved = true;
6402 break;
6403 }
6404 }
6405 }
6406
6407
6408 /* Fire event outside of the lock */
6409 if (fRemoved)
6410 {
6411 Assert(!pAttach.isNull());
6412 ComPtr<IEventSource> es;
6413 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
6414 Assert(SUCCEEDED(hrc));
6415 Bstr mid;
6416 hrc = this->COMGETTER(Id)(mid.asOutParam());
6417 Assert(SUCCEEDED(hrc));
6418 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6419 }
6420
6421 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6422 tr("No host PCI device %08x attached"),
6423 aHostAddress
6424 );
6425}
6426
6427HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6428{
6429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6430
6431 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6432 size_t i = 0;
6433 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6434 it = mHWData->mPCIDeviceAssignments.begin();
6435 it != mHWData->mPCIDeviceAssignments.end();
6436 ++it, ++i)
6437 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6438
6439 return S_OK;
6440}
6441
6442HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6443{
6444 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6445
6446 return S_OK;
6447}
6448
6449HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6450{
6451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6452
6453 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6454
6455 return S_OK;
6456}
6457
6458HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6459{
6460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6461 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6462 if (SUCCEEDED(hrc))
6463 {
6464 hrc = mHWData.backupEx();
6465 if (SUCCEEDED(hrc))
6466 {
6467 i_setModified(IsModified_MachineData);
6468 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6469 }
6470 }
6471 return hrc;
6472}
6473
6474HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6475{
6476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6477 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6478 return S_OK;
6479}
6480
6481HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6482{
6483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6484 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6485 if (SUCCEEDED(hrc))
6486 {
6487 hrc = mHWData.backupEx();
6488 if (SUCCEEDED(hrc))
6489 {
6490 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6491 if (SUCCEEDED(hrc))
6492 i_setModified(IsModified_MachineData);
6493 }
6494 }
6495 return hrc;
6496}
6497
6498HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6499{
6500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6501
6502 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6503
6504 return S_OK;
6505}
6506
6507HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6508{
6509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6510 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6511 if (SUCCEEDED(hrc))
6512 {
6513 hrc = mHWData.backupEx();
6514 if (SUCCEEDED(hrc))
6515 {
6516 i_setModified(IsModified_MachineData);
6517 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6518 }
6519 }
6520 return hrc;
6521}
6522
6523HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6524{
6525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6526
6527 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6533{
6534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6535
6536 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6537 if ( SUCCEEDED(hrc)
6538 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6539 {
6540 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6541 int vrc;
6542
6543 if (aAutostartEnabled)
6544 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6545 else
6546 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6547
6548 if (RT_SUCCESS(vrc))
6549 {
6550 hrc = mHWData.backupEx();
6551 if (SUCCEEDED(hrc))
6552 {
6553 i_setModified(IsModified_MachineData);
6554 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6555 }
6556 }
6557 else if (vrc == VERR_NOT_SUPPORTED)
6558 hrc = setError(VBOX_E_NOT_SUPPORTED,
6559 tr("The VM autostart feature is not supported on this platform"));
6560 else if (vrc == VERR_PATH_NOT_FOUND)
6561 hrc = setError(E_FAIL,
6562 tr("The path to the autostart database is not set"));
6563 else
6564 hrc = setError(E_UNEXPECTED,
6565 aAutostartEnabled ?
6566 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6567 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6568 mUserData->s.strName.c_str(), vrc);
6569 }
6570 return hrc;
6571}
6572
6573HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6574{
6575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6576
6577 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6578
6579 return S_OK;
6580}
6581
6582HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6583{
6584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6585 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6586 if (SUCCEEDED(hrc))
6587 {
6588 hrc = mHWData.backupEx();
6589 if (SUCCEEDED(hrc))
6590 {
6591 i_setModified(IsModified_MachineData);
6592 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6593 }
6594 }
6595 return hrc;
6596}
6597
6598HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6599{
6600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6601
6602 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6603
6604 return S_OK;
6605}
6606
6607HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6608{
6609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6610 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6611 if ( SUCCEEDED(hrc)
6612 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6613 {
6614 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6615 int vrc;
6616
6617 if (aAutostopType != AutostopType_Disabled)
6618 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6619 else
6620 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6621
6622 if (RT_SUCCESS(vrc))
6623 {
6624 hrc = mHWData.backupEx();
6625 if (SUCCEEDED(hrc))
6626 {
6627 i_setModified(IsModified_MachineData);
6628 mHWData->mAutostart.enmAutostopType = aAutostopType;
6629 }
6630 }
6631 else if (vrc == VERR_NOT_SUPPORTED)
6632 hrc = setError(VBOX_E_NOT_SUPPORTED,
6633 tr("The VM autostop feature is not supported on this platform"));
6634 else if (vrc == VERR_PATH_NOT_FOUND)
6635 hrc = setError(E_FAIL,
6636 tr("The path to the autostart database is not set"));
6637 else
6638 hrc = setError(E_UNEXPECTED,
6639 aAutostopType != AutostopType_Disabled ?
6640 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6641 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6642 mUserData->s.strName.c_str(), vrc);
6643 }
6644 return hrc;
6645}
6646
6647HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6648{
6649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6650
6651 aDefaultFrontend = mHWData->mDefaultFrontend;
6652
6653 return S_OK;
6654}
6655
6656HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6657{
6658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6659 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6660 if (SUCCEEDED(hrc))
6661 {
6662 hrc = mHWData.backupEx();
6663 if (SUCCEEDED(hrc))
6664 {
6665 i_setModified(IsModified_MachineData);
6666 mHWData->mDefaultFrontend = aDefaultFrontend;
6667 }
6668 }
6669 return hrc;
6670}
6671
6672HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6673{
6674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6675 size_t cbIcon = mUserData->s.ovIcon.size();
6676 aIcon.resize(cbIcon);
6677 if (cbIcon)
6678 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6679 return S_OK;
6680}
6681
6682HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6683{
6684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6685 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6686 if (SUCCEEDED(hrc))
6687 {
6688 i_setModified(IsModified_MachineData);
6689 mUserData.backup();
6690 size_t cbIcon = aIcon.size();
6691 mUserData->s.ovIcon.resize(cbIcon);
6692 if (cbIcon)
6693 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6694 }
6695 return hrc;
6696}
6697
6698HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6699{
6700#ifdef VBOX_WITH_USB
6701 *aUSBProxyAvailable = true;
6702#else
6703 *aUSBProxyAvailable = false;
6704#endif
6705 return S_OK;
6706}
6707
6708HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6709{
6710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6711
6712 *aVMProcessPriority = mUserData->s.enmVMPriority;
6713
6714 return S_OK;
6715}
6716
6717HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6718{
6719 RT_NOREF(aVMProcessPriority);
6720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6721 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6722 if (SUCCEEDED(hrc))
6723 {
6724 hrc = mUserData.backupEx();
6725 if (SUCCEEDED(hrc))
6726 {
6727 i_setModified(IsModified_MachineData);
6728 mUserData->s.enmVMPriority = aVMProcessPriority;
6729 }
6730 }
6731 alock.release();
6732 if (SUCCEEDED(hrc))
6733 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6734 return hrc;
6735}
6736
6737HRESULT Machine::getVMExecutionEngine(VMExecutionEngine_T *aVMExecutionEngine)
6738{
6739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6740
6741 *aVMExecutionEngine = mUserData->s.enmExecEngine;
6742
6743 return S_OK;
6744}
6745
6746HRESULT Machine::setVMExecutionEngine(VMExecutionEngine_T aVMExecutionEngine)
6747{
6748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6749 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6750 if (SUCCEEDED(hrc))
6751 {
6752 hrc = mUserData.backupEx();
6753 if (SUCCEEDED(hrc))
6754 {
6755 i_setModified(IsModified_MachineData);
6756 mUserData->s.enmExecEngine = aVMExecutionEngine;
6757 }
6758 }
6759 return hrc;
6760}
6761
6762HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6763 ComPtr<IProgress> &aProgress)
6764{
6765 ComObjPtr<Progress> pP;
6766 Progress *ppP = pP;
6767 IProgress *iP = static_cast<IProgress *>(ppP);
6768 IProgress **pProgress = &iP;
6769
6770 IMachine *pTarget = aTarget;
6771
6772 /* Convert the options. */
6773 RTCList<CloneOptions_T> optList;
6774 if (aOptions.size())
6775 for (size_t i = 0; i < aOptions.size(); ++i)
6776 optList.append(aOptions[i]);
6777
6778 if (optList.contains(CloneOptions_Link))
6779 {
6780 if (!i_isSnapshotMachine())
6781 return setError(E_INVALIDARG,
6782 tr("Linked clone can only be created from a snapshot"));
6783 if (aMode != CloneMode_MachineState)
6784 return setError(E_INVALIDARG,
6785 tr("Linked clone can only be created for a single machine state"));
6786 }
6787 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6788
6789 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6790
6791 HRESULT hrc = pWorker->start(pProgress);
6792
6793 pP = static_cast<Progress *>(*pProgress);
6794 pP.queryInterfaceTo(aProgress.asOutParam());
6795
6796 return hrc;
6797
6798}
6799
6800HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6801 const com::Utf8Str &aType,
6802 ComPtr<IProgress> &aProgress)
6803{
6804 LogFlowThisFuncEnter();
6805
6806 ComObjPtr<Progress> ptrProgress;
6807 HRESULT hrc = ptrProgress.createObject();
6808 if (SUCCEEDED(hrc))
6809 {
6810 com::Utf8Str strDefaultPath;
6811 if (aTargetPath.isEmpty())
6812 i_calculateFullPath(".", strDefaultPath);
6813
6814 /* Initialize our worker task */
6815 MachineMoveVM *pTask = NULL;
6816 try
6817 {
6818 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6819 }
6820 catch (std::bad_alloc &)
6821 {
6822 return E_OUTOFMEMORY;
6823 }
6824
6825 hrc = pTask->init();//no exceptions are thrown
6826
6827 if (SUCCEEDED(hrc))
6828 {
6829 hrc = pTask->createThread();
6830 pTask = NULL; /* Consumed by createThread(). */
6831 if (SUCCEEDED(hrc))
6832 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6833 else
6834 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6835 }
6836 else
6837 delete pTask;
6838 }
6839
6840 LogFlowThisFuncLeave();
6841 return hrc;
6842
6843}
6844
6845HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6846{
6847 NOREF(aProgress);
6848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6849
6850 // This check should always fail.
6851 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6852 if (FAILED(hrc)) return hrc;
6853
6854 AssertFailedReturn(E_NOTIMPL);
6855}
6856
6857HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6858{
6859 NOREF(aSavedStateFile);
6860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6861
6862 // This check should always fail.
6863 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6864 if (FAILED(hrc)) return hrc;
6865
6866 AssertFailedReturn(E_NOTIMPL);
6867}
6868
6869HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6870{
6871 NOREF(aFRemoveFile);
6872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6873
6874 // This check should always fail.
6875 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6876 if (FAILED(hrc)) return hrc;
6877
6878 AssertFailedReturn(E_NOTIMPL);
6879}
6880
6881// public methods for internal purposes
6882/////////////////////////////////////////////////////////////////////////////
6883
6884/**
6885 * Adds the given IsModified_* flag to the dirty flags of the machine.
6886 * This must be called either during i_loadSettings or under the machine write lock.
6887 * @param fl Flag
6888 * @param fAllowStateModification If state modifications are allowed.
6889 */
6890void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6891{
6892 mData->flModifications |= fl;
6893 if (fAllowStateModification && i_isStateModificationAllowed())
6894 mData->mCurrentStateModified = true;
6895}
6896
6897/**
6898 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6899 * care of the write locking.
6900 *
6901 * @param fModification The flag to add.
6902 * @param fAllowStateModification If state modifications are allowed.
6903 */
6904void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6905{
6906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6907 i_setModified(fModification, fAllowStateModification);
6908}
6909
6910/**
6911 * Saves the registry entry of this machine to the given configuration node.
6912 *
6913 * @param data Machine registry data.
6914 *
6915 * @note locks this object for reading.
6916 */
6917HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6918{
6919 AutoLimitedCaller autoCaller(this);
6920 AssertComRCReturnRC(autoCaller.hrc());
6921
6922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6923
6924 data.uuid = mData->mUuid;
6925 data.strSettingsFile = mData->m_strConfigFile;
6926
6927 return S_OK;
6928}
6929
6930/**
6931 * Calculates the absolute path of the given path taking the directory of the
6932 * machine settings file as the current directory.
6933 *
6934 * @param strPath Path to calculate the absolute path for.
6935 * @param aResult Where to put the result (used only on success, can be the
6936 * same Utf8Str instance as passed in @a aPath).
6937 * @return IPRT result.
6938 *
6939 * @note Locks this object for reading.
6940 */
6941int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6942{
6943 AutoCaller autoCaller(this);
6944 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
6945
6946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6947
6948 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6949
6950 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6951
6952 strSettingsDir.stripFilename();
6953 char szFolder[RTPATH_MAX];
6954 size_t cbFolder = sizeof(szFolder);
6955 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
6956 if (RT_SUCCESS(vrc))
6957 aResult = szFolder;
6958
6959 return vrc;
6960}
6961
6962/**
6963 * Copies strSource to strTarget, making it relative to the machine folder
6964 * if it is a subdirectory thereof, or simply copying it otherwise.
6965 *
6966 * @param strSource Path to evaluate and copy.
6967 * @param strTarget Buffer to receive target path.
6968 *
6969 * @note Locks this object for reading.
6970 */
6971void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6972 Utf8Str &strTarget)
6973{
6974 AutoCaller autoCaller(this);
6975 AssertComRCReturn(autoCaller.hrc(), (void)0);
6976
6977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6980 // use strTarget as a temporary buffer to hold the machine settings dir
6981 strTarget = mData->m_strConfigFileFull;
6982 strTarget.stripFilename();
6983 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6984 {
6985 // is relative: then append what's left
6986 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6987 // for empty paths (only possible for subdirs) use "." to avoid
6988 // triggering default settings for not present config attributes.
6989 if (strTarget.isEmpty())
6990 strTarget = ".";
6991 }
6992 else
6993 // is not relative: then overwrite
6994 strTarget = strSource;
6995}
6996
6997/**
6998 * Returns the full path to the machine's log folder in the
6999 * \a aLogFolder argument.
7000 */
7001void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7002{
7003 AutoCaller autoCaller(this);
7004 AssertComRCReturnVoid(autoCaller.hrc());
7005
7006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7007
7008 char szTmp[RTPATH_MAX];
7009 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7010 if (RT_SUCCESS(vrc))
7011 {
7012 if (szTmp[0] && !mUserData.isNull())
7013 {
7014 char szTmp2[RTPATH_MAX];
7015 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7016 if (RT_SUCCESS(vrc))
7017 aLogFolder.printf("%s%c%s",
7018 szTmp2,
7019 RTPATH_DELIMITER,
7020 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7021 }
7022 else
7023 vrc = VERR_PATH_IS_RELATIVE;
7024 }
7025
7026 if (RT_FAILURE(vrc))
7027 {
7028 // fallback if VBOX_USER_LOGHOME is not set or invalid
7029 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7030 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7031 aLogFolder.append(RTPATH_DELIMITER);
7032 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7033 }
7034}
7035
7036/**
7037 * Returns the full path to the machine's log file for an given index.
7038 */
7039Utf8Str Machine::i_getLogFilename(ULONG idx)
7040{
7041 Utf8Str logFolder;
7042 getLogFolder(logFolder);
7043 Assert(logFolder.length());
7044
7045 Utf8Str log;
7046 if (idx == 0)
7047 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7048#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7049 else if (idx == 1)
7050 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7051 else
7052 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7053#else
7054 else
7055 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7056#endif
7057 return log;
7058}
7059
7060/**
7061 * Returns the full path to the machine's hardened log file.
7062 */
7063Utf8Str Machine::i_getHardeningLogFilename(void)
7064{
7065 Utf8Str strFilename;
7066 getLogFolder(strFilename);
7067 Assert(strFilename.length());
7068 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7069 return strFilename;
7070}
7071
7072/**
7073 * Returns the default NVRAM filename based on the location of the VM config.
7074 * Note that this is a relative path.
7075 */
7076Utf8Str Machine::i_getDefaultNVRAMFilename()
7077{
7078 AutoCaller autoCaller(this);
7079 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7080
7081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7082
7083 if (i_isSnapshotMachine())
7084 return Utf8Str::Empty;
7085
7086 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7087 strNVRAMFilePath.stripPath();
7088 strNVRAMFilePath.stripSuffix();
7089 strNVRAMFilePath += ".nvram";
7090
7091 return strNVRAMFilePath;
7092}
7093
7094/**
7095 * Returns the NVRAM filename for a new snapshot. This intentionally works
7096 * similarly to the saved state file naming. Note that this is usually
7097 * a relative path, unless the snapshot folder is absolute.
7098 */
7099Utf8Str Machine::i_getSnapshotNVRAMFilename()
7100{
7101 AutoCaller autoCaller(this);
7102 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7103
7104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7105
7106 RTTIMESPEC ts;
7107 RTTimeNow(&ts);
7108 RTTIME time;
7109 RTTimeExplode(&time, &ts);
7110
7111 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7112 strNVRAMFilePath += RTPATH_DELIMITER;
7113 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7114 time.i32Year, time.u8Month, time.u8MonthDay,
7115 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7116
7117 return strNVRAMFilePath;
7118}
7119
7120/**
7121 * Returns the version of the settings file.
7122 */
7123SettingsVersion_T Machine::i_getSettingsVersion(void)
7124{
7125 AutoCaller autoCaller(this);
7126 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7127
7128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7129
7130 return mData->pMachineConfigFile->getSettingsVersion();
7131}
7132
7133/**
7134 * Composes a unique saved state filename based on the current system time. The filename is
7135 * granular to the second so this will work so long as no more than one snapshot is taken on
7136 * a machine per second.
7137 *
7138 * Before version 4.1, we used this formula for saved state files:
7139 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7140 * which no longer works because saved state files can now be shared between the saved state of the
7141 * "saved" machine and an online snapshot, and the following would cause problems:
7142 * 1) save machine
7143 * 2) create online snapshot from that machine state --> reusing saved state file
7144 * 3) save machine again --> filename would be reused, breaking the online snapshot
7145 *
7146 * So instead we now use a timestamp.
7147 *
7148 * @param strStateFilePath
7149 */
7150
7151void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7152{
7153 AutoCaller autoCaller(this);
7154 AssertComRCReturnVoid(autoCaller.hrc());
7155
7156 {
7157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7158 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7159 }
7160
7161 RTTIMESPEC ts;
7162 RTTimeNow(&ts);
7163 RTTIME time;
7164 RTTimeExplode(&time, &ts);
7165
7166 strStateFilePath += RTPATH_DELIMITER;
7167 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7168 time.i32Year, time.u8Month, time.u8MonthDay,
7169 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7170}
7171
7172/**
7173 * Returns whether at least one USB controller is present for the VM.
7174 */
7175bool Machine::i_isUSBControllerPresent()
7176{
7177 AutoCaller autoCaller(this);
7178 AssertComRCReturn(autoCaller.hrc(), false);
7179
7180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7181
7182 return (mUSBControllers->size() > 0);
7183}
7184
7185
7186/**
7187 * @note Locks this object for writing, calls the client process
7188 * (inside the lock).
7189 */
7190HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7191 const Utf8Str &strFrontend,
7192 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7193 ProgressProxy *aProgress)
7194{
7195 LogFlowThisFuncEnter();
7196
7197 AssertReturn(aControl, E_FAIL);
7198 AssertReturn(aProgress, E_FAIL);
7199 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7200
7201 AutoCaller autoCaller(this);
7202 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7203
7204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7205
7206 if (!mData->mRegistered)
7207 return setError(E_UNEXPECTED,
7208 tr("The machine '%s' is not registered"),
7209 mUserData->s.strName.c_str());
7210
7211 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7212
7213 /* The process started when launching a VM with separate UI/VM processes is always
7214 * the UI process, i.e. needs special handling as it won't claim the session. */
7215 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7216
7217 if (fSeparate)
7218 {
7219 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7220 return setError(VBOX_E_INVALID_OBJECT_STATE,
7221 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7222 mUserData->s.strName.c_str());
7223 }
7224 else
7225 {
7226 if ( mData->mSession.mState == SessionState_Locked
7227 || mData->mSession.mState == SessionState_Spawning
7228 || mData->mSession.mState == SessionState_Unlocking)
7229 return setError(VBOX_E_INVALID_OBJECT_STATE,
7230 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7231 mUserData->s.strName.c_str());
7232
7233 /* may not be busy */
7234 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7235 }
7236
7237 /* Hardening logging */
7238#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7239 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7240 {
7241 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7242 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7243 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7244 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7245 {
7246 Utf8Str strStartupLogDir = strHardeningLogFile;
7247 strStartupLogDir.stripFilename();
7248 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7249 file without stripping the file. */
7250 }
7251 strSupHardeningLogArg.append(strHardeningLogFile);
7252
7253 /* Remove legacy log filename to avoid confusion. */
7254 Utf8Str strOldStartupLogFile;
7255 getLogFolder(strOldStartupLogFile);
7256 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7257 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7258 }
7259#else
7260 Utf8Str strSupHardeningLogArg;
7261#endif
7262
7263 Utf8Str strAppOverride;
7264#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7265 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7266#endif
7267
7268#if defined(VBOX_WITH_SDS) && defined(RT_OS_WINDOWS)
7269 bool fUseVBoxSDS = false;
7270#endif
7271
7272 Utf8Str strCanonicalName;
7273 if (false)
7274 { }
7275#ifdef VBOX_WITH_QTGUI
7276 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7277 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7278 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7279 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7280 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7281 {
7282 strCanonicalName = "GUI/Qt";
7283# if defined(VBOX_WITH_SDS) && defined(RT_OS_WINDOWS)
7284 fUseVBoxSDS = true;
7285# endif
7286 }
7287#endif
7288#ifdef VBOX_WITH_VBOXSDL
7289 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7290 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7291 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7292 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7293 {
7294 strCanonicalName = "GUI/SDL";
7295# if defined(VBOX_WITH_SDS) && defined(RT_OS_WINDOWS)
7296 fUseVBoxSDS = true;
7297# endif
7298 }
7299#endif
7300#ifdef VBOX_WITH_HEADLESS
7301 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7302 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7303 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7304 {
7305 strCanonicalName = "headless";
7306 }
7307#endif
7308 else
7309 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7310
7311 Utf8Str idStr = mData->mUuid.toString();
7312 Utf8Str const &strMachineName = mUserData->s.strName;
7313 RTPROCESS pid = NIL_RTPROCESS;
7314
7315#if defined(VBOX_WITH_SDS) && defined(RT_OS_WINDOWS)
7316 DWORD idCallerSession = ~(DWORD)0;
7317 if (fUseVBoxSDS)
7318 {
7319 /*
7320 * The VBoxSDS should be used for process launching the VM with
7321 * GUI only if the caller and the VBoxSDS are in different Windows
7322 * sessions and the caller in the interactive one.
7323 */
7324 fUseVBoxSDS = false;
7325
7326 /* Get windows session of the current process. The process token used
7327 due to several reasons:
7328 1. The token is absent for the current thread except someone set it
7329 for us.
7330 2. Needs to get the id of the session where the process is started.
7331 We only need to do this once, though. */
7332 static DWORD s_idCurrentSession = ~(DWORD)0;
7333 DWORD idCurrentSession = s_idCurrentSession;
7334 if (idCurrentSession == ~(DWORD)0)
7335 {
7336 HANDLE hCurrentProcessToken = NULL;
7337 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7338 {
7339 DWORD cbIgn = 0;
7340 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7341 s_idCurrentSession = idCurrentSession;
7342 else
7343 {
7344 idCurrentSession = ~(DWORD)0;
7345 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7346 }
7347 CloseHandle(hCurrentProcessToken);
7348 }
7349 else
7350 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7351 }
7352
7353 /* get the caller's session */
7354 HRESULT hrc = CoImpersonateClient();
7355 if (SUCCEEDED(hrc))
7356 {
7357 HANDLE hCallerThreadToken;
7358 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7359 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7360 &hCallerThreadToken))
7361 {
7362 SetLastError(NO_ERROR);
7363 DWORD cbIgn = 0;
7364 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7365 {
7366 /* Only need to use SDS if the session ID differs: */
7367 if (idCurrentSession != idCallerSession)
7368 {
7369 fUseVBoxSDS = false;
7370
7371 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7372 DWORD cbTokenGroups = 0;
7373 PTOKEN_GROUPS pTokenGroups = NULL;
7374 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7375 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7376 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7377 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7378 {
7379 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7380 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7381 PSID pInteractiveSid = NULL;
7382 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7383 {
7384 /* Iterate over the groups looking for the interactive SID: */
7385 fUseVBoxSDS = false;
7386 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7387 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7388 {
7389 fUseVBoxSDS = true;
7390 break;
7391 }
7392 FreeSid(pInteractiveSid);
7393 }
7394 }
7395 else
7396 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7397 RTMemTmpFree(pTokenGroups);
7398 }
7399 }
7400 else
7401 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7402 CloseHandle(hCallerThreadToken);
7403 }
7404 else
7405 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7406 CoRevertToSelf();
7407 }
7408 else
7409 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7410 }
7411 if (fUseVBoxSDS)
7412 {
7413 /* connect to VBoxSDS */
7414 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7415 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7416 if (FAILED(hrc))
7417 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7418 strMachineName.c_str());
7419
7420 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7421 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7422 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7423 service to access the files. */
7424 hrc = CoSetProxyBlanket(pVBoxSDS,
7425 RPC_C_AUTHN_DEFAULT,
7426 RPC_C_AUTHZ_DEFAULT,
7427 COLE_DEFAULT_PRINCIPAL,
7428 RPC_C_AUTHN_LEVEL_DEFAULT,
7429 RPC_C_IMP_LEVEL_IMPERSONATE,
7430 NULL,
7431 EOAC_DEFAULT);
7432 if (FAILED(hrc))
7433 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7434
7435 size_t const cEnvVars = aEnvironmentChanges.size();
7436 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7437 for (size_t i = 0; i < cEnvVars; i++)
7438 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7439
7440 ULONG uPid = 0;
7441 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7442 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7443 idCallerSession, &uPid);
7444 if (FAILED(hrc))
7445 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7446 pid = (RTPROCESS)uPid;
7447 }
7448 else
7449#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7450 {
7451 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7452 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7453 if (RT_FAILURE(vrc))
7454 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7455 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7456 }
7457
7458 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7459 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7460
7461 if (!fSeparate)
7462 {
7463 /*
7464 * Note that we don't release the lock here before calling the client,
7465 * because it doesn't need to call us back if called with a NULL argument.
7466 * Releasing the lock here is dangerous because we didn't prepare the
7467 * launch data yet, but the client we've just started may happen to be
7468 * too fast and call LockMachine() that will fail (because of PID, etc.),
7469 * so that the Machine will never get out of the Spawning session state.
7470 */
7471
7472 /* inform the session that it will be a remote one */
7473 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7474#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7475 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7476#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7477 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7478#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7479 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
7480
7481 if (FAILED(hrc))
7482 {
7483 /* restore the session state */
7484 mData->mSession.mState = SessionState_Unlocked;
7485 alock.release();
7486 mParent->i_addProcessToReap(pid);
7487 /* The failure may occur w/o any error info (from RPC), so provide one */
7488 return setError(VBOX_E_VM_ERROR,
7489 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
7490 }
7491
7492 /* attach launch data to the machine */
7493 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7494 mData->mSession.mRemoteControls.push_back(aControl);
7495 mData->mSession.mProgress = aProgress;
7496 mData->mSession.mPID = pid;
7497 mData->mSession.mState = SessionState_Spawning;
7498 Assert(strCanonicalName.isNotEmpty());
7499 mData->mSession.mName = strCanonicalName;
7500 }
7501 else
7502 {
7503 /* For separate UI process we declare the launch as completed instantly, as the
7504 * actual headless VM start may or may not come. No point in remembering anything
7505 * yet, as what matters for us is when the headless VM gets started. */
7506 aProgress->i_notifyComplete(S_OK);
7507 }
7508
7509 alock.release();
7510 mParent->i_addProcessToReap(pid);
7511
7512 LogFlowThisFuncLeave();
7513 return S_OK;
7514}
7515
7516/**
7517 * Returns @c true if the given session machine instance has an open direct
7518 * session (and optionally also for direct sessions which are closing) and
7519 * returns the session control machine instance if so.
7520 *
7521 * Note that when the method returns @c false, the arguments remain unchanged.
7522 *
7523 * @param aMachine Session machine object.
7524 * @param aControl Direct session control object (optional).
7525 * @param aRequireVM If true then only allow VM sessions.
7526 * @param aAllowClosing If true then additionally a session which is currently
7527 * being closed will also be allowed.
7528 *
7529 * @note locks this object for reading.
7530 */
7531bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7532 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7533 bool aRequireVM /*= false*/,
7534 bool aAllowClosing /*= false*/)
7535{
7536 AutoLimitedCaller autoCaller(this);
7537 AssertComRCReturn(autoCaller.hrc(), false);
7538
7539 /* just return false for inaccessible machines */
7540 if (getObjectState().getState() != ObjectState::Ready)
7541 return false;
7542
7543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7544
7545 if ( ( mData->mSession.mState == SessionState_Locked
7546 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7547 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7548 )
7549 {
7550 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7551
7552 aMachine = mData->mSession.mMachine;
7553
7554 if (aControl != NULL)
7555 *aControl = mData->mSession.mDirectControl;
7556
7557 return true;
7558 }
7559
7560 return false;
7561}
7562
7563/**
7564 * Returns @c true if the given machine has an spawning direct session.
7565 *
7566 * @note locks this object for reading.
7567 */
7568bool Machine::i_isSessionSpawning()
7569{
7570 AutoLimitedCaller autoCaller(this);
7571 AssertComRCReturn(autoCaller.hrc(), false);
7572
7573 /* just return false for inaccessible machines */
7574 if (getObjectState().getState() != ObjectState::Ready)
7575 return false;
7576
7577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7578
7579 if (mData->mSession.mState == SessionState_Spawning)
7580 return true;
7581
7582 return false;
7583}
7584
7585/**
7586 * Called from the client watcher thread to check for unexpected client process
7587 * death during Session_Spawning state (e.g. before it successfully opened a
7588 * direct session).
7589 *
7590 * On Win32 and on OS/2, this method is called only when we've got the
7591 * direct client's process termination notification, so it always returns @c
7592 * true.
7593 *
7594 * On other platforms, this method returns @c true if the client process is
7595 * terminated and @c false if it's still alive.
7596 *
7597 * @note Locks this object for writing.
7598 */
7599bool Machine::i_checkForSpawnFailure()
7600{
7601 AutoCaller autoCaller(this);
7602 if (!autoCaller.isOk())
7603 {
7604 /* nothing to do */
7605 LogFlowThisFunc(("Already uninitialized!\n"));
7606 return true;
7607 }
7608
7609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7610
7611 if (mData->mSession.mState != SessionState_Spawning)
7612 {
7613 /* nothing to do */
7614 LogFlowThisFunc(("Not spawning any more!\n"));
7615 return true;
7616 }
7617
7618 /* PID not yet initialized, skip check. */
7619 if (mData->mSession.mPID == NIL_RTPROCESS)
7620 return false;
7621
7622 HRESULT hrc = S_OK;
7623 RTPROCSTATUS status;
7624 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7625 if (vrc != VERR_PROCESS_RUNNING)
7626 {
7627 Utf8Str strExtraInfo;
7628
7629#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7630 /* If the startup logfile exists and is of non-zero length, tell the
7631 user to look there for more details to encourage them to attach it
7632 when reporting startup issues. */
7633 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7634 uint64_t cbStartupLogFile = 0;
7635 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7636 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7637 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7638#endif
7639
7640 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7641 hrc = setError(E_FAIL,
7642 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7643 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7644 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7645 hrc = setError(E_FAIL,
7646 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7647 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7648 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7649 hrc = setError(E_FAIL,
7650 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7651 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7652 else
7653 hrc = setErrorBoth(E_FAIL, vrc,
7654 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7655 i_getName().c_str(), vrc, strExtraInfo.c_str());
7656 }
7657
7658 if (FAILED(hrc))
7659 {
7660 /* Close the remote session, remove the remote control from the list
7661 * and reset session state to Closed (@note keep the code in sync with
7662 * the relevant part in LockMachine()). */
7663
7664 Assert(mData->mSession.mRemoteControls.size() == 1);
7665 if (mData->mSession.mRemoteControls.size() == 1)
7666 {
7667 ErrorInfoKeeper eik;
7668 mData->mSession.mRemoteControls.front()->Uninitialize();
7669 }
7670
7671 mData->mSession.mRemoteControls.clear();
7672 mData->mSession.mState = SessionState_Unlocked;
7673
7674 /* finalize the progress after setting the state */
7675 if (!mData->mSession.mProgress.isNull())
7676 {
7677 mData->mSession.mProgress->notifyComplete(hrc);
7678 mData->mSession.mProgress.setNull();
7679 }
7680
7681 mData->mSession.mPID = NIL_RTPROCESS;
7682
7683 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7684 return true;
7685 }
7686
7687 return false;
7688}
7689
7690/**
7691 * Checks whether the machine can be registered. If so, commits and saves
7692 * all settings.
7693 *
7694 * @note Must be called from mParent's write lock. Locks this object and
7695 * children for writing.
7696 */
7697HRESULT Machine::i_prepareRegister()
7698{
7699 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7700
7701 AutoLimitedCaller autoCaller(this);
7702 AssertComRCReturnRC(autoCaller.hrc());
7703
7704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7705
7706 /* wait for state dependents to drop to zero */
7707 i_ensureNoStateDependencies(alock);
7708
7709 if (!mData->mAccessible)
7710 return setError(VBOX_E_INVALID_OBJECT_STATE,
7711 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7712 mUserData->s.strName.c_str(),
7713 mData->mUuid.toString().c_str());
7714
7715 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7716
7717 if (mData->mRegistered)
7718 return setError(VBOX_E_INVALID_OBJECT_STATE,
7719 tr("The machine '%s' with UUID {%s} is already registered"),
7720 mUserData->s.strName.c_str(),
7721 mData->mUuid.toString().c_str());
7722
7723 HRESULT hrc = S_OK;
7724
7725 // Ensure the settings are saved. If we are going to be registered and
7726 // no config file exists yet, create it by calling i_saveSettings() too.
7727 if ( (mData->flModifications)
7728 || (!mData->pMachineConfigFile->fileExists())
7729 )
7730 {
7731 hrc = i_saveSettings(NULL, alock);
7732 // no need to check whether VirtualBox.xml needs saving too since
7733 // we can't have a machine XML file rename pending
7734 if (FAILED(hrc)) return hrc;
7735 }
7736
7737 /* more config checking goes here */
7738
7739 if (SUCCEEDED(hrc))
7740 {
7741 /* we may have had implicit modifications we want to fix on success */
7742 i_commit();
7743
7744 mData->mRegistered = true;
7745 }
7746 else
7747 {
7748 /* we may have had implicit modifications we want to cancel on failure*/
7749 i_rollback(false /* aNotify */);
7750 }
7751
7752 return hrc;
7753}
7754
7755/**
7756 * Increases the number of objects dependent on the machine state or on the
7757 * registered state. Guarantees that these two states will not change at least
7758 * until #i_releaseStateDependency() is called.
7759 *
7760 * Depending on the @a aDepType value, additional state checks may be made.
7761 * These checks will set extended error info on failure. See
7762 * #i_checkStateDependency() for more info.
7763 *
7764 * If this method returns a failure, the dependency is not added and the caller
7765 * is not allowed to rely on any particular machine state or registration state
7766 * value and may return the failed result code to the upper level.
7767 *
7768 * @param aDepType Dependency type to add.
7769 * @param aState Current machine state (NULL if not interested).
7770 * @param aRegistered Current registered state (NULL if not interested).
7771 *
7772 * @note Locks this object for writing.
7773 */
7774HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7775 MachineState_T *aState /* = NULL */,
7776 BOOL *aRegistered /* = NULL */)
7777{
7778 AutoCaller autoCaller(this);
7779 AssertComRCReturnRC(autoCaller.hrc());
7780
7781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7782
7783 HRESULT hrc = i_checkStateDependency(aDepType);
7784 if (FAILED(hrc)) return hrc;
7785
7786 {
7787 if (mData->mMachineStateChangePending != 0)
7788 {
7789 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7790 * drop to zero so don't add more. It may make sense to wait a bit
7791 * and retry before reporting an error (since the pending state
7792 * transition should be really quick) but let's just assert for
7793 * now to see if it ever happens on practice. */
7794
7795 AssertFailed();
7796
7797 return setError(E_ACCESSDENIED,
7798 tr("Machine state change is in progress. Please retry the operation later."));
7799 }
7800
7801 ++mData->mMachineStateDeps;
7802 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7803 }
7804
7805 if (aState)
7806 *aState = mData->mMachineState;
7807 if (aRegistered)
7808 *aRegistered = mData->mRegistered;
7809
7810 return S_OK;
7811}
7812
7813/**
7814 * Decreases the number of objects dependent on the machine state.
7815 * Must always complete the #i_addStateDependency() call after the state
7816 * dependency is no more necessary.
7817 */
7818void Machine::i_releaseStateDependency()
7819{
7820 AutoCaller autoCaller(this);
7821 AssertComRCReturnVoid(autoCaller.hrc());
7822
7823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7824
7825 /* releaseStateDependency() w/o addStateDependency()? */
7826 AssertReturnVoid(mData->mMachineStateDeps != 0);
7827 -- mData->mMachineStateDeps;
7828
7829 if (mData->mMachineStateDeps == 0)
7830 {
7831 /* inform i_ensureNoStateDependencies() that there are no more deps */
7832 if (mData->mMachineStateChangePending != 0)
7833 {
7834 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7835 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7836 }
7837 }
7838}
7839
7840Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7841{
7842 /* start with nothing found */
7843 Utf8Str strResult("");
7844
7845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7846
7847 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7848 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7849 // found:
7850 strResult = it->second; // source is a Utf8Str
7851
7852 return strResult;
7853}
7854
7855FirmwareType_T Machine::i_getFirmwareType() const
7856{
7857 return mFirmwareSettings->i_getFirmwareType();
7858}
7859
7860// protected methods
7861/////////////////////////////////////////////////////////////////////////////
7862
7863/**
7864 * Performs machine state checks based on the @a aDepType value. If a check
7865 * fails, this method will set extended error info, otherwise it will return
7866 * S_OK. It is supposed, that on failure, the caller will immediately return
7867 * the return value of this method to the upper level.
7868 *
7869 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7870 *
7871 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7872 * current state of this machine object allows to change settings of the
7873 * machine (i.e. the machine is not registered, or registered but not running
7874 * and not saved). It is useful to call this method from Machine setters
7875 * before performing any change.
7876 *
7877 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7878 * as for MutableStateDep except that if the machine is saved, S_OK is also
7879 * returned. This is useful in setters which allow changing machine
7880 * properties when it is in the saved state.
7881 *
7882 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7883 * if the current state of this machine object allows to change runtime
7884 * changeable settings of the machine (i.e. the machine is not registered, or
7885 * registered but either running or not running and not saved). It is useful
7886 * to call this method from Machine setters before performing any changes to
7887 * runtime changeable settings.
7888 *
7889 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7890 * the same as for MutableOrRunningStateDep except that if the machine is
7891 * saved, S_OK is also returned. This is useful in setters which allow
7892 * changing runtime and saved state changeable machine properties.
7893 *
7894 * @param aDepType Dependency type to check.
7895 *
7896 * @note Non Machine based classes should use #i_addStateDependency() and
7897 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7898 * template.
7899 *
7900 * @note This method must be called from under this object's read or write
7901 * lock.
7902 */
7903HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7904{
7905 switch (aDepType)
7906 {
7907 case AnyStateDep:
7908 {
7909 break;
7910 }
7911 case MutableStateDep:
7912 {
7913 if ( mData->mRegistered
7914 && ( !i_isSessionMachine()
7915 || ( mData->mMachineState != MachineState_Aborted
7916 && mData->mMachineState != MachineState_Teleported
7917 && mData->mMachineState != MachineState_PoweredOff
7918 )
7919 )
7920 )
7921 return setError(VBOX_E_INVALID_VM_STATE,
7922 tr("The machine is not mutable (state is %s)"),
7923 Global::stringifyMachineState(mData->mMachineState));
7924 break;
7925 }
7926 case MutableOrSavedStateDep:
7927 {
7928 if ( mData->mRegistered
7929 && ( !i_isSessionMachine()
7930 || ( mData->mMachineState != MachineState_Aborted
7931 && mData->mMachineState != MachineState_Teleported
7932 && mData->mMachineState != MachineState_Saved
7933 && mData->mMachineState != MachineState_AbortedSaved
7934 && mData->mMachineState != MachineState_PoweredOff
7935 )
7936 )
7937 )
7938 return setError(VBOX_E_INVALID_VM_STATE,
7939 tr("The machine is not mutable or saved (state is %s)"),
7940 Global::stringifyMachineState(mData->mMachineState));
7941 break;
7942 }
7943 case MutableOrRunningStateDep:
7944 {
7945 if ( mData->mRegistered
7946 && ( !i_isSessionMachine()
7947 || ( mData->mMachineState != MachineState_Aborted
7948 && mData->mMachineState != MachineState_Teleported
7949 && mData->mMachineState != MachineState_PoweredOff
7950 && !Global::IsOnline(mData->mMachineState)
7951 )
7952 )
7953 )
7954 return setError(VBOX_E_INVALID_VM_STATE,
7955 tr("The machine is not mutable or running (state is %s)"),
7956 Global::stringifyMachineState(mData->mMachineState));
7957 break;
7958 }
7959 case MutableOrSavedOrRunningStateDep:
7960 {
7961 if ( mData->mRegistered
7962 && ( !i_isSessionMachine()
7963 || ( mData->mMachineState != MachineState_Aborted
7964 && mData->mMachineState != MachineState_Teleported
7965 && mData->mMachineState != MachineState_Saved
7966 && mData->mMachineState != MachineState_AbortedSaved
7967 && mData->mMachineState != MachineState_PoweredOff
7968 && !Global::IsOnline(mData->mMachineState)
7969 )
7970 )
7971 )
7972 return setError(VBOX_E_INVALID_VM_STATE,
7973 tr("The machine is not mutable, saved or running (state is %s)"),
7974 Global::stringifyMachineState(mData->mMachineState));
7975 break;
7976 }
7977 }
7978
7979 return S_OK;
7980}
7981
7982/**
7983 * Helper to initialize all associated child objects and allocate data
7984 * structures.
7985 *
7986 * This method must be called as a part of the object's initialization procedure
7987 * (usually done in the #init() method).
7988 *
7989 * @note Must be called only from #init() or from #i_registeredInit().
7990 */
7991HRESULT Machine::initDataAndChildObjects()
7992{
7993 AutoCaller autoCaller(this);
7994 AssertComRCReturnRC(autoCaller.hrc());
7995 AssertReturn( getObjectState().getState() == ObjectState::InInit
7996 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7997
7998 AssertReturn(!mData->mAccessible, E_FAIL);
7999
8000 /* allocate data structures */
8001 mSSData.allocate();
8002 mUserData.allocate();
8003 mHWData.allocate();
8004 mMediumAttachments.allocate();
8005 mStorageControllers.allocate();
8006 mUSBControllers.allocate();
8007
8008 /* create the platform + platform properties objects for this machine */
8009 HRESULT hrc = unconst(mPlatform).createObject();
8010 ComAssertComRCRetRC(hrc);
8011 hrc = mPlatform->init(this);
8012 ComAssertComRCRetRC(hrc);
8013 hrc = unconst(mPlatformProperties).createObject();
8014 ComAssertComRCRetRC(hrc);
8015 hrc = mPlatformProperties->init(mParent);
8016 ComAssertComRCRetRC(hrc);
8017
8018 /* initialize mOSTypeId */
8019 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8020
8021 /* create associated firmware settings object */
8022 unconst(mFirmwareSettings).createObject();
8023 mFirmwareSettings->init(this);
8024
8025 /* create associated recording settings object */
8026 unconst(mRecordingSettings).createObject();
8027 mRecordingSettings->init(this);
8028
8029 /* create associated trusted platform module object */
8030 unconst(mTrustedPlatformModule).createObject();
8031 mTrustedPlatformModule->init(this);
8032
8033 /* create associated NVRAM store object */
8034 unconst(mNvramStore).createObject();
8035 mNvramStore->init(this);
8036
8037 /* create the graphics adapter object (always present) */
8038 unconst(mGraphicsAdapter).createObject();
8039 mGraphicsAdapter->init(this);
8040
8041 /* create an associated VRDE object (default is disabled) */
8042 unconst(mVRDEServer).createObject();
8043 mVRDEServer->init(this);
8044
8045 /* create associated serial port objects */
8046 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8047 {
8048 unconst(mSerialPorts[slot]).createObject();
8049 mSerialPorts[slot]->init(this, slot);
8050 }
8051
8052 /* create associated parallel port objects */
8053 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8054 {
8055 unconst(mParallelPorts[slot]).createObject();
8056 mParallelPorts[slot]->init(this, slot);
8057 }
8058
8059 /* create the audio settings object */
8060 unconst(mAudioSettings).createObject();
8061 mAudioSettings->init(this);
8062
8063 /* create the USB device filters object (always present) */
8064 unconst(mUSBDeviceFilters).createObject();
8065 mUSBDeviceFilters->init(this);
8066
8067 /* create associated network adapter objects */
8068 ChipsetType_T enmChipsetType;
8069 hrc = mPlatform->getChipsetType(&enmChipsetType);
8070 ComAssertComRC(hrc);
8071
8072 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType));
8073 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8074 {
8075 unconst(mNetworkAdapters[slot]).createObject();
8076 mNetworkAdapters[slot]->init(this, slot);
8077 }
8078
8079 /* create the bandwidth control */
8080 unconst(mBandwidthControl).createObject();
8081 mBandwidthControl->init(this);
8082
8083 /* create the guest debug control object */
8084 unconst(mGuestDebugControl).createObject();
8085 mGuestDebugControl->init(this);
8086
8087 return hrc;
8088}
8089
8090/**
8091 * Helper to uninitialize all associated child objects and to free all data
8092 * structures.
8093 *
8094 * This method must be called as a part of the object's uninitialization
8095 * procedure (usually done in the #uninit() method).
8096 *
8097 * @note Must be called only from #uninit() or from #i_registeredInit().
8098 */
8099void Machine::uninitDataAndChildObjects()
8100{
8101 AutoCaller autoCaller(this);
8102 AssertComRCReturnVoid(autoCaller.hrc());
8103 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8104 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8105 || getObjectState().getState() == ObjectState::InUninit
8106 || getObjectState().getState() == ObjectState::Limited);
8107
8108 /* tell all our other child objects we've been uninitialized */
8109 if (mGuestDebugControl)
8110 {
8111 mGuestDebugControl->uninit();
8112 unconst(mGuestDebugControl).setNull();
8113 }
8114
8115 if (mBandwidthControl)
8116 {
8117 mBandwidthControl->uninit();
8118 unconst(mBandwidthControl).setNull();
8119 }
8120
8121 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8122 {
8123 if (mNetworkAdapters[slot])
8124 {
8125 mNetworkAdapters[slot]->uninit();
8126 unconst(mNetworkAdapters[slot]).setNull();
8127 }
8128 }
8129
8130 if (mUSBDeviceFilters)
8131 {
8132 mUSBDeviceFilters->uninit();
8133 unconst(mUSBDeviceFilters).setNull();
8134 }
8135
8136 if (mAudioSettings)
8137 {
8138 mAudioSettings->uninit();
8139 unconst(mAudioSettings).setNull();
8140 }
8141
8142 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8143 {
8144 if (mParallelPorts[slot])
8145 {
8146 mParallelPorts[slot]->uninit();
8147 unconst(mParallelPorts[slot]).setNull();
8148 }
8149 }
8150
8151 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8152 {
8153 if (mSerialPorts[slot])
8154 {
8155 mSerialPorts[slot]->uninit();
8156 unconst(mSerialPorts[slot]).setNull();
8157 }
8158 }
8159
8160 if (mVRDEServer)
8161 {
8162 mVRDEServer->uninit();
8163 unconst(mVRDEServer).setNull();
8164 }
8165
8166 if (mGraphicsAdapter)
8167 {
8168 mGraphicsAdapter->uninit();
8169 unconst(mGraphicsAdapter).setNull();
8170 }
8171
8172 if (mPlatform)
8173 {
8174 mPlatform->uninit();
8175 unconst(mPlatform).setNull();
8176 }
8177
8178 if (mPlatformProperties)
8179 {
8180 mPlatformProperties->uninit();
8181 unconst(mPlatformProperties).setNull();
8182 }
8183
8184 if (mFirmwareSettings)
8185 {
8186 mFirmwareSettings->uninit();
8187 unconst(mFirmwareSettings).setNull();
8188 }
8189
8190 if (mRecordingSettings)
8191 {
8192 mRecordingSettings->uninit();
8193 unconst(mRecordingSettings).setNull();
8194 }
8195
8196 if (mTrustedPlatformModule)
8197 {
8198 mTrustedPlatformModule->uninit();
8199 unconst(mTrustedPlatformModule).setNull();
8200 }
8201
8202 if (mNvramStore)
8203 {
8204 mNvramStore->uninit();
8205 unconst(mNvramStore).setNull();
8206 }
8207
8208 /* Deassociate media (only when a real Machine or a SnapshotMachine
8209 * instance is uninitialized; SessionMachine instances refer to real
8210 * Machine media). This is necessary for a clean re-initialization of
8211 * the VM after successfully re-checking the accessibility state. Note
8212 * that in case of normal Machine or SnapshotMachine uninitialization (as
8213 * a result of unregistering or deleting the snapshot), outdated media
8214 * attachments will already be uninitialized and deleted, so this
8215 * code will not affect them. */
8216 if ( !mMediumAttachments.isNull()
8217 && !i_isSessionMachine()
8218 )
8219 {
8220 for (MediumAttachmentList::const_iterator
8221 it = mMediumAttachments->begin();
8222 it != mMediumAttachments->end();
8223 ++it)
8224 {
8225 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8226 if (pMedium.isNull())
8227 continue;
8228 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8229 AssertComRC(hrc);
8230 }
8231 }
8232
8233 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8234 {
8235 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8236 if (mData->mFirstSnapshot)
8237 {
8238 // Snapshots tree is protected by machine write lock.
8239 // Otherwise we assert in Snapshot::uninit()
8240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8241 mData->mFirstSnapshot->uninit();
8242 mData->mFirstSnapshot.setNull();
8243 }
8244
8245 mData->mCurrentSnapshot.setNull();
8246 }
8247
8248 /* free data structures (the essential mData structure is not freed here
8249 * since it may be still in use) */
8250 mMediumAttachments.free();
8251 mStorageControllers.free();
8252 mUSBControllers.free();
8253 mHWData.free();
8254 mUserData.free();
8255 mSSData.free();
8256}
8257
8258/**
8259 * Returns a pointer to the Machine object for this machine that acts like a
8260 * parent for complex machine data objects such as shared folders, etc.
8261 *
8262 * For primary Machine objects and for SnapshotMachine objects, returns this
8263 * object's pointer itself. For SessionMachine objects, returns the peer
8264 * (primary) machine pointer.
8265 */
8266Machine *Machine::i_getMachine()
8267{
8268 if (i_isSessionMachine())
8269 return (Machine*)mPeer;
8270 return this;
8271}
8272
8273/**
8274 * Makes sure that there are no machine state dependents. If necessary, waits
8275 * for the number of dependents to drop to zero.
8276 *
8277 * Make sure this method is called from under this object's write lock to
8278 * guarantee that no new dependents may be added when this method returns
8279 * control to the caller.
8280 *
8281 * @note Receives a lock to this object for writing. The lock will be released
8282 * while waiting (if necessary).
8283 *
8284 * @warning To be used only in methods that change the machine state!
8285 */
8286void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8287{
8288 AssertReturnVoid(isWriteLockOnCurrentThread());
8289
8290 /* Wait for all state dependents if necessary */
8291 if (mData->mMachineStateDeps != 0)
8292 {
8293 /* lazy semaphore creation */
8294 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8295 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8296
8297 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8298 mData->mMachineStateDeps));
8299
8300 ++mData->mMachineStateChangePending;
8301
8302 /* reset the semaphore before waiting, the last dependent will signal
8303 * it */
8304 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8305
8306 alock.release();
8307
8308 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8309
8310 alock.acquire();
8311
8312 -- mData->mMachineStateChangePending;
8313 }
8314}
8315
8316/**
8317 * Changes the machine state and informs callbacks.
8318 *
8319 * This method is not intended to fail so it either returns S_OK or asserts (and
8320 * returns a failure).
8321 *
8322 * @note Locks this object for writing.
8323 */
8324HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8325{
8326 LogFlowThisFuncEnter();
8327 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8328 Assert(aMachineState != MachineState_Null);
8329
8330 AutoCaller autoCaller(this);
8331 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8332
8333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8334
8335 /* wait for state dependents to drop to zero */
8336 i_ensureNoStateDependencies(alock);
8337
8338 MachineState_T const enmOldState = mData->mMachineState;
8339 if (enmOldState != aMachineState)
8340 {
8341 mData->mMachineState = aMachineState;
8342 RTTimeNow(&mData->mLastStateChange);
8343
8344#ifdef VBOX_WITH_DTRACE_R3_MAIN
8345 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8346#endif
8347 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8348 }
8349
8350 LogFlowThisFuncLeave();
8351 return S_OK;
8352}
8353
8354/**
8355 * Searches for a shared folder with the given logical name
8356 * in the collection of shared folders.
8357 *
8358 * @param aName logical name of the shared folder
8359 * @param aSharedFolder where to return the found object
8360 * @param aSetError whether to set the error info if the folder is
8361 * not found
8362 * @return
8363 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8364 *
8365 * @note
8366 * must be called from under the object's lock!
8367 */
8368HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8369 ComObjPtr<SharedFolder> &aSharedFolder,
8370 bool aSetError /* = false */)
8371{
8372 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8373 for (HWData::SharedFolderList::const_iterator
8374 it = mHWData->mSharedFolders.begin();
8375 it != mHWData->mSharedFolders.end();
8376 ++it)
8377 {
8378 SharedFolder *pSF = *it;
8379 AutoCaller autoCaller(pSF);
8380 if (pSF->i_getName() == aName)
8381 {
8382 aSharedFolder = pSF;
8383 hrc = S_OK;
8384 break;
8385 }
8386 }
8387
8388 if (aSetError && FAILED(hrc))
8389 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8390
8391 return hrc;
8392}
8393
8394/**
8395 * Initializes all machine instance data from the given settings structures
8396 * from XML. The exception is the machine UUID which needs special handling
8397 * depending on the caller's use case, so the caller needs to set that herself.
8398 *
8399 * This gets called in several contexts during machine initialization:
8400 *
8401 * -- When machine XML exists on disk already and needs to be loaded into memory,
8402 * for example, from #i_registeredInit() to load all registered machines on
8403 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8404 * attached to the machine should be part of some media registry already.
8405 *
8406 * -- During OVF import, when a machine config has been constructed from an
8407 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8408 * ensure that the media listed as attachments in the config (which have
8409 * been imported from the OVF) receive the correct registry ID.
8410 *
8411 * -- During VM cloning.
8412 *
8413 * @param config Machine settings from XML.
8414 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8415 * for each attached medium in the config.
8416 * @return
8417 */
8418HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8419 const Guid *puuidRegistry)
8420{
8421 // copy name, description, OS type, teleporter, UTC etc.
8422 mUserData->s = config.machineUserData;
8423
8424 // look up the object by Id to check it is valid
8425 ComObjPtr<GuestOSType> pGuestOSType;
8426 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8427 if (!pGuestOSType.isNull())
8428 mUserData->s.strOsType = pGuestOSType->i_id();
8429
8430#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8431 // stateFile encryption (optional)
8432 mSSData->strStateKeyId = config.strStateKeyId;
8433 mSSData->strStateKeyStore = config.strStateKeyStore;
8434 mData->mstrLogKeyId = config.strLogKeyId;
8435 mData->mstrLogKeyStore = config.strLogKeyStore;
8436#endif
8437
8438 // stateFile (optional)
8439 if (config.strStateFile.isEmpty())
8440 mSSData->strStateFilePath.setNull();
8441 else
8442 {
8443 Utf8Str stateFilePathFull(config.strStateFile);
8444 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8445 if (RT_FAILURE(vrc))
8446 return setErrorBoth(E_FAIL, vrc,
8447 tr("Invalid saved state file path '%s' (%Rrc)"),
8448 config.strStateFile.c_str(),
8449 vrc);
8450 mSSData->strStateFilePath = stateFilePathFull;
8451 }
8452
8453 // snapshot folder needs special processing so set it again
8454 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8455 if (FAILED(hrc)) return hrc;
8456
8457 /* Copy the extra data items (config may or may not be the same as
8458 * mData->pMachineConfigFile) if necessary. When loading the XML files
8459 * from disk they are the same, but not for OVF import. */
8460 if (mData->pMachineConfigFile != &config)
8461 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8462
8463 /* currentStateModified (optional, default is true) */
8464 mData->mCurrentStateModified = config.fCurrentStateModified;
8465
8466 mData->mLastStateChange = config.timeLastStateChange;
8467
8468 /*
8469 * note: all mUserData members must be assigned prior this point because
8470 * we need to commit changes in order to let mUserData be shared by all
8471 * snapshot machine instances.
8472 */
8473 mUserData.commitCopy();
8474
8475 // machine registry, if present (must be loaded before snapshots)
8476 if (config.canHaveOwnMediaRegistry())
8477 {
8478 // determine machine folder
8479 Utf8Str strMachineFolder = i_getSettingsFileFull();
8480 strMachineFolder.stripFilename();
8481 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8482 config.mediaRegistry,
8483 strMachineFolder);
8484 if (FAILED(hrc)) return hrc;
8485 }
8486
8487 /* Snapshot node (optional) */
8488 size_t cRootSnapshots;
8489 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8490 {
8491 // there must be only one root snapshot
8492 Assert(cRootSnapshots == 1);
8493 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8494
8495 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
8496 if (FAILED(hrc)) return hrc;
8497 }
8498
8499 // hardware data
8500 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
8501 config.recordingSettings);
8502 if (FAILED(hrc)) return hrc;
8503
8504 /*
8505 * NOTE: the assignment below must be the last thing to do,
8506 * otherwise it will be not possible to change the settings
8507 * somewhere in the code above because all setters will be
8508 * blocked by i_checkStateDependency(MutableStateDep).
8509 */
8510
8511 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8512 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8513 {
8514 /* no need to use i_setMachineState() during init() */
8515 mData->mMachineState = MachineState_AbortedSaved;
8516 }
8517 else if (config.fAborted)
8518 {
8519 mSSData->strStateFilePath.setNull();
8520
8521 /* no need to use i_setMachineState() during init() */
8522 mData->mMachineState = MachineState_Aborted;
8523 }
8524 else if (!mSSData->strStateFilePath.isEmpty())
8525 {
8526 /* no need to use i_setMachineState() during init() */
8527 mData->mMachineState = MachineState_Saved;
8528 }
8529
8530 // after loading settings, we are no longer different from the XML on disk
8531 mData->flModifications = 0;
8532
8533 return S_OK;
8534}
8535
8536/**
8537 * Loads all snapshots starting from the given settings.
8538 *
8539 * @param data snapshot settings.
8540 * @param aCurSnapshotId Current snapshot ID from the settings file.
8541 */
8542HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8543 const Guid &aCurSnapshotId)
8544{
8545 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8546 AssertReturn(!i_isSessionMachine(), E_FAIL);
8547
8548 HRESULT hrc = S_OK;
8549
8550 std::list<const settings::Snapshot *> llSettingsTodo;
8551 llSettingsTodo.push_back(&data);
8552 std::list<Snapshot *> llParentsTodo;
8553 llParentsTodo.push_back(NULL);
8554
8555 while (llSettingsTodo.size() > 0)
8556 {
8557 const settings::Snapshot *current = llSettingsTodo.front();
8558 llSettingsTodo.pop_front();
8559 Snapshot *pParent = llParentsTodo.front();
8560 llParentsTodo.pop_front();
8561
8562 Utf8Str strStateFile;
8563 if (!current->strStateFile.isEmpty())
8564 {
8565 /* optional */
8566 strStateFile = current->strStateFile;
8567 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8568 if (RT_FAILURE(vrc))
8569 {
8570 setErrorBoth(E_FAIL, vrc,
8571 tr("Invalid saved state file path '%s' (%Rrc)"),
8572 strStateFile.c_str(), vrc);
8573 }
8574 }
8575
8576 /* create a snapshot machine object */
8577 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8578 pSnapshotMachine.createObject();
8579 hrc = pSnapshotMachine->initFromSettings(this,
8580 current->hardware,
8581 &current->debugging,
8582 &current->autostart,
8583 current->recordingSettings,
8584 current->uuid.ref(),
8585 strStateFile);
8586 if (FAILED(hrc)) break;
8587
8588 /* create a snapshot object */
8589 ComObjPtr<Snapshot> pSnapshot;
8590 pSnapshot.createObject();
8591 /* initialize the snapshot */
8592 hrc = pSnapshot->init(mParent, // VirtualBox object
8593 current->uuid,
8594 current->strName,
8595 current->strDescription,
8596 current->timestamp,
8597 pSnapshotMachine,
8598 pParent);
8599 if (FAILED(hrc)) break;
8600
8601 /* memorize the first snapshot if necessary */
8602 if (!mData->mFirstSnapshot)
8603 {
8604 Assert(pParent == NULL);
8605 mData->mFirstSnapshot = pSnapshot;
8606 }
8607
8608 /* memorize the current snapshot when appropriate */
8609 if ( !mData->mCurrentSnapshot
8610 && pSnapshot->i_getId() == aCurSnapshotId
8611 )
8612 mData->mCurrentSnapshot = pSnapshot;
8613
8614 /* create all children */
8615 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8616 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8617 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8618 {
8619 llSettingsTodo.push_back(&*it);
8620 llParentsTodo.push_back(pSnapshot);
8621 }
8622 }
8623
8624 return hrc;
8625}
8626
8627/**
8628 * Loads settings into mHWData.
8629 *
8630 * @param puuidRegistry Registry ID.
8631 * @param puuidSnapshot Snapshot ID
8632 * @param data Reference to the hardware settings.
8633 * @param pDbg Pointer to the debugging settings.
8634 * @param pAutostart Pointer to the autostart settings
8635 * @param recording Reference to recording settings.
8636 */
8637HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8638 const Guid *puuidSnapshot,
8639 const settings::Hardware &data,
8640 const settings::Debugging *pDbg,
8641 const settings::Autostart *pAutostart,
8642 const settings::Recording &recording)
8643{
8644 AssertReturn(!i_isSessionMachine(), E_FAIL);
8645
8646 HRESULT hrc = S_OK;
8647
8648 try
8649 {
8650 ComObjPtr<GuestOSType> pGuestOSType;
8651 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8652
8653 /* The hardware version attribute (optional). */
8654 mHWData->mHWVersion = data.strVersion;
8655 mHWData->mHardwareUUID = data.uuid;
8656
8657 mHWData->mCPUCount = data.cCPUs;
8658 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8659 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8660 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8661 mHWData->mCpuProfile = data.strCpuProfile;
8662
8663 // cpu
8664 if (mHWData->mCPUHotPlugEnabled)
8665 {
8666 for (settings::CpuList::const_iterator
8667 it = data.llCpus.begin();
8668 it != data.llCpus.end();
8669 ++it)
8670 {
8671 const settings::Cpu &cpu = *it;
8672
8673 mHWData->mCPUAttached[cpu.ulId] = true;
8674 }
8675 }
8676
8677 mHWData->mMemorySize = data.ulMemorySizeMB;
8678 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8679
8680 // boot order
8681 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8682 {
8683 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8684 if (it == data.mapBootOrder.end())
8685 mHWData->mBootOrder[i] = DeviceType_Null;
8686 else
8687 mHWData->mBootOrder[i] = it->second;
8688 }
8689
8690 mHWData->mPointingHIDType = data.pointingHIDType;
8691 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8692 mHWData->mParavirtProvider = data.paravirtProvider;
8693 mHWData->mParavirtDebug = data.strParavirtDebug;
8694 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8695
8696 /* GraphicsAdapter */
8697 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8698 if (FAILED(hrc)) return hrc;
8699
8700 /* VRDEServer */
8701 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8702 if (FAILED(hrc)) return hrc;
8703
8704 /* Platform */
8705 hrc = mPlatform->i_loadSettings(data.platformSettings);
8706 if (FAILED(hrc)) return hrc;
8707
8708 i_platformPropertiesUpdate();
8709
8710 /* Firmware */
8711 hrc = mFirmwareSettings->i_loadSettings(data.firmwareSettings);
8712 if (FAILED(hrc)) return hrc;
8713
8714 /* Recording */
8715 hrc = mRecordingSettings->i_loadSettings(recording);
8716 if (FAILED(hrc)) return hrc;
8717
8718 /* Trusted Platform Module */
8719 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8720 if (FAILED(hrc)) return hrc;
8721
8722 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
8723 if (FAILED(hrc)) return hrc;
8724
8725 // Bandwidth control (must come before network adapters)
8726 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
8727 if (FAILED(hrc)) return hrc;
8728
8729 /* USB controllers */
8730 for (settings::USBControllerList::const_iterator
8731 it = data.usbSettings.llUSBControllers.begin();
8732 it != data.usbSettings.llUSBControllers.end();
8733 ++it)
8734 {
8735 const settings::USBController &settingsCtrl = *it;
8736 ComObjPtr<USBController> newCtrl;
8737
8738 newCtrl.createObject();
8739 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8740 mUSBControllers->push_back(newCtrl);
8741 }
8742
8743 /* USB device filters */
8744 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8745 if (FAILED(hrc)) return hrc;
8746
8747 // network adapters (establish array size first and apply defaults, to
8748 // ensure reading the same settings as we saved, since the list skips
8749 // adapters having defaults)
8750 size_t const newCount = PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType);
8751 size_t const oldCount = mNetworkAdapters.size();
8752 if (newCount > oldCount)
8753 {
8754 mNetworkAdapters.resize(newCount);
8755 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8756 {
8757 unconst(mNetworkAdapters[slot]).createObject();
8758 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8759 }
8760 }
8761 else if (newCount < oldCount)
8762 mNetworkAdapters.resize(newCount);
8763 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8764 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8765 for (settings::NetworkAdaptersList::const_iterator
8766 it = data.llNetworkAdapters.begin();
8767 it != data.llNetworkAdapters.end();
8768 ++it)
8769 {
8770 const settings::NetworkAdapter &nic = *it;
8771
8772 /* slot uniqueness is guaranteed by XML Schema */
8773 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8774 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8775 if (FAILED(hrc)) return hrc;
8776 }
8777
8778 // serial ports (establish defaults first, to ensure reading the same
8779 // settings as we saved, since the list skips ports having defaults)
8780 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8781 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8782 for (settings::SerialPortsList::const_iterator
8783 it = data.llSerialPorts.begin();
8784 it != data.llSerialPorts.end();
8785 ++it)
8786 {
8787 const settings::SerialPort &s = *it;
8788
8789 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8790 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8791 if (FAILED(hrc)) return hrc;
8792 }
8793
8794 // parallel ports (establish defaults first, to ensure reading the same
8795 // settings as we saved, since the list skips ports having defaults)
8796 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8797 mParallelPorts[i]->i_applyDefaults();
8798 for (settings::ParallelPortsList::const_iterator
8799 it = data.llParallelPorts.begin();
8800 it != data.llParallelPorts.end();
8801 ++it)
8802 {
8803 const settings::ParallelPort &p = *it;
8804
8805 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8806 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8807 if (FAILED(hrc)) return hrc;
8808 }
8809
8810 /* Audio settings */
8811 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
8812 if (FAILED(hrc)) return hrc;
8813
8814 /* storage controllers */
8815 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
8816 if (FAILED(hrc)) return hrc;
8817
8818 /* Shared folders */
8819 for (settings::SharedFoldersList::const_iterator
8820 it = data.llSharedFolders.begin();
8821 it != data.llSharedFolders.end();
8822 ++it)
8823 {
8824 const settings::SharedFolder &sf = *it;
8825
8826 ComObjPtr<SharedFolder> sharedFolder;
8827 /* Check for double entries. Not allowed! */
8828 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8829 if (SUCCEEDED(hrc))
8830 return setError(VBOX_E_OBJECT_IN_USE,
8831 tr("Shared folder named '%s' already exists"),
8832 sf.strName.c_str());
8833
8834 /* Create the new shared folder. Don't break on error. This will be
8835 * reported when the machine starts. */
8836 sharedFolder.createObject();
8837 hrc = sharedFolder->init(i_getMachine(),
8838 sf.strName,
8839 sf.strHostPath,
8840 RT_BOOL(sf.fWritable),
8841 RT_BOOL(sf.fAutoMount),
8842 sf.strAutoMountPoint,
8843 false /* fFailOnError */,
8844 sf.enmSymlinkPolicy);
8845 if (FAILED(hrc)) return hrc;
8846 mHWData->mSharedFolders.push_back(sharedFolder);
8847 }
8848
8849 // Clipboard
8850 mHWData->mClipboardMode = data.clipboardMode;
8851 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8852
8853 // drag'n'drop
8854 mHWData->mDnDMode = data.dndMode;
8855
8856 // guest settings
8857 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8858
8859 // IO settings
8860 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8861 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8862
8863 // Host PCI devices
8864 for (settings::HostPCIDeviceAttachmentList::const_iterator
8865 it = data.pciAttachments.begin();
8866 it != data.pciAttachments.end();
8867 ++it)
8868 {
8869 const settings::HostPCIDeviceAttachment &hpda = *it;
8870 ComObjPtr<PCIDeviceAttachment> pda;
8871
8872 pda.createObject();
8873 pda->i_loadSettings(this, hpda);
8874 mHWData->mPCIDeviceAssignments.push_back(pda);
8875 }
8876
8877 /*
8878 * (The following isn't really real hardware, but it lives in HWData
8879 * for reasons of convenience.)
8880 */
8881
8882#ifdef VBOX_WITH_GUEST_PROPS
8883 /* Guest properties (optional) */
8884
8885 /* Only load transient guest properties for configs which have saved
8886 * state, because there shouldn't be any for powered off VMs. The same
8887 * logic applies for snapshots, as offline snapshots shouldn't have
8888 * any such properties. They confuse the code in various places.
8889 * Note: can't rely on the machine state, as it isn't set yet. */
8890 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8891 /* apologies for the hacky unconst() usage, but this needs hacking
8892 * actually inconsistent settings into consistency, otherwise there
8893 * will be some corner cases where the inconsistency survives
8894 * surprisingly long without getting fixed, especially for snapshots
8895 * as there are no config changes. */
8896 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8897 for (settings::GuestPropertiesList::iterator
8898 it = llGuestProperties.begin();
8899 it != llGuestProperties.end();
8900 /*nothing*/)
8901 {
8902 const settings::GuestProperty &prop = *it;
8903 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8904 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8905 if ( fSkipTransientGuestProperties
8906 && ( fFlags & GUEST_PROP_F_TRANSIENT
8907 || fFlags & GUEST_PROP_F_TRANSRESET))
8908 {
8909 it = llGuestProperties.erase(it);
8910 continue;
8911 }
8912 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8913 mHWData->mGuestProperties[prop.strName] = property;
8914 ++it;
8915 }
8916#endif /* VBOX_WITH_GUEST_PROPS defined */
8917
8918 hrc = i_loadDebugging(pDbg);
8919 if (FAILED(hrc))
8920 return hrc;
8921
8922 mHWData->mAutostart = *pAutostart;
8923
8924 /* default frontend */
8925 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8926 }
8927 catch (std::bad_alloc &)
8928 {
8929 return E_OUTOFMEMORY;
8930 }
8931
8932 AssertComRC(hrc);
8933 return hrc;
8934}
8935
8936/**
8937 * Called from i_loadHardware() to load the debugging settings of the
8938 * machine.
8939 *
8940 * @param pDbg Pointer to the settings.
8941 */
8942HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8943{
8944 mHWData->mDebugging = *pDbg;
8945 /* no more processing currently required, this will probably change. */
8946
8947 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
8948 if (FAILED(hrc)) return hrc;
8949
8950 return S_OK;
8951}
8952
8953/**
8954 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8955 *
8956 * @param data storage settings.
8957 * @param puuidRegistry media registry ID to set media to or NULL;
8958 * see Machine::i_loadMachineDataFromSettings()
8959 * @param puuidSnapshot snapshot ID
8960 * @return
8961 */
8962HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8963 const Guid *puuidRegistry,
8964 const Guid *puuidSnapshot)
8965{
8966 AssertReturn(!i_isSessionMachine(), E_FAIL);
8967
8968 HRESULT hrc = S_OK;
8969
8970 for (settings::StorageControllersList::const_iterator
8971 it = data.llStorageControllers.begin();
8972 it != data.llStorageControllers.end();
8973 ++it)
8974 {
8975 const settings::StorageController &ctlData = *it;
8976
8977 ComObjPtr<StorageController> pCtl;
8978 /* Try to find one with the name first. */
8979 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8980 if (SUCCEEDED(hrc))
8981 return setError(VBOX_E_OBJECT_IN_USE,
8982 tr("Storage controller named '%s' already exists"),
8983 ctlData.strName.c_str());
8984
8985 pCtl.createObject();
8986 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
8987 if (FAILED(hrc)) return hrc;
8988
8989 mStorageControllers->push_back(pCtl);
8990
8991 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8992 if (FAILED(hrc)) return hrc;
8993
8994 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8995 if (FAILED(hrc)) return hrc;
8996
8997 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8998 if (FAILED(hrc)) return hrc;
8999
9000 /* Load the attached devices now. */
9001 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
9002 if (FAILED(hrc)) return hrc;
9003 }
9004
9005 return S_OK;
9006}
9007
9008/**
9009 * Called from i_loadStorageControllers for a controller's devices.
9010 *
9011 * @param aStorageController
9012 * @param data
9013 * @param puuidRegistry media registry ID to set media to or NULL; see
9014 * Machine::i_loadMachineDataFromSettings()
9015 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9016 * @return
9017 */
9018HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9019 const settings::StorageController &data,
9020 const Guid *puuidRegistry,
9021 const Guid *puuidSnapshot)
9022{
9023 HRESULT hrc = S_OK;
9024
9025 /* paranoia: detect duplicate attachments */
9026 for (settings::AttachedDevicesList::const_iterator
9027 it = data.llAttachedDevices.begin();
9028 it != data.llAttachedDevices.end();
9029 ++it)
9030 {
9031 const settings::AttachedDevice &ad = *it;
9032
9033 for (settings::AttachedDevicesList::const_iterator it2 = it;
9034 it2 != data.llAttachedDevices.end();
9035 ++it2)
9036 {
9037 if (it == it2)
9038 continue;
9039
9040 const settings::AttachedDevice &ad2 = *it2;
9041
9042 if ( ad.lPort == ad2.lPort
9043 && ad.lDevice == ad2.lDevice)
9044 {
9045 return setError(E_FAIL,
9046 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9047 aStorageController->i_getName().c_str(),
9048 ad.lPort,
9049 ad.lDevice,
9050 mUserData->s.strName.c_str());
9051 }
9052 }
9053 }
9054
9055 for (settings::AttachedDevicesList::const_iterator
9056 it = data.llAttachedDevices.begin();
9057 it != data.llAttachedDevices.end();
9058 ++it)
9059 {
9060 const settings::AttachedDevice &dev = *it;
9061 ComObjPtr<Medium> medium;
9062
9063 switch (dev.deviceType)
9064 {
9065 case DeviceType_Floppy:
9066 case DeviceType_DVD:
9067 if (dev.strHostDriveSrc.isNotEmpty())
9068 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9069 false /* fRefresh */, medium);
9070 else
9071 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9072 dev.uuid,
9073 false /* fRefresh */,
9074 false /* aSetError */,
9075 medium);
9076 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9077 // This is not an error. The host drive or UUID might have vanished, so just go
9078 // ahead without this removeable medium attachment
9079 hrc = S_OK;
9080 break;
9081
9082 case DeviceType_HardDisk:
9083 {
9084 /* find a hard disk by UUID */
9085 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9086 if (FAILED(hrc))
9087 {
9088 if (i_isSnapshotMachine())
9089 {
9090 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9091 // so the user knows that the bad disk is in a snapshot somewhere
9092 com::ErrorInfo info;
9093 return setError(E_FAIL,
9094 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9095 puuidSnapshot->raw(),
9096 info.getText().raw());
9097 }
9098 return hrc;
9099 }
9100
9101 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9102
9103 if (medium->i_getType() == MediumType_Immutable)
9104 {
9105 if (i_isSnapshotMachine())
9106 return setError(E_FAIL,
9107 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9108 "of the virtual machine '%s' ('%s')"),
9109 medium->i_getLocationFull().c_str(),
9110 dev.uuid.raw(),
9111 puuidSnapshot->raw(),
9112 mUserData->s.strName.c_str(),
9113 mData->m_strConfigFileFull.c_str());
9114
9115 return setError(E_FAIL,
9116 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9117 medium->i_getLocationFull().c_str(),
9118 dev.uuid.raw(),
9119 mUserData->s.strName.c_str(),
9120 mData->m_strConfigFileFull.c_str());
9121 }
9122
9123 if (medium->i_getType() == MediumType_MultiAttach)
9124 {
9125 if (i_isSnapshotMachine())
9126 return setError(E_FAIL,
9127 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9128 "of the virtual machine '%s' ('%s')"),
9129 medium->i_getLocationFull().c_str(),
9130 dev.uuid.raw(),
9131 puuidSnapshot->raw(),
9132 mUserData->s.strName.c_str(),
9133 mData->m_strConfigFileFull.c_str());
9134
9135 return setError(E_FAIL,
9136 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9137 medium->i_getLocationFull().c_str(),
9138 dev.uuid.raw(),
9139 mUserData->s.strName.c_str(),
9140 mData->m_strConfigFileFull.c_str());
9141 }
9142
9143 if ( !i_isSnapshotMachine()
9144 && medium->i_getChildren().size() != 0
9145 )
9146 return setError(E_FAIL,
9147 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9148 "because it has %d differencing child hard disks"),
9149 medium->i_getLocationFull().c_str(),
9150 dev.uuid.raw(),
9151 mUserData->s.strName.c_str(),
9152 mData->m_strConfigFileFull.c_str(),
9153 medium->i_getChildren().size());
9154
9155 if (i_findAttachment(*mMediumAttachments.data(),
9156 medium))
9157 return setError(E_FAIL,
9158 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9159 medium->i_getLocationFull().c_str(),
9160 dev.uuid.raw(),
9161 mUserData->s.strName.c_str(),
9162 mData->m_strConfigFileFull.c_str());
9163
9164 break;
9165 }
9166
9167 default:
9168 return setError(E_FAIL,
9169 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9170 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9171 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9172 }
9173
9174 if (FAILED(hrc))
9175 break;
9176
9177 /* Bandwidth groups are loaded at this point. */
9178 ComObjPtr<BandwidthGroup> pBwGroup;
9179
9180 if (!dev.strBwGroup.isEmpty())
9181 {
9182 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9183 if (FAILED(hrc))
9184 return setError(E_FAIL,
9185 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9186 medium->i_getLocationFull().c_str(),
9187 dev.strBwGroup.c_str(),
9188 mUserData->s.strName.c_str(),
9189 mData->m_strConfigFileFull.c_str());
9190 pBwGroup->i_reference();
9191 }
9192
9193 const Utf8Str controllerName = aStorageController->i_getName();
9194 ComObjPtr<MediumAttachment> pAttachment;
9195 pAttachment.createObject();
9196 hrc = pAttachment->init(this,
9197 medium,
9198 controllerName,
9199 dev.lPort,
9200 dev.lDevice,
9201 dev.deviceType,
9202 false,
9203 dev.fPassThrough,
9204 dev.fTempEject,
9205 dev.fNonRotational,
9206 dev.fDiscard,
9207 dev.fHotPluggable,
9208 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9209 if (FAILED(hrc)) break;
9210
9211 /* associate the medium with this machine and snapshot */
9212 if (!medium.isNull())
9213 {
9214 AutoCaller medCaller(medium);
9215 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9216 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9217
9218 if (i_isSnapshotMachine())
9219 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9220 else
9221 hrc = medium->i_addBackReference(mData->mUuid);
9222 /* If the medium->addBackReference fails it sets an appropriate
9223 * error message, so no need to do any guesswork here. */
9224
9225 if (puuidRegistry)
9226 // caller wants registry ID to be set on all attached media (OVF import case)
9227 medium->i_addRegistry(*puuidRegistry);
9228 }
9229
9230 if (FAILED(hrc))
9231 break;
9232
9233 /* back up mMediumAttachments to let registeredInit() properly rollback
9234 * on failure (= limited accessibility) */
9235 i_setModified(IsModified_Storage);
9236 mMediumAttachments.backup();
9237 mMediumAttachments->push_back(pAttachment);
9238 }
9239
9240 return hrc;
9241}
9242
9243/**
9244 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9245 *
9246 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9247 * @param aSnapshot where to return the found snapshot
9248 * @param aSetError true to set extended error info on failure
9249 */
9250HRESULT Machine::i_findSnapshotById(const Guid &aId,
9251 ComObjPtr<Snapshot> &aSnapshot,
9252 bool aSetError /* = false */)
9253{
9254 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9255
9256 if (!mData->mFirstSnapshot)
9257 {
9258 if (aSetError)
9259 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9260 return E_FAIL;
9261 }
9262
9263 if (aId.isZero())
9264 aSnapshot = mData->mFirstSnapshot;
9265 else
9266 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9267
9268 if (!aSnapshot)
9269 {
9270 if (aSetError)
9271 return setError(E_FAIL,
9272 tr("Could not find a snapshot with UUID {%s}"),
9273 aId.toString().c_str());
9274 return E_FAIL;
9275 }
9276
9277 return S_OK;
9278}
9279
9280/**
9281 * Returns the snapshot with the given name or fails of no such snapshot.
9282 *
9283 * @param strName snapshot name to find
9284 * @param aSnapshot where to return the found snapshot
9285 * @param aSetError true to set extended error info on failure
9286 */
9287HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9288 ComObjPtr<Snapshot> &aSnapshot,
9289 bool aSetError /* = false */)
9290{
9291 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9292
9293 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9294
9295 if (!mData->mFirstSnapshot)
9296 {
9297 if (aSetError)
9298 return setError(VBOX_E_OBJECT_NOT_FOUND,
9299 tr("This machine does not have any snapshots"));
9300 return VBOX_E_OBJECT_NOT_FOUND;
9301 }
9302
9303 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9304
9305 if (!aSnapshot)
9306 {
9307 if (aSetError)
9308 return setError(VBOX_E_OBJECT_NOT_FOUND,
9309 tr("Could not find a snapshot named '%s'"), strName.c_str());
9310 return VBOX_E_OBJECT_NOT_FOUND;
9311 }
9312
9313 return S_OK;
9314}
9315
9316/**
9317 * Returns a storage controller object with the given name.
9318 *
9319 * @param aName storage controller name to find
9320 * @param aStorageController where to return the found storage controller
9321 * @param aSetError true to set extended error info on failure
9322 */
9323HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9324 ComObjPtr<StorageController> &aStorageController,
9325 bool aSetError /* = false */)
9326{
9327 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9328
9329 for (StorageControllerList::const_iterator
9330 it = mStorageControllers->begin();
9331 it != mStorageControllers->end();
9332 ++it)
9333 {
9334 if ((*it)->i_getName() == aName)
9335 {
9336 aStorageController = (*it);
9337 return S_OK;
9338 }
9339 }
9340
9341 if (aSetError)
9342 return setError(VBOX_E_OBJECT_NOT_FOUND,
9343 tr("Could not find a storage controller named '%s'"),
9344 aName.c_str());
9345 return VBOX_E_OBJECT_NOT_FOUND;
9346}
9347
9348/**
9349 * Returns a USB controller object with the given name.
9350 *
9351 * @param aName USB controller name to find
9352 * @param aUSBController where to return the found USB controller
9353 * @param aSetError true to set extended error info on failure
9354 */
9355HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9356 ComObjPtr<USBController> &aUSBController,
9357 bool aSetError /* = false */)
9358{
9359 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9360
9361 for (USBControllerList::const_iterator
9362 it = mUSBControllers->begin();
9363 it != mUSBControllers->end();
9364 ++it)
9365 {
9366 if ((*it)->i_getName() == aName)
9367 {
9368 aUSBController = (*it);
9369 return S_OK;
9370 }
9371 }
9372
9373 if (aSetError)
9374 return setError(VBOX_E_OBJECT_NOT_FOUND,
9375 tr("Could not find a storage controller named '%s'"),
9376 aName.c_str());
9377 return VBOX_E_OBJECT_NOT_FOUND;
9378}
9379
9380/**
9381 * Returns the number of USB controller instance of the given type.
9382 *
9383 * @param enmType USB controller type.
9384 */
9385ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9386{
9387 ULONG cCtrls = 0;
9388
9389 for (USBControllerList::const_iterator
9390 it = mUSBControllers->begin();
9391 it != mUSBControllers->end();
9392 ++it)
9393 {
9394 if ((*it)->i_getControllerType() == enmType)
9395 cCtrls++;
9396 }
9397
9398 return cCtrls;
9399}
9400
9401HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9402 MediumAttachmentList &atts)
9403{
9404 AutoCaller autoCaller(this);
9405 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
9406
9407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9408
9409 for (MediumAttachmentList::const_iterator
9410 it = mMediumAttachments->begin();
9411 it != mMediumAttachments->end();
9412 ++it)
9413 {
9414 const ComObjPtr<MediumAttachment> &pAtt = *it;
9415 // should never happen, but deal with NULL pointers in the list.
9416 AssertContinue(!pAtt.isNull());
9417
9418 // getControllerName() needs caller+read lock
9419 AutoCaller autoAttCaller(pAtt);
9420 if (FAILED(autoAttCaller.hrc()))
9421 {
9422 atts.clear();
9423 return autoAttCaller.hrc();
9424 }
9425 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9426
9427 if (pAtt->i_getControllerName() == aName)
9428 atts.push_back(pAtt);
9429 }
9430
9431 return S_OK;
9432}
9433
9434
9435/**
9436 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9437 * file if the machine name was changed and about creating a new settings file
9438 * if this is a new machine.
9439 *
9440 * @note Must be never called directly but only from #saveSettings().
9441 */
9442HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9443 bool *pfSettingsFileIsNew)
9444{
9445 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9446
9447 HRESULT hrc = S_OK;
9448
9449 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9450 /// @todo need to handle primary group change, too
9451
9452 /* attempt to rename the settings file if machine name is changed */
9453 if ( mUserData->s.fNameSync
9454 && mUserData.isBackedUp()
9455 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9456 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9457 )
9458 {
9459 bool dirRenamed = false;
9460 bool fileRenamed = false;
9461
9462 Utf8Str configFile, newConfigFile;
9463 Utf8Str configFilePrev, newConfigFilePrev;
9464 Utf8Str NVRAMFile, newNVRAMFile;
9465 Utf8Str configDir, newConfigDir;
9466
9467 do
9468 {
9469 int vrc = VINF_SUCCESS;
9470
9471 Utf8Str name = mUserData.backedUpData()->s.strName;
9472 Utf8Str newName = mUserData->s.strName;
9473 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9474 if (group == "/")
9475 group.setNull();
9476 Utf8Str newGroup = mUserData->s.llGroups.front();
9477 if (newGroup == "/")
9478 newGroup.setNull();
9479
9480 configFile = mData->m_strConfigFileFull;
9481
9482 /* first, rename the directory if it matches the group and machine name */
9483 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9484 /** @todo hack, make somehow use of ComposeMachineFilename */
9485 if (mUserData->s.fDirectoryIncludesUUID)
9486 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9487 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9488 /** @todo hack, make somehow use of ComposeMachineFilename */
9489 if (mUserData->s.fDirectoryIncludesUUID)
9490 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9491 configDir = configFile;
9492 configDir.stripFilename();
9493 newConfigDir = configDir;
9494 if ( configDir.length() >= groupPlusName.length()
9495 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9496 groupPlusName.c_str()))
9497 {
9498 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9499 Utf8Str newConfigBaseDir(newConfigDir);
9500 newConfigDir.append(newGroupPlusName);
9501 /* consistency: use \ if appropriate on the platform */
9502 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9503 /* new dir and old dir cannot be equal here because of 'if'
9504 * above and because name != newName */
9505 Assert(configDir != newConfigDir);
9506 if (!fSettingsFileIsNew)
9507 {
9508 /* perform real rename only if the machine is not new */
9509 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9510 if ( vrc == VERR_FILE_NOT_FOUND
9511 || vrc == VERR_PATH_NOT_FOUND)
9512 {
9513 /* create the parent directory, then retry renaming */
9514 Utf8Str parent(newConfigDir);
9515 parent.stripFilename();
9516 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9517 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9518 }
9519 if (RT_FAILURE(vrc))
9520 {
9521 hrc = setErrorBoth(E_FAIL, vrc,
9522 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9523 configDir.c_str(),
9524 newConfigDir.c_str(),
9525 vrc);
9526 break;
9527 }
9528 /* delete subdirectories which are no longer needed */
9529 Utf8Str dir(configDir);
9530 dir.stripFilename();
9531 while (dir != newConfigBaseDir && dir != ".")
9532 {
9533 vrc = RTDirRemove(dir.c_str());
9534 if (RT_FAILURE(vrc))
9535 break;
9536 dir.stripFilename();
9537 }
9538 dirRenamed = true;
9539 }
9540 }
9541
9542 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9543
9544 /* then try to rename the settings file itself */
9545 if (newConfigFile != configFile)
9546 {
9547 /* get the path to old settings file in renamed directory */
9548 Assert(mData->m_strConfigFileFull == configFile);
9549 configFile.printf("%s%c%s",
9550 newConfigDir.c_str(),
9551 RTPATH_DELIMITER,
9552 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9553 if (!fSettingsFileIsNew)
9554 {
9555 /* perform real rename only if the machine is not new */
9556 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9557 if (RT_FAILURE(vrc))
9558 {
9559 hrc = setErrorBoth(E_FAIL, vrc,
9560 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9561 configFile.c_str(),
9562 newConfigFile.c_str(),
9563 vrc);
9564 break;
9565 }
9566 fileRenamed = true;
9567 configFilePrev = configFile;
9568 configFilePrev += "-prev";
9569 newConfigFilePrev = newConfigFile;
9570 newConfigFilePrev += "-prev";
9571 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9572 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9573 if (NVRAMFile.isNotEmpty())
9574 {
9575 // in the NVRAM file path, replace the old directory with the new directory
9576 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9577 {
9578 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9579 NVRAMFile = newConfigDir + strNVRAMFile;
9580 }
9581 newNVRAMFile = newConfigFile;
9582 newNVRAMFile.stripSuffix();
9583 newNVRAMFile += ".nvram";
9584 RTPathRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9585 }
9586 }
9587 }
9588
9589 // update m_strConfigFileFull amd mConfigFile
9590 mData->m_strConfigFileFull = newConfigFile;
9591 // compute the relative path too
9592 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9593
9594 // store the old and new so that VirtualBox::i_saveSettings() can update
9595 // the media registry
9596 if ( mData->mRegistered
9597 && (configDir != newConfigDir || configFile != newConfigFile))
9598 {
9599 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9600
9601 if (pfNeedsGlobalSaveSettings)
9602 *pfNeedsGlobalSaveSettings = true;
9603 }
9604
9605 // in the saved state file path, replace the old directory with the new directory
9606 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9607 {
9608 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9609 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9610 }
9611 if (newNVRAMFile.isNotEmpty())
9612 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9613
9614 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9615 if (mData->mFirstSnapshot)
9616 {
9617 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9618 newConfigDir.c_str());
9619 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9620 newConfigDir.c_str());
9621 }
9622 }
9623 while (0);
9624
9625 if (FAILED(hrc))
9626 {
9627 /* silently try to rename everything back */
9628 if (fileRenamed)
9629 {
9630 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9631 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9632 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9633 RTPathRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9634 }
9635 if (dirRenamed)
9636 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9637 }
9638
9639 if (FAILED(hrc)) return hrc;
9640 }
9641
9642 if (fSettingsFileIsNew)
9643 {
9644 /* create a virgin config file */
9645 int vrc = VINF_SUCCESS;
9646
9647 /* ensure the settings directory exists */
9648 Utf8Str path(mData->m_strConfigFileFull);
9649 path.stripFilename();
9650 if (!RTDirExists(path.c_str()))
9651 {
9652 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9653 if (RT_FAILURE(vrc))
9654 {
9655 return setErrorBoth(E_FAIL, vrc,
9656 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9657 path.c_str(),
9658 vrc);
9659 }
9660 }
9661
9662 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9663 path = mData->m_strConfigFileFull;
9664 RTFILE f = NIL_RTFILE;
9665 vrc = RTFileOpen(&f, path.c_str(),
9666 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9667 if (RT_FAILURE(vrc))
9668 return setErrorBoth(E_FAIL, vrc,
9669 tr("Could not create the settings file '%s' (%Rrc)"),
9670 path.c_str(),
9671 vrc);
9672 RTFileClose(f);
9673 }
9674 if (pfSettingsFileIsNew)
9675 *pfSettingsFileIsNew = fSettingsFileIsNew;
9676
9677 return hrc;
9678}
9679
9680/**
9681 * Saves and commits machine data, user data and hardware data.
9682 *
9683 * Note that on failure, the data remains uncommitted.
9684 *
9685 * @a aFlags may combine the following flags:
9686 *
9687 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9688 * Used when saving settings after an operation that makes them 100%
9689 * correspond to the settings from the current snapshot.
9690 * - SaveS_Force: settings will be saved without doing a deep compare of the
9691 * settings structures. This is used when this is called because snapshots
9692 * have changed to avoid the overhead of the deep compare.
9693 *
9694 * @note Must be called from under this object's write lock. Locks children for
9695 * writing.
9696 *
9697 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9698 * initialized to false and that will be set to true by this function if
9699 * the caller must invoke VirtualBox::i_saveSettings() because the global
9700 * settings have changed. This will happen if a machine rename has been
9701 * saved and the global machine and media registries will therefore need
9702 * updating.
9703 * @param alock Reference to the lock for this machine object.
9704 * @param aFlags Flags.
9705 */
9706HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9707 AutoWriteLock &alock,
9708 int aFlags /*= 0*/)
9709{
9710 LogFlowThisFuncEnter();
9711
9712 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9713
9714 /* make sure child objects are unable to modify the settings while we are
9715 * saving them */
9716 i_ensureNoStateDependencies(alock);
9717
9718 AssertReturn(!i_isSnapshotMachine(),
9719 E_FAIL);
9720
9721 if (!mData->mAccessible)
9722 return setError(VBOX_E_INVALID_VM_STATE,
9723 tr("The machine is not accessible, so cannot save settings"));
9724
9725 HRESULT hrc = S_OK;
9726 PCVBOXCRYPTOIF pCryptoIf = NULL;
9727 const char *pszPassword = NULL;
9728 SecretKey *pKey = NULL;
9729
9730#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9731 if (mData->mstrKeyId.isNotEmpty())
9732 {
9733 /* VM is going to be encrypted. */
9734 alock.release(); /** @todo Revise the locking. */
9735 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
9736 alock.acquire();
9737 if (FAILED(hrc)) return hrc; /* Error is set. */
9738
9739 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
9740 if (RT_SUCCESS(vrc))
9741 pszPassword = (const char *)pKey->getKeyBuffer();
9742 else
9743 {
9744 mParent->i_releaseCryptoIf(pCryptoIf);
9745 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
9746 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
9747 mData->mstrKeyId.c_str(), vrc);
9748 }
9749 }
9750#else
9751 RT_NOREF(pKey);
9752#endif
9753
9754 bool fNeedsWrite = false;
9755 bool fSettingsFileIsNew = false;
9756
9757 /* First, prepare to save settings. It will care about renaming the
9758 * settings directory and file if the machine name was changed and about
9759 * creating a new settings file if this is a new machine. */
9760 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
9761 if (FAILED(hrc))
9762 {
9763#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9764 if (pCryptoIf)
9765 {
9766 alock.release(); /** @todo Revise the locking. */
9767 mParent->i_releaseCryptoIf(pCryptoIf);
9768 alock.acquire();
9769 }
9770 if (pKey)
9771 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9772#endif
9773 return hrc;
9774 }
9775
9776 // keep a pointer to the current settings structures
9777 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9778 settings::MachineConfigFile *pNewConfig = NULL;
9779
9780 try
9781 {
9782 // make a fresh one to have everyone write stuff into
9783 pNewConfig = new settings::MachineConfigFile(NULL);
9784 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9785#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9786 pNewConfig->strKeyId = mData->mstrKeyId;
9787 pNewConfig->strKeyStore = mData->mstrKeyStore;
9788#endif
9789
9790 // now go and copy all the settings data from COM to the settings structures
9791 // (this calls i_saveSettings() on all the COM objects in the machine)
9792 i_copyMachineDataToSettings(*pNewConfig);
9793
9794 if (aFlags & SaveS_ResetCurStateModified)
9795 {
9796 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9797 mData->mCurrentStateModified = FALSE;
9798 fNeedsWrite = true; // always, no need to compare
9799 }
9800 else if (aFlags & SaveS_Force)
9801 {
9802 fNeedsWrite = true; // always, no need to compare
9803 }
9804 else
9805 {
9806 if (!mData->mCurrentStateModified)
9807 {
9808 // do a deep compare of the settings that we just saved with the settings
9809 // previously stored in the config file; this invokes MachineConfigFile::operator==
9810 // which does a deep compare of all the settings, which is expensive but less expensive
9811 // than writing out XML in vain
9812 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9813
9814 // could still be modified if any settings changed
9815 mData->mCurrentStateModified = fAnySettingsChanged;
9816
9817 fNeedsWrite = fAnySettingsChanged;
9818 }
9819 else
9820 fNeedsWrite = true;
9821 }
9822
9823 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9824
9825 if (fNeedsWrite)
9826 {
9827 // now spit it all out!
9828 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
9829 if (aFlags & SaveS_RemoveBackup)
9830 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
9831 }
9832
9833 mData->pMachineConfigFile = pNewConfig;
9834 delete pOldConfig;
9835 i_commit();
9836
9837 // after saving settings, we are no longer different from the XML on disk
9838 mData->flModifications = 0;
9839 }
9840 catch (HRESULT err)
9841 {
9842 // we assume that error info is set by the thrower
9843 hrc = err;
9844
9845 // delete any newly created settings file
9846 if (fSettingsFileIsNew)
9847 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
9848
9849 // restore old config
9850 delete pNewConfig;
9851 mData->pMachineConfigFile = pOldConfig;
9852 }
9853 catch (...)
9854 {
9855 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9856 }
9857
9858#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9859 if (pCryptoIf)
9860 {
9861 alock.release(); /** @todo Revise the locking. */
9862 mParent->i_releaseCryptoIf(pCryptoIf);
9863 alock.acquire();
9864 }
9865 if (pKey)
9866 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9867#endif
9868
9869 if (fNeedsWrite)
9870 {
9871 /* Fire the data change event, even on failure (since we've already
9872 * committed all data). This is done only for SessionMachines because
9873 * mutable Machine instances are always not registered (i.e. private
9874 * to the client process that creates them) and thus don't need to
9875 * inform callbacks. */
9876 if (i_isSessionMachine())
9877 mParent->i_onMachineDataChanged(mData->mUuid);
9878 }
9879
9880 LogFlowThisFunc(("hrc=%08X\n", hrc));
9881 LogFlowThisFuncLeave();
9882 return hrc;
9883}
9884
9885/**
9886 * Implementation for saving the machine settings into the given
9887 * settings::MachineConfigFile instance. This copies machine extradata
9888 * from the previous machine config file in the instance data, if any.
9889 *
9890 * This gets called from two locations:
9891 *
9892 * -- Machine::i_saveSettings(), during the regular XML writing;
9893 *
9894 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9895 * exported to OVF and we write the VirtualBox proprietary XML
9896 * into a <vbox:Machine> tag.
9897 *
9898 * This routine fills all the fields in there, including snapshots, *except*
9899 * for the following:
9900 *
9901 * -- fCurrentStateModified. There is some special logic associated with that.
9902 *
9903 * The caller can then call MachineConfigFile::write() or do something else
9904 * with it.
9905 *
9906 * Caller must hold the machine lock!
9907 *
9908 * This throws XML errors and HRESULT, so the caller must have a catch block!
9909 */
9910void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9911{
9912 // deep copy extradata, being extra careful with self assignment (the STL
9913 // map assignment on Mac OS X clang based Xcode isn't checking)
9914 if (&config != mData->pMachineConfigFile)
9915 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9916
9917 config.uuid = mData->mUuid;
9918
9919 // copy name, description, OS type, teleport, UTC etc.
9920 config.machineUserData = mUserData->s;
9921
9922#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9923 config.strStateKeyId = mSSData->strStateKeyId;
9924 config.strStateKeyStore = mSSData->strStateKeyStore;
9925 config.strLogKeyId = mData->mstrLogKeyId;
9926 config.strLogKeyStore = mData->mstrLogKeyStore;
9927#endif
9928
9929 if ( mData->mMachineState == MachineState_Saved
9930 || mData->mMachineState == MachineState_AbortedSaved
9931 || mData->mMachineState == MachineState_Restoring
9932 // when doing certain snapshot operations we may or may not have
9933 // a saved state in the current state, so keep everything as is
9934 || ( ( mData->mMachineState == MachineState_Snapshotting
9935 || mData->mMachineState == MachineState_DeletingSnapshot
9936 || mData->mMachineState == MachineState_RestoringSnapshot)
9937 && (!mSSData->strStateFilePath.isEmpty())
9938 )
9939 )
9940 {
9941 Assert(!mSSData->strStateFilePath.isEmpty());
9942 /* try to make the file name relative to the settings file dir */
9943 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9944 }
9945 else
9946 {
9947 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9948 config.strStateFile.setNull();
9949 }
9950
9951 if (mData->mCurrentSnapshot)
9952 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9953 else
9954 config.uuidCurrentSnapshot.clear();
9955
9956 config.timeLastStateChange = mData->mLastStateChange;
9957 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
9958 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9959
9960 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
9961 if (FAILED(hrc)) throw hrc;
9962
9963 // save machine's media registry if this is VirtualBox 4.0 or later
9964 if (config.canHaveOwnMediaRegistry())
9965 {
9966 // determine machine folder
9967 Utf8Str strMachineFolder = i_getSettingsFileFull();
9968 strMachineFolder.stripFilename();
9969 mParent->i_saveMediaRegistry(config.mediaRegistry,
9970 i_getId(), // only media with registry ID == machine UUID
9971 strMachineFolder);
9972 // this throws HRESULT
9973 }
9974
9975 // save snapshots
9976 hrc = i_saveAllSnapshots(config);
9977 if (FAILED(hrc)) throw hrc;
9978}
9979
9980/**
9981 * Saves all snapshots of the machine into the given machine config file. Called
9982 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9983 * @param config
9984 * @return
9985 */
9986HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9987{
9988 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9989
9990 HRESULT hrc = S_OK;
9991
9992 try
9993 {
9994 config.llFirstSnapshot.clear();
9995
9996 if (mData->mFirstSnapshot)
9997 {
9998 // the settings use a list for "the first snapshot"
9999 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10000
10001 // get reference to the snapshot on the list and work on that
10002 // element straight in the list to avoid excessive copying later
10003 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10004 if (FAILED(hrc)) throw hrc;
10005 }
10006
10007// if (mType == IsSessionMachine)
10008// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10009
10010 }
10011 catch (HRESULT err)
10012 {
10013 /* we assume that error info is set by the thrower */
10014 hrc = err;
10015 }
10016 catch (...)
10017 {
10018 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10019 }
10020
10021 return hrc;
10022}
10023
10024/**
10025 * Saves the VM hardware configuration. It is assumed that the
10026 * given node is empty.
10027 *
10028 * @param data Reference to the settings object for the hardware config.
10029 * @param pDbg Pointer to the settings object for the debugging config
10030 * which happens to live in mHWData.
10031 * @param pAutostart Pointer to the settings object for the autostart config
10032 * which happens to live in mHWData.
10033 * @param recording Reference to reecording settings.
10034 */
10035HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10036 settings::Autostart *pAutostart, settings::Recording &recording)
10037{
10038 HRESULT hrc = S_OK;
10039
10040 try
10041 {
10042 /* The hardware version attribute (optional).
10043 Automatically upgrade from 1 to current default hardware version
10044 when there is no saved state. (ugly!) */
10045 if ( mHWData->mHWVersion == "1"
10046 && mSSData->strStateFilePath.isEmpty()
10047 )
10048 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10049
10050 data.strVersion = mHWData->mHWVersion;
10051 data.uuid = mHWData->mHardwareUUID;
10052
10053 // CPU
10054 data.cCPUs = mHWData->mCPUCount;
10055 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10056 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10057 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10058 data.strCpuProfile = mHWData->mCpuProfile;
10059
10060 data.llCpus.clear();
10061 if (data.fCpuHotPlug)
10062 {
10063 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10064 {
10065 if (mHWData->mCPUAttached[idx])
10066 {
10067 settings::Cpu cpu;
10068 cpu.ulId = idx;
10069 data.llCpus.push_back(cpu);
10070 }
10071 }
10072 }
10073
10074 // memory
10075 data.ulMemorySizeMB = mHWData->mMemorySize;
10076 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10077
10078 // HID
10079 data.pointingHIDType = mHWData->mPointingHIDType;
10080 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10081
10082 // paravirt
10083 data.paravirtProvider = mHWData->mParavirtProvider;
10084 data.strParavirtDebug = mHWData->mParavirtDebug;
10085
10086 // emulated USB card reader
10087 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10088
10089 // boot order
10090 data.mapBootOrder.clear();
10091 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10092 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10093
10094 /* VRDEServer settings (optional) */
10095 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10096 if (FAILED(hrc)) throw hrc;
10097
10098 /* Platform (required) */
10099 hrc = mPlatform->i_saveSettings(data.platformSettings);
10100 if (FAILED(hrc)) return hrc;
10101
10102 /* Firmware settings (required) */
10103 hrc = mFirmwareSettings->i_saveSettings(data.firmwareSettings);
10104 if (FAILED(hrc)) throw hrc;
10105
10106 /* Recording settings. */
10107 hrc = mRecordingSettings->i_saveSettings(recording);
10108 if (FAILED(hrc)) throw hrc;
10109
10110 /* Trusted Platform Module settings (required) */
10111 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10112 if (FAILED(hrc)) throw hrc;
10113
10114 /* NVRAM settings (required) */
10115 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10116 if (FAILED(hrc)) throw hrc;
10117
10118 /* GraphicsAdapter settings (required) */
10119 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10120 if (FAILED(hrc)) throw hrc;
10121
10122 /* USB Controller (required) */
10123 data.usbSettings.llUSBControllers.clear();
10124 for (USBControllerList::const_iterator
10125 it = mUSBControllers->begin();
10126 it != mUSBControllers->end();
10127 ++it)
10128 {
10129 ComObjPtr<USBController> ctrl = *it;
10130 settings::USBController settingsCtrl;
10131
10132 settingsCtrl.strName = ctrl->i_getName();
10133 settingsCtrl.enmType = ctrl->i_getControllerType();
10134
10135 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10136 }
10137
10138 /* USB device filters (required) */
10139 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10140 if (FAILED(hrc)) throw hrc;
10141
10142 /* Network adapters (required) */
10143 size_t const uMaxNICs =
10144 RT_MIN(PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType), mNetworkAdapters.size());
10145 data.llNetworkAdapters.clear();
10146 /* Write out only the nominal number of network adapters for this
10147 * chipset type. Since Machine::commit() hasn't been called there
10148 * may be extra NIC settings in the vector. */
10149 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10150 {
10151 settings::NetworkAdapter nic;
10152 nic.ulSlot = (uint32_t)slot;
10153 /* paranoia check... must not be NULL, but must not crash either. */
10154 if (mNetworkAdapters[slot])
10155 {
10156 if (mNetworkAdapters[slot]->i_hasDefaults())
10157 continue;
10158
10159 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10160 if (FAILED(hrc)) throw hrc;
10161
10162 data.llNetworkAdapters.push_back(nic);
10163 }
10164 }
10165
10166 /* Serial ports */
10167 data.llSerialPorts.clear();
10168 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10169 {
10170 if (mSerialPorts[slot]->i_hasDefaults())
10171 continue;
10172
10173 settings::SerialPort s;
10174 s.ulSlot = slot;
10175 hrc = mSerialPorts[slot]->i_saveSettings(s);
10176 if (FAILED(hrc)) return hrc;
10177
10178 data.llSerialPorts.push_back(s);
10179 }
10180
10181 /* Parallel ports */
10182 data.llParallelPorts.clear();
10183 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10184 {
10185 if (mParallelPorts[slot]->i_hasDefaults())
10186 continue;
10187
10188 settings::ParallelPort p;
10189 p.ulSlot = slot;
10190 hrc = mParallelPorts[slot]->i_saveSettings(p);
10191 if (FAILED(hrc)) return hrc;
10192
10193 data.llParallelPorts.push_back(p);
10194 }
10195
10196 /* Audio settings */
10197 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10198 if (FAILED(hrc)) return hrc;
10199
10200 hrc = i_saveStorageControllers(data.storage);
10201 if (FAILED(hrc)) return hrc;
10202
10203 /* Shared folders */
10204 data.llSharedFolders.clear();
10205 for (HWData::SharedFolderList::const_iterator
10206 it = mHWData->mSharedFolders.begin();
10207 it != mHWData->mSharedFolders.end();
10208 ++it)
10209 {
10210 SharedFolder *pSF = *it;
10211 AutoCaller sfCaller(pSF);
10212 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10213 settings::SharedFolder sf;
10214 sf.strName = pSF->i_getName();
10215 sf.strHostPath = pSF->i_getHostPath();
10216 sf.fWritable = !!pSF->i_isWritable();
10217 sf.fAutoMount = !!pSF->i_isAutoMounted();
10218 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10219 sf.enmSymlinkPolicy = pSF->i_getSymlinkPolicy();
10220
10221 data.llSharedFolders.push_back(sf);
10222 }
10223
10224 // clipboard
10225 data.clipboardMode = mHWData->mClipboardMode;
10226 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10227
10228 // drag'n'drop
10229 data.dndMode = mHWData->mDnDMode;
10230
10231 /* Guest */
10232 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10233
10234 // IO settings
10235 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10236 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10237
10238 /* BandwidthControl (required) */
10239 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10240 if (FAILED(hrc)) throw hrc;
10241
10242 /* Host PCI devices */
10243 data.pciAttachments.clear();
10244 for (HWData::PCIDeviceAssignmentList::const_iterator
10245 it = mHWData->mPCIDeviceAssignments.begin();
10246 it != mHWData->mPCIDeviceAssignments.end();
10247 ++it)
10248 {
10249 ComObjPtr<PCIDeviceAttachment> pda = *it;
10250 settings::HostPCIDeviceAttachment hpda;
10251
10252 hrc = pda->i_saveSettings(hpda);
10253 if (FAILED(hrc)) throw hrc;
10254
10255 data.pciAttachments.push_back(hpda);
10256 }
10257
10258 // guest properties
10259 data.llGuestProperties.clear();
10260#ifdef VBOX_WITH_GUEST_PROPS
10261 for (HWData::GuestPropertyMap::const_iterator
10262 it = mHWData->mGuestProperties.begin();
10263 it != mHWData->mGuestProperties.end();
10264 ++it)
10265 {
10266 HWData::GuestProperty property = it->second;
10267
10268 /* Remove transient guest properties at shutdown unless we
10269 * are saving state. Note that restoring snapshot intentionally
10270 * keeps them, they will be removed if appropriate once the final
10271 * machine state is set (as crashes etc. need to work). */
10272 if ( ( mData->mMachineState == MachineState_PoweredOff
10273 || mData->mMachineState == MachineState_Aborted
10274 || mData->mMachineState == MachineState_Teleported)
10275 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10276 continue;
10277 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10278 prop.strName = it->first;
10279 prop.strValue = property.strValue;
10280 prop.timestamp = (uint64_t)property.mTimestamp;
10281 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10282 GuestPropWriteFlags(property.mFlags, szFlags);
10283 prop.strFlags = szFlags;
10284
10285 data.llGuestProperties.push_back(prop);
10286 }
10287
10288 /* I presume this doesn't require a backup(). */
10289 mData->mGuestPropertiesModified = FALSE;
10290#endif /* VBOX_WITH_GUEST_PROPS defined */
10291
10292 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10293 if (FAILED(hrc)) throw hrc;
10294
10295 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10296 *pAutostart = mHWData->mAutostart;
10297
10298 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10299 }
10300 catch (std::bad_alloc &)
10301 {
10302 return E_OUTOFMEMORY;
10303 }
10304
10305 AssertComRC(hrc);
10306 return hrc;
10307}
10308
10309/**
10310 * Saves the storage controller configuration.
10311 *
10312 * @param data storage settings.
10313 */
10314HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10315{
10316 data.llStorageControllers.clear();
10317
10318 for (StorageControllerList::const_iterator
10319 it = mStorageControllers->begin();
10320 it != mStorageControllers->end();
10321 ++it)
10322 {
10323 ComObjPtr<StorageController> pCtl = *it;
10324
10325 settings::StorageController ctl;
10326 ctl.strName = pCtl->i_getName();
10327 ctl.controllerType = pCtl->i_getControllerType();
10328 ctl.storageBus = pCtl->i_getStorageBus();
10329 ctl.ulInstance = pCtl->i_getInstance();
10330 ctl.fBootable = pCtl->i_getBootable();
10331
10332 /* Save the port count. */
10333 ULONG portCount;
10334 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10335 ComAssertComRCRet(hrc, hrc);
10336 ctl.ulPortCount = portCount;
10337
10338 /* Save fUseHostIOCache */
10339 BOOL fUseHostIOCache;
10340 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10341 ComAssertComRCRet(hrc, hrc);
10342 ctl.fUseHostIOCache = !!fUseHostIOCache;
10343
10344 /* save the devices now. */
10345 hrc = i_saveStorageDevices(pCtl, ctl);
10346 ComAssertComRCRet(hrc, hrc);
10347
10348 data.llStorageControllers.push_back(ctl);
10349 }
10350
10351 return S_OK;
10352}
10353
10354/**
10355 * Saves the hard disk configuration.
10356 */
10357HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10358 settings::StorageController &data)
10359{
10360 MediumAttachmentList atts;
10361
10362 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10363 if (FAILED(hrc)) return hrc;
10364
10365 data.llAttachedDevices.clear();
10366 for (MediumAttachmentList::const_iterator
10367 it = atts.begin();
10368 it != atts.end();
10369 ++it)
10370 {
10371 settings::AttachedDevice dev;
10372 IMediumAttachment *iA = *it;
10373 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10374 Medium *pMedium = pAttach->i_getMedium();
10375
10376 dev.deviceType = pAttach->i_getType();
10377 dev.lPort = pAttach->i_getPort();
10378 dev.lDevice = pAttach->i_getDevice();
10379 dev.fPassThrough = pAttach->i_getPassthrough();
10380 dev.fHotPluggable = pAttach->i_getHotPluggable();
10381 if (pMedium)
10382 {
10383 if (pMedium->i_isHostDrive())
10384 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10385 else
10386 dev.uuid = pMedium->i_getId();
10387 dev.fTempEject = pAttach->i_getTempEject();
10388 dev.fNonRotational = pAttach->i_getNonRotational();
10389 dev.fDiscard = pAttach->i_getDiscard();
10390 }
10391
10392 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10393
10394 data.llAttachedDevices.push_back(dev);
10395 }
10396
10397 return S_OK;
10398}
10399
10400/**
10401 * Saves machine state settings as defined by aFlags
10402 * (SaveSTS_* values).
10403 *
10404 * @param aFlags Combination of SaveSTS_* flags.
10405 *
10406 * @note Locks objects for writing.
10407 */
10408HRESULT Machine::i_saveStateSettings(int aFlags)
10409{
10410 if (aFlags == 0)
10411 return S_OK;
10412
10413 AutoCaller autoCaller(this);
10414 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10415
10416 /* This object's write lock is also necessary to serialize file access
10417 * (prevent concurrent reads and writes) */
10418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10419
10420 HRESULT hrc = S_OK;
10421
10422 Assert(mData->pMachineConfigFile);
10423
10424 try
10425 {
10426 if (aFlags & SaveSTS_CurStateModified)
10427 mData->pMachineConfigFile->fCurrentStateModified = true;
10428
10429 if (aFlags & SaveSTS_StateFilePath)
10430 {
10431 if (!mSSData->strStateFilePath.isEmpty())
10432 /* try to make the file name relative to the settings file dir */
10433 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10434 else
10435 mData->pMachineConfigFile->strStateFile.setNull();
10436 }
10437
10438 if (aFlags & SaveSTS_StateTimeStamp)
10439 {
10440 Assert( mData->mMachineState != MachineState_Aborted
10441 || mSSData->strStateFilePath.isEmpty());
10442
10443 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10444
10445 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10446 || mData->mMachineState == MachineState_AbortedSaved);
10447/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10448 }
10449
10450 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10451 }
10452 catch (...)
10453 {
10454 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10455 }
10456
10457 return hrc;
10458}
10459
10460/**
10461 * Ensures that the given medium is added to a media registry. If this machine
10462 * was created with 4.0 or later, then the machine registry is used. Otherwise
10463 * the global VirtualBox media registry is used.
10464 *
10465 * Caller must NOT hold machine lock, media tree or any medium locks!
10466 *
10467 * @param pMedium
10468 */
10469void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10470{
10471 /* Paranoia checks: do not hold machine or media tree locks. */
10472 AssertReturnVoid(!isWriteLockOnCurrentThread());
10473 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10474
10475 ComObjPtr<Medium> pBase;
10476 {
10477 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10478 pBase = pMedium->i_getBase();
10479 }
10480
10481 /* Paranoia checks: do not hold medium locks. */
10482 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10483 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10484
10485 // decide which medium registry to use now that the medium is attached:
10486 Guid uuid;
10487 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10488 if (fCanHaveOwnMediaRegistry)
10489 // machine XML is VirtualBox 4.0 or higher:
10490 uuid = i_getId(); // machine UUID
10491 else
10492 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10493
10494 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10495 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10496 if (pMedium->i_addRegistry(uuid))
10497 mParent->i_markRegistryModified(uuid);
10498
10499 /* For more complex hard disk structures it can happen that the base
10500 * medium isn't yet associated with any medium registry. Do that now. */
10501 if (pMedium != pBase)
10502 {
10503 /* Tree lock needed by Medium::addRegistryAll. */
10504 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10505 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10506 {
10507 treeLock.release();
10508 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10509 treeLock.acquire();
10510 }
10511 if (pBase->i_addRegistryAll(uuid))
10512 {
10513 treeLock.release();
10514 mParent->i_markRegistryModified(uuid);
10515 }
10516 }
10517}
10518
10519/**
10520 * Physically deletes a file belonging to a machine.
10521 *
10522 * @returns HRESULT
10523 * @retval VBOX_E_FILE_ERROR on failure.
10524 * @param strFile File to delete.
10525 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
10526 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
10527 * @param strWhat File hint which will be used when setting an error. Optional.
10528 * @param prc Where to return IPRT's status code on failure.
10529 * Optional and can be NULL.
10530 */
10531HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
10532 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
10533{
10534 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
10535
10536 HRESULT hrc = S_OK;
10537
10538 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
10539
10540 int vrc = RTFileDelete(strFile.c_str());
10541 if (RT_FAILURE(vrc))
10542 {
10543 if ( !fIgnoreFailures
10544 /* Don't (externally) bitch about stuff which doesn't exist. */
10545 && ( vrc != VERR_FILE_NOT_FOUND
10546 && vrc != VERR_PATH_NOT_FOUND
10547 )
10548 )
10549 {
10550 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
10551
10552 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
10553 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
10554 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
10555 }
10556 }
10557
10558 if (prc)
10559 *prc = vrc;
10560 return hrc;
10561}
10562
10563/**
10564 * Creates differencing hard disks for all normal hard disks attached to this
10565 * machine and a new set of attachments to refer to created disks.
10566 *
10567 * Used when taking a snapshot or when deleting the current state. Gets called
10568 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10569 *
10570 * This method assumes that mMediumAttachments contains the original hard disk
10571 * attachments it needs to create diffs for. On success, these attachments will
10572 * be replaced with the created diffs.
10573 *
10574 * Attachments with non-normal hard disks are left as is.
10575 *
10576 * If @a aOnline is @c false then the original hard disks that require implicit
10577 * diffs will be locked for reading. Otherwise it is assumed that they are
10578 * already locked for writing (when the VM was started). Note that in the latter
10579 * case it is responsibility of the caller to lock the newly created diffs for
10580 * writing if this method succeeds.
10581 *
10582 * @param aProgress Progress object to run (must contain at least as
10583 * many operations left as the number of hard disks
10584 * attached).
10585 * @param aWeight Weight of this operation.
10586 * @param aOnline Whether the VM was online prior to this operation.
10587 *
10588 * @note The progress object is not marked as completed, neither on success nor
10589 * on failure. This is a responsibility of the caller.
10590 *
10591 * @note Locks this object and the media tree for writing.
10592 */
10593HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10594 ULONG aWeight,
10595 bool aOnline)
10596{
10597 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10598
10599 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10600 AssertReturn(!!pProgressControl, E_INVALIDARG);
10601
10602 AutoCaller autoCaller(this);
10603 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10604
10605 /* We can't use AutoMultiWriteLock2 here as some error code paths acquire
10606 * the media tree lock which means we need to be able to drop the media
10607 * tree lock individually in those cases. */
10608 AutoWriteLock aMachineLock(this->lockHandle() COMMA_LOCKVAL_SRC_POS);
10609 AutoWriteLock aTreeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10610
10611 /* must be in a protective state because we release the lock below */
10612 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10613 || mData->mMachineState == MachineState_OnlineSnapshotting
10614 || mData->mMachineState == MachineState_LiveSnapshotting
10615 || mData->mMachineState == MachineState_RestoringSnapshot
10616 || mData->mMachineState == MachineState_DeletingSnapshot
10617 , E_FAIL);
10618
10619 HRESULT hrc = S_OK;
10620
10621 // use appropriate locked media map (online or offline)
10622 MediumLockListMap lockedMediaOffline;
10623 MediumLockListMap *lockedMediaMap;
10624 if (aOnline)
10625 lockedMediaMap = &mData->mSession.mLockedMedia;
10626 else
10627 lockedMediaMap = &lockedMediaOffline;
10628
10629 try
10630 {
10631 if (!aOnline)
10632 {
10633 /* lock all attached hard disks early to detect "in use"
10634 * situations before creating actual diffs */
10635 for (MediumAttachmentList::const_iterator
10636 it = mMediumAttachments->begin();
10637 it != mMediumAttachments->end();
10638 ++it)
10639 {
10640 MediumAttachment *pAtt = *it;
10641 if (pAtt->i_getType() == DeviceType_HardDisk)
10642 {
10643 Medium *pMedium = pAtt->i_getMedium();
10644 Assert(pMedium);
10645
10646 MediumLockList *pMediumLockList(new MediumLockList());
10647 aTreeLock.release();
10648 aMachineLock.release();
10649 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10650 NULL /* pToLockWrite */,
10651 false /* fMediumLockWriteAll */,
10652 NULL,
10653 *pMediumLockList);
10654 aMachineLock.acquire();
10655 aTreeLock.acquire();
10656 if (FAILED(hrc))
10657 {
10658 delete pMediumLockList;
10659 throw hrc;
10660 }
10661 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10662 if (FAILED(hrc))
10663 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
10664 }
10665 }
10666
10667 /* Now lock all media. If this fails, nothing is locked. */
10668 aTreeLock.release();
10669 aMachineLock.release();
10670 hrc = lockedMediaMap->Lock();
10671 aMachineLock.acquire();
10672 aTreeLock.acquire();
10673 if (FAILED(hrc))
10674 throw setError(hrc, tr("Locking of attached media failed"));
10675 }
10676
10677 /* remember the current list (note that we don't use backup() since
10678 * mMediumAttachments may be already backed up) */
10679 MediumAttachmentList atts = *mMediumAttachments.data();
10680
10681 /* start from scratch */
10682 mMediumAttachments->clear();
10683
10684 /* go through remembered attachments and create diffs for normal hard
10685 * disks and attach them */
10686 for (MediumAttachmentList::const_iterator
10687 it = atts.begin();
10688 it != atts.end();
10689 ++it)
10690 {
10691 MediumAttachment *pAtt = *it;
10692
10693 DeviceType_T devType = pAtt->i_getType();
10694 Medium *pMedium = pAtt->i_getMedium();
10695
10696 if ( devType != DeviceType_HardDisk
10697 || pMedium == NULL
10698 || pMedium->i_getType() != MediumType_Normal)
10699 {
10700 /* copy the attachment as is */
10701
10702 /** @todo the progress object created in SessionMachine::TakeSnaphot
10703 * only expects operations for hard disks. Later other
10704 * device types need to show up in the progress as well. */
10705 if (devType == DeviceType_HardDisk)
10706 {
10707 if (pMedium == NULL)
10708 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10709 aWeight); // weight
10710 else
10711 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10712 pMedium->i_getBase()->i_getName().c_str()).raw(),
10713 aWeight); // weight
10714 }
10715
10716 mMediumAttachments->push_back(pAtt);
10717 continue;
10718 }
10719
10720 /* need a diff */
10721 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10722 pMedium->i_getBase()->i_getName().c_str()).raw(),
10723 aWeight); // weight
10724
10725 Utf8Str strFullSnapshotFolder;
10726 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10727
10728 ComObjPtr<Medium> diff;
10729 diff.createObject();
10730 // store the diff in the same registry as the parent
10731 // (this cannot fail here because we can't create implicit diffs for
10732 // unregistered images)
10733 Guid uuidRegistryParent;
10734 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10735 Assert(fInRegistry); NOREF(fInRegistry);
10736 hrc = diff->init(mParent,
10737 pMedium->i_getPreferredDiffFormat(),
10738 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10739 uuidRegistryParent,
10740 DeviceType_HardDisk);
10741 if (FAILED(hrc))
10742 {
10743 /* Throwing an exception here causes the 'diff' object to go out of scope
10744 * which triggers its destructor (ComObjPtr<Medium>::~ComObjPtr) which will
10745 * ultimately call Medium::uninit() which acquires the media tree lock
10746 * (VirtualBox::i_getMediaTreeLockHandle()) so drop the media tree lock here. */
10747 aTreeLock.release();
10748 throw hrc;
10749 }
10750
10751 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10752 * the push_back? Looks like we're going to release medium with the
10753 * wrong kind of lock (general issue with if we fail anywhere at all)
10754 * and an orphaned VDI in the snapshots folder. */
10755
10756 /* update the appropriate lock list */
10757 MediumLockList *pMediumLockList;
10758 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
10759 AssertComRCThrowRC(hrc);
10760 if (aOnline)
10761 {
10762 aTreeLock.release();
10763 aMachineLock.release();
10764 /* The currently attached medium will be read-only, change
10765 * the lock type to read. */
10766 hrc = pMediumLockList->Update(pMedium, false);
10767 aMachineLock.acquire();
10768 aTreeLock.acquire();
10769 AssertComRCThrowRC(hrc);
10770 }
10771
10772 /* release the locks before the potentially lengthy operation */
10773 aTreeLock.release();
10774 aMachineLock.release();
10775 hrc = pMedium->i_createDiffStorage(diff,
10776 pMedium->i_getPreferredDiffVariant(),
10777 pMediumLockList,
10778 NULL /* aProgress */,
10779 true /* aWait */,
10780 false /* aNotify */);
10781 aMachineLock.acquire();
10782 aTreeLock.acquire();
10783 if (FAILED(hrc))
10784 {
10785 /* As above, 'diff' will go out of scope via the 'throw' here resulting in
10786 * Medium::uninit() being called which acquires the media tree lock. */
10787 aTreeLock.release();
10788 throw hrc;
10789 }
10790
10791 /* actual lock list update is done in Machine::i_commitMedia */
10792
10793 hrc = diff->i_addBackReference(mData->mUuid);
10794 AssertComRCThrowRC(hrc);
10795
10796 /* add a new attachment */
10797 ComObjPtr<MediumAttachment> attachment;
10798 attachment.createObject();
10799 hrc = attachment->init(this,
10800 diff,
10801 pAtt->i_getControllerName(),
10802 pAtt->i_getPort(),
10803 pAtt->i_getDevice(),
10804 DeviceType_HardDisk,
10805 true /* aImplicit */,
10806 false /* aPassthrough */,
10807 false /* aTempEject */,
10808 pAtt->i_getNonRotational(),
10809 pAtt->i_getDiscard(),
10810 pAtt->i_getHotPluggable(),
10811 pAtt->i_getBandwidthGroup());
10812 if (FAILED(hrc)) {
10813 /* As above, 'diff' will go out of scope via the 'throw' here resulting in
10814 * Medium::uninit() being called which acquires the media tree lock. */
10815 aTreeLock.release();
10816 throw hrc;
10817 }
10818
10819 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10820 AssertComRCThrowRC(hrc);
10821 mMediumAttachments->push_back(attachment);
10822 }
10823 }
10824 catch (HRESULT hrcXcpt)
10825 {
10826 hrc = hrcXcpt;
10827 }
10828
10829 /* unlock all hard disks we locked when there is no VM */
10830 if (!aOnline)
10831 {
10832 ErrorInfoKeeper eik;
10833
10834 HRESULT hrc2 = lockedMediaMap->Clear();
10835 AssertComRC(hrc2);
10836 }
10837
10838 return hrc;
10839}
10840
10841/**
10842 * Deletes implicit differencing hard disks created either by
10843 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10844 * mMediumAttachments.
10845 *
10846 * Note that to delete hard disks created by #attachDevice() this method is
10847 * called from #i_rollbackMedia() when the changes are rolled back.
10848 *
10849 * @note Locks this object and the media tree for writing.
10850 */
10851HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10852{
10853 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10854
10855 AutoCaller autoCaller(this);
10856 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10857
10858 AutoMultiWriteLock2 alock(this->lockHandle(),
10859 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10860
10861 /* We absolutely must have backed up state. */
10862 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10863
10864 /* Check if there are any implicitly created diff images. */
10865 bool fImplicitDiffs = false;
10866 for (MediumAttachmentList::const_iterator
10867 it = mMediumAttachments->begin();
10868 it != mMediumAttachments->end();
10869 ++it)
10870 {
10871 const ComObjPtr<MediumAttachment> &pAtt = *it;
10872 if (pAtt->i_isImplicit())
10873 {
10874 fImplicitDiffs = true;
10875 break;
10876 }
10877 }
10878 /* If there is nothing to do, leave early. This saves lots of image locking
10879 * effort. It also avoids a MachineStateChanged event without real reason.
10880 * This is important e.g. when loading a VM config, because there should be
10881 * no events. Otherwise API clients can become thoroughly confused for
10882 * inaccessible VMs (the code for loading VM configs uses this method for
10883 * cleanup if the config makes no sense), as they take such events as an
10884 * indication that the VM is alive, and they would force the VM config to
10885 * be reread, leading to an endless loop. */
10886 if (!fImplicitDiffs)
10887 return S_OK;
10888
10889 HRESULT hrc = S_OK;
10890 MachineState_T oldState = mData->mMachineState;
10891
10892 /* will release the lock before the potentially lengthy operation,
10893 * so protect with the special state (unless already protected) */
10894 if ( oldState != MachineState_Snapshotting
10895 && oldState != MachineState_OnlineSnapshotting
10896 && oldState != MachineState_LiveSnapshotting
10897 && oldState != MachineState_RestoringSnapshot
10898 && oldState != MachineState_DeletingSnapshot
10899 && oldState != MachineState_DeletingSnapshotOnline
10900 && oldState != MachineState_DeletingSnapshotPaused
10901 )
10902 i_setMachineState(MachineState_SettingUp);
10903
10904 // use appropriate locked media map (online or offline)
10905 MediumLockListMap lockedMediaOffline;
10906 MediumLockListMap *lockedMediaMap;
10907 if (aOnline)
10908 lockedMediaMap = &mData->mSession.mLockedMedia;
10909 else
10910 lockedMediaMap = &lockedMediaOffline;
10911
10912 try
10913 {
10914 if (!aOnline)
10915 {
10916 /* lock all attached hard disks early to detect "in use"
10917 * situations before deleting actual diffs */
10918 for (MediumAttachmentList::const_iterator
10919 it = mMediumAttachments->begin();
10920 it != mMediumAttachments->end();
10921 ++it)
10922 {
10923 MediumAttachment *pAtt = *it;
10924 if (pAtt->i_getType() == DeviceType_HardDisk)
10925 {
10926 Medium *pMedium = pAtt->i_getMedium();
10927 Assert(pMedium);
10928
10929 MediumLockList *pMediumLockList(new MediumLockList());
10930 alock.release();
10931 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10932 NULL /* pToLockWrite */,
10933 false /* fMediumLockWriteAll */,
10934 NULL,
10935 *pMediumLockList);
10936 alock.acquire();
10937
10938 if (FAILED(hrc))
10939 {
10940 delete pMediumLockList;
10941 throw hrc;
10942 }
10943
10944 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10945 if (FAILED(hrc))
10946 throw hrc;
10947 }
10948 }
10949
10950 if (FAILED(hrc))
10951 throw hrc;
10952 } // end of offline
10953
10954 /* Lock lists are now up to date and include implicitly created media */
10955
10956 /* Go through remembered attachments and delete all implicitly created
10957 * diffs and fix up the attachment information */
10958 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10959 MediumAttachmentList implicitAtts;
10960 for (MediumAttachmentList::const_iterator
10961 it = mMediumAttachments->begin();
10962 it != mMediumAttachments->end();
10963 ++it)
10964 {
10965 ComObjPtr<MediumAttachment> pAtt = *it;
10966 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10967 if (pMedium.isNull())
10968 continue;
10969
10970 // Implicit attachments go on the list for deletion and back references are removed.
10971 if (pAtt->i_isImplicit())
10972 {
10973 /* Deassociate and mark for deletion */
10974 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10975 hrc = pMedium->i_removeBackReference(mData->mUuid);
10976 if (FAILED(hrc))
10977 throw hrc;
10978 implicitAtts.push_back(pAtt);
10979 continue;
10980 }
10981
10982 /* Was this medium attached before? */
10983 if (!i_findAttachment(oldAtts, pMedium))
10984 {
10985 /* no: de-associate */
10986 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10987 hrc = pMedium->i_removeBackReference(mData->mUuid);
10988 if (FAILED(hrc))
10989 throw hrc;
10990 continue;
10991 }
10992 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10993 }
10994
10995 /* If there are implicit attachments to delete, throw away the lock
10996 * map contents (which will unlock all media) since the medium
10997 * attachments will be rolled back. Below we need to completely
10998 * recreate the lock map anyway since it is infinitely complex to
10999 * do this incrementally (would need reconstructing each attachment
11000 * change, which would be extremely hairy). */
11001 if (implicitAtts.size() != 0)
11002 {
11003 ErrorInfoKeeper eik;
11004
11005 HRESULT hrc2 = lockedMediaMap->Clear();
11006 AssertComRC(hrc2);
11007 }
11008
11009 /* rollback hard disk changes */
11010 mMediumAttachments.rollback();
11011
11012 MultiResult mrc(S_OK);
11013
11014 // Delete unused implicit diffs.
11015 if (implicitAtts.size() != 0)
11016 {
11017 alock.release();
11018
11019 for (MediumAttachmentList::const_iterator
11020 it = implicitAtts.begin();
11021 it != implicitAtts.end();
11022 ++it)
11023 {
11024 // Remove medium associated with this attachment.
11025 ComObjPtr<MediumAttachment> pAtt = *it;
11026 Assert(pAtt);
11027 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11028 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11029 Assert(pMedium);
11030
11031 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11032 // continue on delete failure, just collect error messages
11033 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
11034 pMedium->i_getLocationFull().c_str() ));
11035 mrc = hrc;
11036 }
11037 // Clear the list of deleted implicit attachments now, while not
11038 // holding the lock, as it will ultimately trigger Medium::uninit()
11039 // calls which assume that the media tree lock isn't held.
11040 implicitAtts.clear();
11041
11042 alock.acquire();
11043
11044 /* if there is a VM recreate media lock map as mentioned above,
11045 * otherwise it is a waste of time and we leave things unlocked */
11046 if (aOnline)
11047 {
11048 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11049 /* must never be NULL, but better safe than sorry */
11050 if (!pMachine.isNull())
11051 {
11052 alock.release();
11053 hrc = mData->mSession.mMachine->i_lockMedia();
11054 alock.acquire();
11055 if (FAILED(hrc))
11056 throw hrc;
11057 }
11058 }
11059 }
11060 }
11061 catch (HRESULT hrcXcpt)
11062 {
11063 hrc = hrcXcpt;
11064 }
11065
11066 if (mData->mMachineState == MachineState_SettingUp)
11067 i_setMachineState(oldState);
11068
11069 /* unlock all hard disks we locked when there is no VM */
11070 if (!aOnline)
11071 {
11072 ErrorInfoKeeper eik;
11073
11074 HRESULT hrc2 = lockedMediaMap->Clear();
11075 AssertComRC(hrc2);
11076 }
11077
11078 return hrc;
11079}
11080
11081
11082/**
11083 * Looks through the given list of media attachments for one with the given parameters
11084 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11085 * can be searched as well if needed.
11086 *
11087 * @param ll
11088 * @param aControllerName
11089 * @param aControllerPort
11090 * @param aDevice
11091 * @return
11092 */
11093MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11094 const Utf8Str &aControllerName,
11095 LONG aControllerPort,
11096 LONG aDevice)
11097{
11098 for (MediumAttachmentList::const_iterator
11099 it = ll.begin();
11100 it != ll.end();
11101 ++it)
11102 {
11103 MediumAttachment *pAttach = *it;
11104 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11105 return pAttach;
11106 }
11107
11108 return NULL;
11109}
11110
11111/**
11112 * Looks through the given list of media attachments for one with the given parameters
11113 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11114 * can be searched as well if needed.
11115 *
11116 * @param ll
11117 * @param pMedium
11118 * @return
11119 */
11120MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11121 ComObjPtr<Medium> pMedium)
11122{
11123 for (MediumAttachmentList::const_iterator
11124 it = ll.begin();
11125 it != ll.end();
11126 ++it)
11127 {
11128 MediumAttachment *pAttach = *it;
11129 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11130 if (pMediumThis == pMedium)
11131 return pAttach;
11132 }
11133
11134 return NULL;
11135}
11136
11137/**
11138 * Looks through the given list of media attachments for one with the given parameters
11139 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11140 * can be searched as well if needed.
11141 *
11142 * @param ll
11143 * @param id
11144 * @return
11145 */
11146MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11147 Guid &id)
11148{
11149 for (MediumAttachmentList::const_iterator
11150 it = ll.begin();
11151 it != ll.end();
11152 ++it)
11153 {
11154 MediumAttachment *pAttach = *it;
11155 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11156 if (pMediumThis->i_getId() == id)
11157 return pAttach;
11158 }
11159
11160 return NULL;
11161}
11162
11163/**
11164 * Main implementation for Machine::DetachDevice. This also gets called
11165 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11166 *
11167 * @param pAttach Medium attachment to detach.
11168 * @param writeLock Machine write lock which the caller must have locked once.
11169 * This may be released temporarily in here.
11170 * @param pSnapshot If NULL, then the detachment is for the current machine.
11171 * Otherwise this is for a SnapshotMachine, and this must be
11172 * its snapshot.
11173 * @return
11174 */
11175HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11176 AutoWriteLock &writeLock,
11177 Snapshot *pSnapshot)
11178{
11179 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11180 DeviceType_T mediumType = pAttach->i_getType();
11181
11182 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11183
11184 if (pAttach->i_isImplicit())
11185 {
11186 /* attempt to implicitly delete the implicitly created diff */
11187
11188 /// @todo move the implicit flag from MediumAttachment to Medium
11189 /// and forbid any hard disk operation when it is implicit. Or maybe
11190 /// a special media state for it to make it even more simple.
11191
11192 Assert(mMediumAttachments.isBackedUp());
11193
11194 /* will release the lock before the potentially lengthy operation, so
11195 * protect with the special state */
11196 MachineState_T oldState = mData->mMachineState;
11197 i_setMachineState(MachineState_SettingUp);
11198
11199 writeLock.release();
11200
11201 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11202
11203 writeLock.acquire();
11204
11205 i_setMachineState(oldState);
11206
11207 if (FAILED(hrc)) return hrc;
11208 }
11209
11210 i_setModified(IsModified_Storage);
11211 mMediumAttachments.backup();
11212 mMediumAttachments->remove(pAttach);
11213
11214 if (!oldmedium.isNull())
11215 {
11216 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11217 if (pSnapshot)
11218 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11219 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11220 else if (mediumType != DeviceType_HardDisk)
11221 oldmedium->i_removeBackReference(mData->mUuid);
11222 }
11223
11224 return S_OK;
11225}
11226
11227/**
11228 * Goes thru all media of the given list and
11229 *
11230 * 1) calls i_detachDevice() on each of them for this machine and
11231 * 2) adds all Medium objects found in the process to the given list,
11232 * depending on cleanupMode.
11233 *
11234 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11235 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11236 * media to the list.
11237 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11238 * also removable media if they are located in the VM folder and referenced
11239 * only by this VM (media prepared by unattended installer).
11240 *
11241 * This gets called from Machine::Unregister, both for the actual Machine and
11242 * the SnapshotMachine objects that might be found in the snapshots.
11243 *
11244 * Requires caller and locking. The machine lock must be passed in because it
11245 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11246 *
11247 * @param writeLock Machine lock from top-level caller; this gets passed to
11248 * i_detachDevice.
11249 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11250 * object if called for a SnapshotMachine.
11251 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11252 * added to llMedia; if Full, then all media get added;
11253 * otherwise no media get added.
11254 * @param llMedia Caller's list to receive Medium objects which got detached so
11255 * caller can close() them, depending on cleanupMode.
11256 * @return
11257 */
11258HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11259 Snapshot *pSnapshot,
11260 CleanupMode_T cleanupMode,
11261 MediaList &llMedia)
11262{
11263 Assert(isWriteLockOnCurrentThread());
11264
11265 HRESULT hrc;
11266
11267 // make a temporary list because i_detachDevice invalidates iterators into
11268 // mMediumAttachments
11269 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11270
11271 for (MediumAttachmentList::iterator
11272 it = llAttachments2.begin();
11273 it != llAttachments2.end();
11274 ++it)
11275 {
11276 ComObjPtr<MediumAttachment> &pAttach = *it;
11277 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11278
11279 if (!pMedium.isNull())
11280 {
11281 AutoCaller mac(pMedium);
11282 if (FAILED(mac.hrc())) return mac.hrc();
11283 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11284 DeviceType_T devType = pMedium->i_getDeviceType();
11285 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11286 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11287 strMediumLocation.stripFilename();
11288 Utf8Str strMachineFolder = i_getSettingsFileFull();
11289 strMachineFolder.stripFilename();
11290 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11291 && devType == DeviceType_HardDisk)
11292 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11293 && ( devType == DeviceType_HardDisk
11294 || ( cBackRefs <= 1
11295 && strMediumLocation == strMachineFolder
11296 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11297 || (cleanupMode == CleanupMode_Full)
11298 )
11299 {
11300 llMedia.push_back(pMedium);
11301 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11302 /* Not allowed to keep this lock as below we need the parent
11303 * medium lock, and the lock order is parent to child. */
11304 lock.release();
11305 /*
11306 * Search for media which are not attached to any machine, but
11307 * in the chain to an attached disk. Media are only consided
11308 * if they are:
11309 * - have only one child
11310 * - no references to any machines
11311 * - are of normal medium type
11312 */
11313 while (!pParent.isNull())
11314 {
11315 AutoCaller mac1(pParent);
11316 if (FAILED(mac1.hrc())) return mac1.hrc();
11317 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11318 if (pParent->i_getChildren().size() == 1)
11319 {
11320 if ( pParent->i_getMachineBackRefCount() == 0
11321 && pParent->i_getType() == MediumType_Normal
11322 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11323 llMedia.push_back(pParent);
11324 }
11325 else
11326 break;
11327 pParent = pParent->i_getParent();
11328 }
11329 }
11330 }
11331
11332 // real machine: then we need to use the proper method
11333 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11334
11335 if (FAILED(hrc))
11336 return hrc;
11337 }
11338
11339 return S_OK;
11340}
11341
11342/**
11343 * Perform deferred hard disk detachments.
11344 *
11345 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11346 * changed (not backed up).
11347 *
11348 * If @a aOnline is @c true then this method will also unlock the old hard
11349 * disks for which the new implicit diffs were created and will lock these new
11350 * diffs for writing.
11351 *
11352 * @param aOnline Whether the VM was online prior to this operation.
11353 *
11354 * @note Locks this object for writing!
11355 */
11356void Machine::i_commitMedia(bool aOnline /*= false*/)
11357{
11358 AutoCaller autoCaller(this);
11359 AssertComRCReturnVoid(autoCaller.hrc());
11360
11361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11362
11363 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11364
11365 HRESULT hrc = S_OK;
11366
11367 /* no attach/detach operations -- nothing to do */
11368 if (!mMediumAttachments.isBackedUp())
11369 return;
11370
11371 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11372 bool fMediaNeedsLocking = false;
11373
11374 /* enumerate new attachments */
11375 for (MediumAttachmentList::const_iterator
11376 it = mMediumAttachments->begin();
11377 it != mMediumAttachments->end();
11378 ++it)
11379 {
11380 MediumAttachment *pAttach = *it;
11381
11382 pAttach->i_commit();
11383
11384 Medium *pMedium = pAttach->i_getMedium();
11385 bool fImplicit = pAttach->i_isImplicit();
11386
11387 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11388 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11389 fImplicit));
11390
11391 /** @todo convert all this Machine-based voodoo to MediumAttachment
11392 * based commit logic. */
11393 if (fImplicit)
11394 {
11395 /* convert implicit attachment to normal */
11396 pAttach->i_setImplicit(false);
11397
11398 if ( aOnline
11399 && pMedium
11400 && pAttach->i_getType() == DeviceType_HardDisk
11401 )
11402 {
11403 /* update the appropriate lock list */
11404 MediumLockList *pMediumLockList;
11405 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11406 AssertComRC(hrc);
11407 if (pMediumLockList)
11408 {
11409 /* unlock if there's a need to change the locking */
11410 if (!fMediaNeedsLocking)
11411 {
11412 Assert(mData->mSession.mLockedMedia.IsLocked());
11413 hrc = mData->mSession.mLockedMedia.Unlock();
11414 AssertComRC(hrc);
11415 fMediaNeedsLocking = true;
11416 }
11417 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
11418 AssertComRC(hrc);
11419 hrc = pMediumLockList->Append(pMedium, true);
11420 AssertComRC(hrc);
11421 }
11422 }
11423
11424 continue;
11425 }
11426
11427 if (pMedium)
11428 {
11429 /* was this medium attached before? */
11430 for (MediumAttachmentList::iterator
11431 oldIt = oldAtts.begin();
11432 oldIt != oldAtts.end();
11433 ++oldIt)
11434 {
11435 MediumAttachment *pOldAttach = *oldIt;
11436 if (pOldAttach->i_getMedium() == pMedium)
11437 {
11438 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11439
11440 /* yes: remove from old to avoid de-association */
11441 oldAtts.erase(oldIt);
11442 break;
11443 }
11444 }
11445 }
11446 }
11447
11448 /* enumerate remaining old attachments and de-associate from the
11449 * current machine state */
11450 for (MediumAttachmentList::const_iterator
11451 it = oldAtts.begin();
11452 it != oldAtts.end();
11453 ++it)
11454 {
11455 MediumAttachment *pAttach = *it;
11456 Medium *pMedium = pAttach->i_getMedium();
11457
11458 /* Detach only hard disks, since DVD/floppy media is detached
11459 * instantly in MountMedium. */
11460 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11461 {
11462 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11463
11464 /* now de-associate from the current machine state */
11465 hrc = pMedium->i_removeBackReference(mData->mUuid);
11466 AssertComRC(hrc);
11467
11468 if (aOnline)
11469 {
11470 /* unlock since medium is not used anymore */
11471 MediumLockList *pMediumLockList;
11472 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11473 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
11474 {
11475 /* this happens for online snapshots, there the attachment
11476 * is changing, but only to a diff image created under
11477 * the old one, so there is no separate lock list */
11478 Assert(!pMediumLockList);
11479 }
11480 else
11481 {
11482 AssertComRC(hrc);
11483 if (pMediumLockList)
11484 {
11485 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
11486 AssertComRC(hrc);
11487 }
11488 }
11489 }
11490 }
11491 }
11492
11493 /* take media locks again so that the locking state is consistent */
11494 if (fMediaNeedsLocking)
11495 {
11496 Assert(aOnline);
11497 hrc = mData->mSession.mLockedMedia.Lock();
11498 AssertComRC(hrc);
11499 }
11500
11501 /* commit the hard disk changes */
11502 mMediumAttachments.commit();
11503
11504 if (i_isSessionMachine())
11505 {
11506 /*
11507 * Update the parent machine to point to the new owner.
11508 * This is necessary because the stored parent will point to the
11509 * session machine otherwise and cause crashes or errors later
11510 * when the session machine gets invalid.
11511 */
11512 /** @todo Change the MediumAttachment class to behave like any other
11513 * class in this regard by creating peer MediumAttachment
11514 * objects for session machines and share the data with the peer
11515 * machine.
11516 */
11517 for (MediumAttachmentList::const_iterator
11518 it = mMediumAttachments->begin();
11519 it != mMediumAttachments->end();
11520 ++it)
11521 (*it)->i_updateParentMachine(mPeer);
11522
11523 /* attach new data to the primary machine and reshare it */
11524 mPeer->mMediumAttachments.attach(mMediumAttachments);
11525 }
11526
11527 return;
11528}
11529
11530/**
11531 * Perform deferred deletion of implicitly created diffs.
11532 *
11533 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11534 * changed (not backed up).
11535 *
11536 * @note Locks this object for writing!
11537 */
11538void Machine::i_rollbackMedia()
11539{
11540 AutoCaller autoCaller(this);
11541 AssertComRCReturnVoid(autoCaller.hrc());
11542
11543 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11544 LogFlowThisFunc(("Entering rollbackMedia\n"));
11545
11546 HRESULT hrc = S_OK;
11547
11548 /* no attach/detach operations -- nothing to do */
11549 if (!mMediumAttachments.isBackedUp())
11550 return;
11551
11552 /* enumerate new attachments */
11553 for (MediumAttachmentList::const_iterator
11554 it = mMediumAttachments->begin();
11555 it != mMediumAttachments->end();
11556 ++it)
11557 {
11558 MediumAttachment *pAttach = *it;
11559 /* Fix up the backrefs for DVD/floppy media. */
11560 if (pAttach->i_getType() != DeviceType_HardDisk)
11561 {
11562 Medium *pMedium = pAttach->i_getMedium();
11563 if (pMedium)
11564 {
11565 hrc = pMedium->i_removeBackReference(mData->mUuid);
11566 AssertComRC(hrc);
11567 }
11568 }
11569
11570 (*it)->i_rollback();
11571
11572 pAttach = *it;
11573 /* Fix up the backrefs for DVD/floppy media. */
11574 if (pAttach->i_getType() != DeviceType_HardDisk)
11575 {
11576 Medium *pMedium = pAttach->i_getMedium();
11577 if (pMedium)
11578 {
11579 hrc = pMedium->i_addBackReference(mData->mUuid);
11580 AssertComRC(hrc);
11581 }
11582 }
11583 }
11584
11585 /** @todo convert all this Machine-based voodoo to MediumAttachment
11586 * based rollback logic. */
11587 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11588
11589 return;
11590}
11591
11592/**
11593 * Returns true if the settings file is located in the directory named exactly
11594 * as the machine; this means, among other things, that the machine directory
11595 * should be auto-renamed.
11596 *
11597 * @param aSettingsDir if not NULL, the full machine settings file directory
11598 * name will be assigned there.
11599 *
11600 * @note Doesn't lock anything.
11601 * @note Not thread safe (must be called from this object's lock).
11602 */
11603bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11604{
11605 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11606 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11607 if (aSettingsDir)
11608 *aSettingsDir = strMachineDirName;
11609 strMachineDirName.stripPath(); // vmname
11610 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11611 strConfigFileOnly.stripPath() // vmname.vbox
11612 .stripSuffix(); // vmname
11613 /** @todo hack, make somehow use of ComposeMachineFilename */
11614 if (mUserData->s.fDirectoryIncludesUUID)
11615 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11616
11617 AssertReturn(!strMachineDirName.isEmpty(), false);
11618 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11619
11620 return strMachineDirName == strConfigFileOnly;
11621}
11622
11623/**
11624 * Discards all changes to machine settings.
11625 *
11626 * @param aNotify Whether to notify the direct session about changes or not.
11627 *
11628 * @note Locks objects for writing!
11629 */
11630void Machine::i_rollback(bool aNotify)
11631{
11632 AutoCaller autoCaller(this);
11633 AssertComRCReturn(autoCaller.hrc(), (void)0);
11634
11635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11636
11637 if (!mStorageControllers.isNull())
11638 {
11639 if (mStorageControllers.isBackedUp())
11640 {
11641 /* unitialize all new devices (absent in the backed up list). */
11642 StorageControllerList *backedList = mStorageControllers.backedUpData();
11643 for (StorageControllerList::const_iterator
11644 it = mStorageControllers->begin();
11645 it != mStorageControllers->end();
11646 ++it)
11647 {
11648 if ( std::find(backedList->begin(), backedList->end(), *it)
11649 == backedList->end()
11650 )
11651 {
11652 (*it)->uninit();
11653 }
11654 }
11655
11656 /* restore the list */
11657 mStorageControllers.rollback();
11658 }
11659
11660 /* rollback any changes to devices after restoring the list */
11661 if (mData->flModifications & IsModified_Storage)
11662 {
11663 for (StorageControllerList::const_iterator
11664 it = mStorageControllers->begin();
11665 it != mStorageControllers->end();
11666 ++it)
11667 {
11668 (*it)->i_rollback();
11669 }
11670 }
11671 }
11672
11673 if (!mUSBControllers.isNull())
11674 {
11675 if (mUSBControllers.isBackedUp())
11676 {
11677 /* unitialize all new devices (absent in the backed up list). */
11678 USBControllerList *backedList = mUSBControllers.backedUpData();
11679 for (USBControllerList::const_iterator
11680 it = mUSBControllers->begin();
11681 it != mUSBControllers->end();
11682 ++it)
11683 {
11684 if ( std::find(backedList->begin(), backedList->end(), *it)
11685 == backedList->end()
11686 )
11687 {
11688 (*it)->uninit();
11689 }
11690 }
11691
11692 /* restore the list */
11693 mUSBControllers.rollback();
11694 }
11695
11696 /* rollback any changes to devices after restoring the list */
11697 if (mData->flModifications & IsModified_USB)
11698 {
11699 for (USBControllerList::const_iterator
11700 it = mUSBControllers->begin();
11701 it != mUSBControllers->end();
11702 ++it)
11703 {
11704 (*it)->i_rollback();
11705 }
11706 }
11707 }
11708
11709 mUserData.rollback();
11710
11711 mHWData.rollback();
11712
11713 if (mData->flModifications & IsModified_Storage)
11714 i_rollbackMedia();
11715
11716 if (mPlatform)
11717 {
11718 mPlatform->i_rollback();
11719 i_platformPropertiesUpdate();
11720 }
11721
11722 if (mFirmwareSettings)
11723 mFirmwareSettings->i_rollback();
11724
11725 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11726 mRecordingSettings->i_rollback();
11727
11728 if (mTrustedPlatformModule)
11729 mTrustedPlatformModule->i_rollback();
11730
11731 if (mNvramStore)
11732 mNvramStore->i_rollback();
11733
11734 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11735 mGraphicsAdapter->i_rollback();
11736
11737 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11738 mVRDEServer->i_rollback();
11739
11740 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
11741 mAudioSettings->i_rollback();
11742
11743 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11744 mUSBDeviceFilters->i_rollback();
11745
11746 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11747 mBandwidthControl->i_rollback();
11748
11749 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
11750 mGuestDebugControl->i_rollback();
11751
11752 if (mPlatform && (mData->flModifications & IsModified_Platform))
11753 {
11754 ChipsetType_T enmChipset;
11755 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11756 ComAssertComRC(hrc);
11757
11758 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipset));
11759 }
11760
11761 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11762 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11763 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11764
11765 if (mData->flModifications & IsModified_NetworkAdapters)
11766 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11767 if ( mNetworkAdapters[slot]
11768 && mNetworkAdapters[slot]->i_isModified())
11769 {
11770 mNetworkAdapters[slot]->i_rollback();
11771 networkAdapters[slot] = mNetworkAdapters[slot];
11772 }
11773
11774 if (mData->flModifications & IsModified_SerialPorts)
11775 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11776 if ( mSerialPorts[slot]
11777 && mSerialPorts[slot]->i_isModified())
11778 {
11779 mSerialPorts[slot]->i_rollback();
11780 serialPorts[slot] = mSerialPorts[slot];
11781 }
11782
11783 if (mData->flModifications & IsModified_ParallelPorts)
11784 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11785 if ( mParallelPorts[slot]
11786 && mParallelPorts[slot]->i_isModified())
11787 {
11788 mParallelPorts[slot]->i_rollback();
11789 parallelPorts[slot] = mParallelPorts[slot];
11790 }
11791
11792 if (aNotify)
11793 {
11794 /* inform the direct session about changes */
11795
11796 ComObjPtr<Machine> that = this;
11797 uint32_t flModifications = mData->flModifications;
11798 alock.release();
11799
11800 if (flModifications & IsModified_SharedFolders)
11801 that->i_onSharedFolderChange(FALSE);
11802
11803 if (flModifications & IsModified_VRDEServer)
11804 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11805 if (flModifications & IsModified_USB)
11806 that->i_onUSBControllerChange();
11807
11808 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11809 if (networkAdapters[slot])
11810 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11811 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11812 if (serialPorts[slot])
11813 that->i_onSerialPortChange(serialPorts[slot]);
11814 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11815 if (parallelPorts[slot])
11816 that->i_onParallelPortChange(parallelPorts[slot]);
11817
11818 if (flModifications & IsModified_Storage)
11819 {
11820 for (StorageControllerList::const_iterator
11821 it = mStorageControllers->begin();
11822 it != mStorageControllers->end();
11823 ++it)
11824 {
11825 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11826 }
11827 }
11828
11829 if (flModifications & IsModified_GuestDebugControl)
11830 that->i_onGuestDebugControlChange(mGuestDebugControl);
11831
11832#if 0
11833 if (flModifications & IsModified_BandwidthControl)
11834 that->onBandwidthControlChange();
11835#endif
11836 }
11837}
11838
11839/**
11840 * Commits all the changes to machine settings.
11841 *
11842 * Note that this operation is supposed to never fail.
11843 *
11844 * @note Locks this object and children for writing.
11845 */
11846void Machine::i_commit()
11847{
11848 AutoCaller autoCaller(this);
11849 AssertComRCReturnVoid(autoCaller.hrc());
11850
11851 AutoCaller peerCaller(mPeer);
11852 AssertComRCReturnVoid(peerCaller.hrc());
11853
11854 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11855
11856 /*
11857 * use safe commit to ensure Snapshot machines (that share mUserData)
11858 * will still refer to a valid memory location
11859 */
11860 mUserData.commitCopy();
11861
11862 mHWData.commit();
11863
11864 if (mMediumAttachments.isBackedUp())
11865 i_commitMedia(Global::IsOnline(mData->mMachineState));
11866
11867 mPlatform->i_commit();
11868 mFirmwareSettings->i_commit();
11869 mRecordingSettings->i_commit();
11870 mTrustedPlatformModule->i_commit();
11871 mNvramStore->i_commit();
11872 mGraphicsAdapter->i_commit();
11873 mVRDEServer->i_commit();
11874 mAudioSettings->i_commit();
11875 mUSBDeviceFilters->i_commit();
11876 mBandwidthControl->i_commit();
11877 mGuestDebugControl->i_commit();
11878
11879 /* Since mNetworkAdapters is a list which might have been changed (resized)
11880 * without using the Backupable<> template we need to handle the copying
11881 * of the list entries manually, including the creation of peers for the
11882 * new objects. */
11883 ChipsetType_T enmChipset;
11884 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11885 ComAssertComRC(hrc);
11886
11887 bool commitNetworkAdapters = false;
11888 size_t const newSize = PlatformProperties::s_getMaxNetworkAdapters(enmChipset);
11889 if (mPeer)
11890 {
11891 size_t const oldSize = mNetworkAdapters.size();
11892 size_t const oldPeerSize = mPeer->mNetworkAdapters.size();
11893
11894 /* commit everything, even the ones which will go away */
11895 for (size_t slot = 0; slot < oldSize; slot++)
11896 mNetworkAdapters[slot]->i_commit();
11897 /* copy over the new entries, creating a peer and uninit the original */
11898 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, oldPeerSize));
11899 /* make sure to have enough room for iterating over the (newly added) slots down below */
11900 if (newSize > oldSize)
11901 {
11902 mNetworkAdapters.resize(newSize);
11903
11904 com::Utf8Str osTypeId;
11905 ComObjPtr<GuestOSType> osType = NULL;
11906 hrc = getOSTypeId(osTypeId);
11907 if (SUCCEEDED(hrc))
11908 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
11909
11910 for (size_t slot = oldSize; slot < newSize; slot++)
11911 {
11912 mNetworkAdapters[slot].createObject();
11913 mNetworkAdapters[slot]->init(this, (ULONG)slot);
11914 mNetworkAdapters[slot]->i_applyDefaults(SUCCEEDED(hrc) ? osType : NULL);
11915 }
11916 }
11917 for (size_t slot = 0; slot < newSize; slot++)
11918 {
11919 /* look if this adapter has a peer device */
11920 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11921 if (!peer)
11922 {
11923 /* no peer means the adapter is a newly created one;
11924 * create a peer owning data this data share it with */
11925 peer.createObject();
11926 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11927 }
11928 mPeer->mNetworkAdapters[slot] = peer;
11929 }
11930 /* uninit any no longer needed network adapters */
11931 for (size_t slot = newSize; slot < oldSize; ++slot)
11932 mNetworkAdapters[slot]->uninit();
11933 for (size_t slot = newSize; slot < oldPeerSize; ++slot)
11934 {
11935 if (mPeer->mNetworkAdapters[slot])
11936 mPeer->mNetworkAdapters[slot]->uninit();
11937 }
11938 /* Keep the original network adapter count until this point, so that
11939 * discarding a chipset type change will not lose settings. */
11940 mNetworkAdapters.resize(newSize);
11941 mPeer->mNetworkAdapters.resize(newSize);
11942 }
11943 else
11944 {
11945 /* we have no peer (our parent is the newly created machine);
11946 * just commit changes to the network adapters */
11947 commitNetworkAdapters = true;
11948 }
11949 if (commitNetworkAdapters)
11950 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11951 mNetworkAdapters[slot]->i_commit();
11952
11953 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11954 mSerialPorts[slot]->i_commit();
11955 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11956 mParallelPorts[slot]->i_commit();
11957
11958 bool commitStorageControllers = false;
11959
11960 if (mStorageControllers.isBackedUp())
11961 {
11962 mStorageControllers.commit();
11963
11964 if (mPeer)
11965 {
11966 /* Commit all changes to new controllers (this will reshare data with
11967 * peers for those who have peers) */
11968 StorageControllerList *newList = new StorageControllerList();
11969 for (StorageControllerList::const_iterator
11970 it = mStorageControllers->begin();
11971 it != mStorageControllers->end();
11972 ++it)
11973 {
11974 (*it)->i_commit();
11975
11976 /* look if this controller has a peer device */
11977 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11978 if (!peer)
11979 {
11980 /* no peer means the device is a newly created one;
11981 * create a peer owning data this device share it with */
11982 peer.createObject();
11983 peer->init(mPeer, *it, true /* aReshare */);
11984 }
11985 else
11986 {
11987 /* remove peer from the old list */
11988 mPeer->mStorageControllers->remove(peer);
11989 }
11990 /* and add it to the new list */
11991 newList->push_back(peer);
11992 }
11993
11994 /* uninit old peer's controllers that are left */
11995 for (StorageControllerList::const_iterator
11996 it = mPeer->mStorageControllers->begin();
11997 it != mPeer->mStorageControllers->end();
11998 ++it)
11999 {
12000 (*it)->uninit();
12001 }
12002
12003 /* attach new list of controllers to our peer */
12004 mPeer->mStorageControllers.attach(newList);
12005 }
12006 else
12007 {
12008 /* we have no peer (our parent is the newly created machine);
12009 * just commit changes to devices */
12010 commitStorageControllers = true;
12011 }
12012 }
12013 else
12014 {
12015 /* the list of controllers itself is not changed,
12016 * just commit changes to controllers themselves */
12017 commitStorageControllers = true;
12018 }
12019
12020 if (commitStorageControllers)
12021 {
12022 for (StorageControllerList::const_iterator
12023 it = mStorageControllers->begin();
12024 it != mStorageControllers->end();
12025 ++it)
12026 {
12027 (*it)->i_commit();
12028 }
12029 }
12030
12031 bool commitUSBControllers = false;
12032
12033 if (mUSBControllers.isBackedUp())
12034 {
12035 mUSBControllers.commit();
12036
12037 if (mPeer)
12038 {
12039 /* Commit all changes to new controllers (this will reshare data with
12040 * peers for those who have peers) */
12041 USBControllerList *newList = new USBControllerList();
12042 for (USBControllerList::const_iterator
12043 it = mUSBControllers->begin();
12044 it != mUSBControllers->end();
12045 ++it)
12046 {
12047 (*it)->i_commit();
12048
12049 /* look if this controller has a peer device */
12050 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12051 if (!peer)
12052 {
12053 /* no peer means the device is a newly created one;
12054 * create a peer owning data this device share it with */
12055 peer.createObject();
12056 peer->init(mPeer, *it, true /* aReshare */);
12057 }
12058 else
12059 {
12060 /* remove peer from the old list */
12061 mPeer->mUSBControllers->remove(peer);
12062 }
12063 /* and add it to the new list */
12064 newList->push_back(peer);
12065 }
12066
12067 /* uninit old peer's controllers that are left */
12068 for (USBControllerList::const_iterator
12069 it = mPeer->mUSBControllers->begin();
12070 it != mPeer->mUSBControllers->end();
12071 ++it)
12072 {
12073 (*it)->uninit();
12074 }
12075
12076 /* attach new list of controllers to our peer */
12077 mPeer->mUSBControllers.attach(newList);
12078 }
12079 else
12080 {
12081 /* we have no peer (our parent is the newly created machine);
12082 * just commit changes to devices */
12083 commitUSBControllers = true;
12084 }
12085 }
12086 else
12087 {
12088 /* the list of controllers itself is not changed,
12089 * just commit changes to controllers themselves */
12090 commitUSBControllers = true;
12091 }
12092
12093 if (commitUSBControllers)
12094 {
12095 for (USBControllerList::const_iterator
12096 it = mUSBControllers->begin();
12097 it != mUSBControllers->end();
12098 ++it)
12099 {
12100 (*it)->i_commit();
12101 }
12102 }
12103
12104 if (i_isSessionMachine())
12105 {
12106 /* attach new data to the primary machine and reshare it */
12107 mPeer->mUserData.attach(mUserData);
12108 mPeer->mHWData.attach(mHWData);
12109 /* mmMediumAttachments is reshared by fixupMedia */
12110 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12111 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12112 }
12113}
12114
12115/**
12116 * Copies all the hardware data from the given machine.
12117 *
12118 * Currently, only called when the VM is being restored from a snapshot. In
12119 * particular, this implies that the VM is not running during this method's
12120 * call.
12121 *
12122 * @note This method must be called from under this object's lock.
12123 *
12124 * @note This method doesn't call #i_commit(), so all data remains backed up and
12125 * unsaved.
12126 */
12127void Machine::i_copyFrom(Machine *aThat)
12128{
12129 AssertReturnVoid(!i_isSnapshotMachine());
12130 AssertReturnVoid(aThat->i_isSnapshotMachine());
12131
12132 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12133
12134 mHWData.assignCopy(aThat->mHWData);
12135
12136 // create copies of all shared folders (mHWData after attaching a copy
12137 // contains just references to original objects)
12138 for (HWData::SharedFolderList::iterator
12139 it = mHWData->mSharedFolders.begin();
12140 it != mHWData->mSharedFolders.end();
12141 ++it)
12142 {
12143 ComObjPtr<SharedFolder> folder;
12144 folder.createObject();
12145 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12146 AssertComRC(hrc);
12147 *it = folder;
12148 }
12149
12150 mPlatform->i_copyFrom(aThat->mPlatform);
12151 i_platformPropertiesUpdate();
12152 mFirmwareSettings->i_copyFrom(aThat->mFirmwareSettings);
12153 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12154 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12155 mNvramStore->i_copyFrom(aThat->mNvramStore);
12156 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12157 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12158 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12159 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12160 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12161 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12162
12163 /* create private copies of all controllers */
12164 mStorageControllers.backup();
12165 mStorageControllers->clear();
12166 for (StorageControllerList::const_iterator
12167 it = aThat->mStorageControllers->begin();
12168 it != aThat->mStorageControllers->end();
12169 ++it)
12170 {
12171 ComObjPtr<StorageController> ctrl;
12172 ctrl.createObject();
12173 ctrl->initCopy(this, *it);
12174 mStorageControllers->push_back(ctrl);
12175 }
12176
12177 /* create private copies of all USB controllers */
12178 mUSBControllers.backup();
12179 mUSBControllers->clear();
12180 for (USBControllerList::const_iterator
12181 it = aThat->mUSBControllers->begin();
12182 it != aThat->mUSBControllers->end();
12183 ++it)
12184 {
12185 ComObjPtr<USBController> ctrl;
12186 ctrl.createObject();
12187 ctrl->initCopy(this, *it);
12188 mUSBControllers->push_back(ctrl);
12189 }
12190
12191 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12192 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12193 {
12194 if (mNetworkAdapters[slot].isNotNull())
12195 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12196 else
12197 {
12198 unconst(mNetworkAdapters[slot]).createObject();
12199 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12200 }
12201 }
12202 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12203 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12204 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12205 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12206}
12207
12208/**
12209 * Returns whether the given storage controller is hotplug capable.
12210 *
12211 * @returns true if the controller supports hotplugging
12212 * false otherwise.
12213 * @param enmCtrlType The controller type to check for.
12214 */
12215bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12216{
12217 BOOL aHotplugCapable = FALSE;
12218 HRESULT hrc = mPlatformProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12219 AssertComRC(hrc);
12220
12221 return RT_BOOL(aHotplugCapable);
12222}
12223
12224#ifdef VBOX_WITH_RESOURCE_USAGE_API
12225
12226void Machine::i_getDiskList(MediaList &list)
12227{
12228 for (MediumAttachmentList::const_iterator
12229 it = mMediumAttachments->begin();
12230 it != mMediumAttachments->end();
12231 ++it)
12232 {
12233 MediumAttachment *pAttach = *it;
12234 /* just in case */
12235 AssertContinue(pAttach);
12236
12237 AutoCaller localAutoCallerA(pAttach);
12238 if (FAILED(localAutoCallerA.hrc())) continue;
12239
12240 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12241
12242 if (pAttach->i_getType() == DeviceType_HardDisk)
12243 list.push_back(pAttach->i_getMedium());
12244 }
12245}
12246
12247void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12248{
12249 AssertReturnVoid(isWriteLockOnCurrentThread());
12250 AssertPtrReturnVoid(aCollector);
12251
12252 pm::CollectorHAL *hal = aCollector->getHAL();
12253 /* Create sub metrics */
12254 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12255 "Percentage of processor time spent in user mode by the VM process.");
12256 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12257 "Percentage of processor time spent in kernel mode by the VM process.");
12258 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12259 "Size of resident portion of VM process in memory.");
12260 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12261 "Actual size of all VM disks combined.");
12262 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12263 "Network receive rate.");
12264 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12265 "Network transmit rate.");
12266 /* Create and register base metrics */
12267 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12268 cpuLoadUser, cpuLoadKernel);
12269 aCollector->registerBaseMetric(cpuLoad);
12270 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12271 ramUsageUsed);
12272 aCollector->registerBaseMetric(ramUsage);
12273 MediaList disks;
12274 i_getDiskList(disks);
12275 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12276 diskUsageUsed);
12277 aCollector->registerBaseMetric(diskUsage);
12278
12279 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12280 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12281 new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12283 new pm::AggregateMin()));
12284 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12285 new pm::AggregateMax()));
12286 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12287 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12288 new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12290 new pm::AggregateMin()));
12291 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12292 new pm::AggregateMax()));
12293
12294 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12295 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12296 new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12298 new pm::AggregateMin()));
12299 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12300 new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12303 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12304 new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12306 new pm::AggregateMin()));
12307 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12308 new pm::AggregateMax()));
12309
12310
12311 /* Guest metrics collector */
12312 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12313 aCollector->registerGuest(mCollectorGuest);
12314 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12315
12316 /* Create sub metrics */
12317 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12318 "Percentage of processor time spent in user mode as seen by the guest.");
12319 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12320 "Percentage of processor time spent in kernel mode as seen by the guest.");
12321 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12322 "Percentage of processor time spent idling as seen by the guest.");
12323
12324 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12325 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12326 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12327 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12328 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12329 pm::SubMetric *guestMemCache = new pm::SubMetric(
12330 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12331
12332 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12333 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12334
12335 /* Create and register base metrics */
12336 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12337 machineNetRx, machineNetTx);
12338 aCollector->registerBaseMetric(machineNetRate);
12339
12340 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12341 guestLoadUser, guestLoadKernel, guestLoadIdle);
12342 aCollector->registerBaseMetric(guestCpuLoad);
12343
12344 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12345 guestMemTotal, guestMemFree,
12346 guestMemBalloon, guestMemShared,
12347 guestMemCache, guestPagedTotal);
12348 aCollector->registerBaseMetric(guestCpuMem);
12349
12350 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12351 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12356 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12374
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12379
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12384
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12389
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12394
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12399
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12402 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12404}
12405
12406void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12407{
12408 AssertReturnVoid(isWriteLockOnCurrentThread());
12409
12410 if (aCollector)
12411 {
12412 aCollector->unregisterMetricsFor(aMachine);
12413 aCollector->unregisterBaseMetricsFor(aMachine);
12414 }
12415}
12416
12417#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12418
12419/**
12420 * Updates the machine's platform properties based on the current platform architecture.
12421 *
12422 * @note Called internally when committing, rolling back or loading settings.
12423 */
12424void Machine::i_platformPropertiesUpdate()
12425{
12426 if (mPlatform)
12427 {
12428 /* Update architecture for platform properties. */
12429 PlatformArchitecture_T platformArchitecture;
12430 HRESULT hrc = mPlatform->getArchitecture(&platformArchitecture);
12431 ComAssertComRC(hrc);
12432 hrc = mPlatformProperties->i_setArchitecture(platformArchitecture);
12433 ComAssertComRC(hrc);
12434 }
12435}
12436
12437
12438////////////////////////////////////////////////////////////////////////////////
12439
12440DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12441
12442HRESULT SessionMachine::FinalConstruct()
12443{
12444 LogFlowThisFunc(("\n"));
12445
12446 mClientToken = NULL;
12447
12448 return BaseFinalConstruct();
12449}
12450
12451void SessionMachine::FinalRelease()
12452{
12453 LogFlowThisFunc(("\n"));
12454
12455 Assert(!mClientToken);
12456 /* paranoia, should not hang around any more */
12457 if (mClientToken)
12458 {
12459 delete mClientToken;
12460 mClientToken = NULL;
12461 }
12462
12463 uninit(Uninit::Unexpected);
12464
12465 BaseFinalRelease();
12466}
12467
12468/**
12469 * @note Must be called only by Machine::LockMachine() from its own write lock.
12470 */
12471HRESULT SessionMachine::init(Machine *aMachine)
12472{
12473 LogFlowThisFuncEnter();
12474 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12475
12476 AssertReturn(aMachine, E_INVALIDARG);
12477
12478 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12479
12480 /* Enclose the state transition NotReady->InInit->Ready */
12481 AutoInitSpan autoInitSpan(this);
12482 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12483
12484 HRESULT hrc = S_OK;
12485
12486 RT_ZERO(mAuthLibCtx);
12487
12488 /* create the machine client token */
12489 try
12490 {
12491 mClientToken = new ClientToken(aMachine, this);
12492 if (!mClientToken->isReady())
12493 {
12494 delete mClientToken;
12495 mClientToken = NULL;
12496 hrc = E_FAIL;
12497 }
12498 }
12499 catch (std::bad_alloc &)
12500 {
12501 hrc = E_OUTOFMEMORY;
12502 }
12503 if (FAILED(hrc))
12504 return hrc;
12505
12506 /* memorize the peer Machine */
12507 unconst(mPeer) = aMachine;
12508 /* share the parent pointer */
12509 unconst(mParent) = aMachine->mParent;
12510
12511 /* take the pointers to data to share */
12512 mData.share(aMachine->mData);
12513 mSSData.share(aMachine->mSSData);
12514
12515 mUserData.share(aMachine->mUserData);
12516 mHWData.share(aMachine->mHWData);
12517 mMediumAttachments.share(aMachine->mMediumAttachments);
12518
12519 mStorageControllers.allocate();
12520 for (StorageControllerList::const_iterator
12521 it = aMachine->mStorageControllers->begin();
12522 it != aMachine->mStorageControllers->end();
12523 ++it)
12524 {
12525 ComObjPtr<StorageController> ctl;
12526 ctl.createObject();
12527 ctl->init(this, *it);
12528 mStorageControllers->push_back(ctl);
12529 }
12530
12531 mUSBControllers.allocate();
12532 for (USBControllerList::const_iterator
12533 it = aMachine->mUSBControllers->begin();
12534 it != aMachine->mUSBControllers->end();
12535 ++it)
12536 {
12537 ComObjPtr<USBController> ctl;
12538 ctl.createObject();
12539 ctl->init(this, *it);
12540 mUSBControllers->push_back(ctl);
12541 }
12542
12543 unconst(mPlatformProperties).createObject();
12544 mPlatformProperties->init(mParent);
12545 unconst(mPlatform).createObject();
12546 mPlatform->init(this, aMachine->mPlatform);
12547
12548 i_platformPropertiesUpdate();
12549
12550 unconst(mFirmwareSettings).createObject();
12551 mFirmwareSettings->init(this, aMachine->mFirmwareSettings);
12552
12553 unconst(mRecordingSettings).createObject();
12554 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12555
12556 unconst(mTrustedPlatformModule).createObject();
12557 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12558
12559 unconst(mNvramStore).createObject();
12560 mNvramStore->init(this, aMachine->mNvramStore);
12561
12562 /* create another GraphicsAdapter object that will be mutable */
12563 unconst(mGraphicsAdapter).createObject();
12564 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12565 /* create another VRDEServer object that will be mutable */
12566 unconst(mVRDEServer).createObject();
12567 mVRDEServer->init(this, aMachine->mVRDEServer);
12568 /* create another audio settings object that will be mutable */
12569 unconst(mAudioSettings).createObject();
12570 mAudioSettings->init(this, aMachine->mAudioSettings);
12571 /* create a list of serial ports that will be mutable */
12572 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12573 {
12574 unconst(mSerialPorts[slot]).createObject();
12575 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12576 }
12577 /* create a list of parallel ports that will be mutable */
12578 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12579 {
12580 unconst(mParallelPorts[slot]).createObject();
12581 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12582 }
12583
12584 /* create another USB device filters object that will be mutable */
12585 unconst(mUSBDeviceFilters).createObject();
12586 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12587
12588 /* create a list of network adapters that will be mutable */
12589 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12590 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12591 {
12592 unconst(mNetworkAdapters[slot]).createObject();
12593 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12594 }
12595
12596 /* create another bandwidth control object that will be mutable */
12597 unconst(mBandwidthControl).createObject();
12598 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12599
12600 unconst(mGuestDebugControl).createObject();
12601 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
12602
12603 /* default is to delete saved state on Saved -> PoweredOff transition */
12604 mRemoveSavedState = true;
12605
12606 /* Confirm a successful initialization when it's the case */
12607 autoInitSpan.setSucceeded();
12608
12609 miNATNetworksStarted = 0;
12610
12611 LogFlowThisFuncLeave();
12612 return hrc;
12613}
12614
12615/**
12616 * Uninitializes this session object. If the reason is other than
12617 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12618 * or the client watcher code.
12619 *
12620 * @param aReason uninitialization reason
12621 *
12622 * @note Locks mParent + this object for writing.
12623 */
12624void SessionMachine::uninit(Uninit::Reason aReason)
12625{
12626 LogFlowThisFuncEnter();
12627 LogFlowThisFunc(("reason=%d\n", aReason));
12628
12629 /*
12630 * Strongly reference ourselves to prevent this object deletion after
12631 * mData->mSession.mMachine.setNull() below (which can release the last
12632 * reference and call the destructor). Important: this must be done before
12633 * accessing any members (and before AutoUninitSpan that does it as well).
12634 * This self reference will be released as the very last step on return.
12635 */
12636 ComObjPtr<SessionMachine> selfRef;
12637 if (aReason != Uninit::Unexpected)
12638 selfRef = this;
12639
12640 /* Enclose the state transition Ready->InUninit->NotReady */
12641 AutoUninitSpan autoUninitSpan(this);
12642 if (autoUninitSpan.uninitDone())
12643 {
12644 LogFlowThisFunc(("Already uninitialized\n"));
12645 LogFlowThisFuncLeave();
12646 return;
12647 }
12648
12649 if (autoUninitSpan.initFailed())
12650 {
12651 /* We've been called by init() because it's failed. It's not really
12652 * necessary (nor it's safe) to perform the regular uninit sequence
12653 * below, the following is enough.
12654 */
12655 LogFlowThisFunc(("Initialization failed.\n"));
12656 /* destroy the machine client token */
12657 if (mClientToken)
12658 {
12659 delete mClientToken;
12660 mClientToken = NULL;
12661 }
12662 uninitDataAndChildObjects();
12663 mData.free();
12664 unconst(mParent) = NULL;
12665 unconst(mPeer) = NULL;
12666 LogFlowThisFuncLeave();
12667 return;
12668 }
12669
12670 MachineState_T lastState;
12671 {
12672 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12673 lastState = mData->mMachineState;
12674 }
12675 NOREF(lastState);
12676
12677#ifdef VBOX_WITH_USB
12678 // release all captured USB devices, but do this before requesting the locks below
12679 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12680 {
12681 /* Console::captureUSBDevices() is called in the VM process only after
12682 * setting the machine state to Starting or Restoring.
12683 * Console::detachAllUSBDevices() will be called upon successful
12684 * termination. So, we need to release USB devices only if there was
12685 * an abnormal termination of a running VM.
12686 *
12687 * This is identical to SessionMachine::DetachAllUSBDevices except
12688 * for the aAbnormal argument. */
12689 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12690 AssertComRC(hrc);
12691 NOREF(hrc);
12692
12693 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12694 if (service)
12695 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12696 }
12697#endif /* VBOX_WITH_USB */
12698
12699 /* We need to lock this object in uninit() because the lock is shared
12700 * with mPeer (as well as data we modify below). mParent lock is needed
12701 * by several calls to it.
12702 * We can't use AutoMultiWriteLock2 here as some error code paths
12703 * acquire the machine lock so we need to be able to drop the machine
12704 * lock individually in those cases. */
12705 AutoWriteLock aVBoxLock(mParent COMMA_LOCKVAL_SRC_POS);
12706 AutoWriteLock aMachineLock(this COMMA_LOCKVAL_SRC_POS);
12707
12708#ifdef VBOX_WITH_RESOURCE_USAGE_API
12709 /*
12710 * It is safe to call Machine::i_unregisterMetrics() here because
12711 * PerformanceCollector::samplerCallback no longer accesses guest methods
12712 * holding the lock.
12713 */
12714 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12715 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12716 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12717 if (mCollectorGuest)
12718 {
12719 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12720 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12721 mCollectorGuest = NULL;
12722 }
12723#endif
12724
12725 if (aReason == Uninit::Abnormal)
12726 {
12727 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12728
12729 /*
12730 * Move the VM to the 'Aborted' machine state unless we are restoring a
12731 * VM that was in the 'Saved' machine state. In that case, if the VM
12732 * fails before reaching either the 'Restoring' machine state or the
12733 * 'Running' machine state then we set the machine state to
12734 * 'AbortedSaved' in order to preserve the saved state file so that the
12735 * VM can be restored in the future.
12736 */
12737 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12738 i_setMachineState(MachineState_AbortedSaved);
12739 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12740 i_setMachineState(MachineState_Aborted);
12741 }
12742
12743 // any machine settings modified?
12744 if (mData->flModifications)
12745 {
12746 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12747 aMachineLock.release();
12748 discardSettings();
12749 mParent->i_unmarkRegistryModified(i_getId());
12750 aMachineLock.acquire();
12751 }
12752
12753 mData->mSession.mPID = NIL_RTPROCESS;
12754
12755 if (aReason == Uninit::Unexpected)
12756 {
12757 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12758 * client watcher thread to update the set of machines that have open
12759 * sessions. */
12760 mParent->i_updateClientWatcher();
12761 }
12762
12763 /* uninitialize all remote controls */
12764 if (mData->mSession.mRemoteControls.size())
12765 {
12766 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12767 mData->mSession.mRemoteControls.size()));
12768
12769 /* Always restart a the beginning, since the iterator is invalidated
12770 * by using erase(). */
12771 for (Data::Session::RemoteControlList::iterator
12772 it = mData->mSession.mRemoteControls.begin();
12773 it != mData->mSession.mRemoteControls.end();
12774 it = mData->mSession.mRemoteControls.begin())
12775 {
12776 ComPtr<IInternalSessionControl> pControl = *it;
12777 mData->mSession.mRemoteControls.erase(it);
12778 aMachineLock.release();
12779 aVBoxLock.release();
12780 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12781 HRESULT hrc = pControl->Uninitialize();
12782 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
12783 if (FAILED(hrc))
12784 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12785 aVBoxLock.acquire();
12786 aMachineLock.acquire();
12787 }
12788 mData->mSession.mRemoteControls.clear();
12789 }
12790
12791 /* Remove all references to the NAT network service. The service will stop
12792 * if all references (also from other VMs) are removed. */
12793 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12794 {
12795 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12796 {
12797 BOOL enabled;
12798 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12799 if ( FAILED(hrc)
12800 || !enabled)
12801 continue;
12802
12803 NetworkAttachmentType_T type;
12804 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12805 if ( SUCCEEDED(hrc)
12806 && type == NetworkAttachmentType_NATNetwork)
12807 {
12808 Bstr name;
12809 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12810 if (SUCCEEDED(hrc))
12811 {
12812 aMachineLock.release();
12813 aVBoxLock.release();
12814 Utf8Str strName(name);
12815 LogRel(("VM '%s' stops using NAT network '%s'\n",
12816 mUserData->s.strName.c_str(), strName.c_str()));
12817 mParent->i_natNetworkRefDec(strName);
12818 aVBoxLock.acquire();
12819 aMachineLock.acquire();
12820 }
12821 }
12822 }
12823 }
12824
12825 /*
12826 * An expected uninitialization can come only from #i_checkForDeath().
12827 * Otherwise it means that something's gone really wrong (for example,
12828 * the Session implementation has released the VirtualBox reference
12829 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12830 * etc). However, it's also possible, that the client releases the IPC
12831 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12832 * but the VirtualBox release event comes first to the server process.
12833 * This case is practically possible, so we should not assert on an
12834 * unexpected uninit, just log a warning.
12835 */
12836
12837 if (aReason == Uninit::Unexpected)
12838 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12839
12840 if (aReason != Uninit::Normal)
12841 {
12842 mData->mSession.mDirectControl.setNull();
12843 }
12844 else
12845 {
12846 /* this must be null here (see #OnSessionEnd()) */
12847 Assert(mData->mSession.mDirectControl.isNull());
12848 Assert(mData->mSession.mState == SessionState_Unlocking);
12849 Assert(!mData->mSession.mProgress.isNull());
12850 }
12851 if (mData->mSession.mProgress)
12852 {
12853 if (aReason == Uninit::Normal)
12854 mData->mSession.mProgress->i_notifyComplete(S_OK);
12855 else
12856 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12857 COM_IIDOF(ISession),
12858 getComponentName(),
12859 tr("The VM session was aborted"));
12860 mData->mSession.mProgress.setNull();
12861 }
12862
12863 if (mConsoleTaskData.mProgress)
12864 {
12865 Assert(aReason == Uninit::Abnormal);
12866 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12867 COM_IIDOF(ISession),
12868 getComponentName(),
12869 tr("The VM session was aborted"));
12870 mConsoleTaskData.mProgress.setNull();
12871 }
12872
12873 /* remove the association between the peer machine and this session machine */
12874 Assert( (SessionMachine*)mData->mSession.mMachine == this
12875 || aReason == Uninit::Unexpected);
12876
12877 /* reset the rest of session data */
12878 mData->mSession.mLockType = LockType_Null;
12879 mData->mSession.mMachine.setNull();
12880 mData->mSession.mState = SessionState_Unlocked;
12881 mData->mSession.mName.setNull();
12882
12883 /* destroy the machine client token before leaving the exclusive lock */
12884 if (mClientToken)
12885 {
12886 delete mClientToken;
12887 mClientToken = NULL;
12888 }
12889
12890 /* fire an event */
12891 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12892
12893 uninitDataAndChildObjects();
12894
12895 /* free the essential data structure last */
12896 mData.free();
12897
12898 /* release the exclusive locks before setting the below two to NULL */
12899 aMachineLock.release();
12900 aVBoxLock.release();
12901
12902 unconst(mParent) = NULL;
12903 unconst(mPeer) = NULL;
12904
12905 AuthLibUnload(&mAuthLibCtx);
12906
12907 LogFlowThisFuncLeave();
12908}
12909
12910// util::Lockable interface
12911////////////////////////////////////////////////////////////////////////////////
12912
12913/**
12914 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12915 * with the primary Machine instance (mPeer).
12916 */
12917RWLockHandle *SessionMachine::lockHandle() const
12918{
12919 AssertReturn(mPeer != NULL, NULL);
12920 return mPeer->lockHandle();
12921}
12922
12923// IInternalMachineControl methods
12924////////////////////////////////////////////////////////////////////////////////
12925
12926/**
12927 * Passes collected guest statistics to performance collector object
12928 */
12929HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12930 ULONG aCpuKernel, ULONG aCpuIdle,
12931 ULONG aMemTotal, ULONG aMemFree,
12932 ULONG aMemBalloon, ULONG aMemShared,
12933 ULONG aMemCache, ULONG aPageTotal,
12934 ULONG aAllocVMM, ULONG aFreeVMM,
12935 ULONG aBalloonedVMM, ULONG aSharedVMM,
12936 ULONG aVmNetRx, ULONG aVmNetTx)
12937{
12938#ifdef VBOX_WITH_RESOURCE_USAGE_API
12939 if (mCollectorGuest)
12940 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12941 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12942 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12943 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12944
12945 return S_OK;
12946#else
12947 NOREF(aValidStats);
12948 NOREF(aCpuUser);
12949 NOREF(aCpuKernel);
12950 NOREF(aCpuIdle);
12951 NOREF(aMemTotal);
12952 NOREF(aMemFree);
12953 NOREF(aMemBalloon);
12954 NOREF(aMemShared);
12955 NOREF(aMemCache);
12956 NOREF(aPageTotal);
12957 NOREF(aAllocVMM);
12958 NOREF(aFreeVMM);
12959 NOREF(aBalloonedVMM);
12960 NOREF(aSharedVMM);
12961 NOREF(aVmNetRx);
12962 NOREF(aVmNetTx);
12963 return E_NOTIMPL;
12964#endif
12965}
12966
12967////////////////////////////////////////////////////////////////////////////////
12968//
12969// SessionMachine task records
12970//
12971////////////////////////////////////////////////////////////////////////////////
12972
12973/**
12974 * Task record for saving the machine state.
12975 */
12976class SessionMachine::SaveStateTask
12977 : public Machine::Task
12978{
12979public:
12980 SaveStateTask(SessionMachine *m,
12981 Progress *p,
12982 const Utf8Str &t,
12983 Reason_T enmReason,
12984 const Utf8Str &strStateFilePath)
12985 : Task(m, p, t),
12986 m_enmReason(enmReason),
12987 m_strStateFilePath(strStateFilePath)
12988 {}
12989
12990private:
12991 void handler()
12992 {
12993 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12994 }
12995
12996 Reason_T m_enmReason;
12997 Utf8Str m_strStateFilePath;
12998
12999 friend class SessionMachine;
13000};
13001
13002/**
13003 * Task thread implementation for SessionMachine::SaveState(), called from
13004 * SessionMachine::taskHandler().
13005 *
13006 * @note Locks this object for writing.
13007 *
13008 * @param task
13009 */
13010void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13011{
13012 LogFlowThisFuncEnter();
13013
13014 AutoCaller autoCaller(this);
13015 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13016 if (FAILED(autoCaller.hrc()))
13017 {
13018 /* we might have been uninitialized because the session was accidentally
13019 * closed by the client, so don't assert */
13020 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
13021 task.m_pProgress->i_notifyComplete(hrc);
13022 LogFlowThisFuncLeave();
13023 return;
13024 }
13025
13026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 HRESULT hrc = S_OK;
13029
13030 try
13031 {
13032 ComPtr<IInternalSessionControl> directControl;
13033 if (mData->mSession.mLockType == LockType_VM)
13034 directControl = mData->mSession.mDirectControl;
13035 if (directControl.isNull())
13036 throw setError(VBOX_E_INVALID_VM_STATE,
13037 tr("Trying to save state without a running VM"));
13038 alock.release();
13039 BOOL fSuspendedBySave;
13040 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13041 Assert(!fSuspendedBySave);
13042 alock.acquire();
13043
13044 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
13045 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
13046 throw E_FAIL);
13047
13048 if (SUCCEEDED(hrc))
13049 {
13050 mSSData->strStateFilePath = task.m_strStateFilePath;
13051
13052 /* save all VM settings */
13053 hrc = i_saveSettings(NULL, alock);
13054 // no need to check whether VirtualBox.xml needs saving also since
13055 // we can't have a name change pending at this point
13056 }
13057 else
13058 {
13059 // On failure, set the state to the state we had at the beginning.
13060 i_setMachineState(task.m_machineStateBackup);
13061 i_updateMachineStateOnClient();
13062
13063 // Delete the saved state file (might have been already created).
13064 // No need to check whether this is shared with a snapshot here
13065 // because we certainly created a fresh saved state file here.
13066 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13067 }
13068 }
13069 catch (HRESULT hrcXcpt)
13070 {
13071 hrc = hrcXcpt;
13072 }
13073
13074 task.m_pProgress->i_notifyComplete(hrc);
13075
13076 LogFlowThisFuncLeave();
13077}
13078
13079/**
13080 * @note Locks this object for writing.
13081 */
13082HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13083{
13084 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13085}
13086
13087HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13088{
13089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13090
13091 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13092 if (FAILED(hrc)) return hrc;
13093
13094 if ( mData->mMachineState != MachineState_Running
13095 && mData->mMachineState != MachineState_Paused
13096 )
13097 return setError(VBOX_E_INVALID_VM_STATE,
13098 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13099 Global::stringifyMachineState(mData->mMachineState));
13100
13101 ComObjPtr<Progress> pProgress;
13102 pProgress.createObject();
13103 hrc = pProgress->init(i_getVirtualBox(),
13104 static_cast<IMachine *>(this) /* aInitiator */,
13105 tr("Saving the execution state of the virtual machine"),
13106 FALSE /* aCancelable */);
13107 if (FAILED(hrc))
13108 return hrc;
13109
13110 Utf8Str strStateFilePath;
13111 i_composeSavedStateFilename(strStateFilePath);
13112
13113 /* create and start the task on a separate thread (note that it will not
13114 * start working until we release alock) */
13115 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13116 hrc = pTask->createThread();
13117 if (FAILED(hrc))
13118 return hrc;
13119
13120 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13121 i_setMachineState(MachineState_Saving);
13122 i_updateMachineStateOnClient();
13123
13124 pProgress.queryInterfaceTo(aProgress.asOutParam());
13125
13126 return S_OK;
13127}
13128
13129/**
13130 * @note Locks this object for writing.
13131 */
13132HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13133{
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13137 if (FAILED(hrc)) return hrc;
13138
13139 if ( mData->mMachineState != MachineState_PoweredOff
13140 && mData->mMachineState != MachineState_Teleported
13141 && mData->mMachineState != MachineState_Aborted
13142 )
13143 return setError(VBOX_E_INVALID_VM_STATE,
13144 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13145 Global::stringifyMachineState(mData->mMachineState));
13146
13147 com::Utf8Str stateFilePathFull;
13148 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13149 if (RT_FAILURE(vrc))
13150 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13151 tr("Invalid saved state file path '%s' (%Rrc)"),
13152 aSavedStateFile.c_str(),
13153 vrc);
13154
13155 mSSData->strStateFilePath = stateFilePathFull;
13156
13157 /* The below i_setMachineState() will detect the state transition and will
13158 * update the settings file */
13159
13160 return i_setMachineState(MachineState_Saved);
13161}
13162
13163/**
13164 * @note Locks this object for writing.
13165 */
13166HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13167{
13168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13169
13170 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13171 if (FAILED(hrc)) return hrc;
13172
13173 if ( mData->mMachineState != MachineState_Saved
13174 && mData->mMachineState != MachineState_AbortedSaved)
13175 return setError(VBOX_E_INVALID_VM_STATE,
13176 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13177 Global::stringifyMachineState(mData->mMachineState));
13178
13179 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13180
13181 /*
13182 * Saved -> PoweredOff transition will be detected in the SessionMachine
13183 * and properly handled.
13184 */
13185 hrc = i_setMachineState(MachineState_PoweredOff);
13186 return hrc;
13187}
13188
13189
13190/**
13191 * @note Locks the same as #i_setMachineState() does.
13192 */
13193HRESULT SessionMachine::updateState(MachineState_T aState)
13194{
13195 return i_setMachineState(aState);
13196}
13197
13198/**
13199 * @note Locks this object for writing.
13200 */
13201HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13202{
13203 IProgress *pProgress(aProgress);
13204
13205 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13206
13207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13208
13209 if (mData->mSession.mState != SessionState_Locked)
13210 return VBOX_E_INVALID_OBJECT_STATE;
13211
13212 if (!mData->mSession.mProgress.isNull())
13213 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13214
13215 /* If we didn't reference the NAT network service yet, add a reference to
13216 * force a start */
13217 if (miNATNetworksStarted < 1)
13218 {
13219 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13220 {
13221 BOOL enabled;
13222 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13223 if ( FAILED(hrc)
13224 || !enabled)
13225 continue;
13226
13227 NetworkAttachmentType_T type;
13228 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13229 if ( SUCCEEDED(hrc)
13230 && type == NetworkAttachmentType_NATNetwork)
13231 {
13232 Bstr name;
13233 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13234 if (SUCCEEDED(hrc))
13235 {
13236 Utf8Str strName(name);
13237 LogRel(("VM '%s' starts using NAT network '%s'\n",
13238 mUserData->s.strName.c_str(), strName.c_str()));
13239 mPeer->lockHandle()->unlockWrite();
13240 mParent->i_natNetworkRefInc(strName);
13241#ifdef RT_LOCK_STRICT
13242 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13243#else
13244 mPeer->lockHandle()->lockWrite();
13245#endif
13246 }
13247 }
13248 }
13249 miNATNetworksStarted++;
13250 }
13251
13252 LogFlowThisFunc(("returns S_OK.\n"));
13253 return S_OK;
13254}
13255
13256/**
13257 * @note Locks this object for writing.
13258 */
13259HRESULT SessionMachine::endPowerUp(LONG aResult)
13260{
13261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13262
13263 if (mData->mSession.mState != SessionState_Locked)
13264 return VBOX_E_INVALID_OBJECT_STATE;
13265
13266 /* Finalize the LaunchVMProcess progress object. */
13267 if (mData->mSession.mProgress)
13268 {
13269 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13270 mData->mSession.mProgress.setNull();
13271 }
13272
13273 if (SUCCEEDED((HRESULT)aResult))
13274 {
13275#ifdef VBOX_WITH_RESOURCE_USAGE_API
13276 /* The VM has been powered up successfully, so it makes sense
13277 * now to offer the performance metrics for a running machine
13278 * object. Doing it earlier wouldn't be safe. */
13279 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13280 mData->mSession.mPID);
13281#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13282 }
13283
13284 return S_OK;
13285}
13286
13287/**
13288 * @note Locks this object for writing.
13289 */
13290HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13291{
13292 LogFlowThisFuncEnter();
13293
13294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13295
13296 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13297 E_FAIL);
13298
13299 /* create a progress object to track operation completion */
13300 ComObjPtr<Progress> pProgress;
13301 pProgress.createObject();
13302 pProgress->init(i_getVirtualBox(),
13303 static_cast<IMachine *>(this) /* aInitiator */,
13304 tr("Stopping the virtual machine"),
13305 FALSE /* aCancelable */);
13306
13307 /* fill in the console task data */
13308 mConsoleTaskData.mLastState = mData->mMachineState;
13309 mConsoleTaskData.mProgress = pProgress;
13310
13311 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13312 i_setMachineState(MachineState_Stopping);
13313
13314 pProgress.queryInterfaceTo(aProgress.asOutParam());
13315
13316 return S_OK;
13317}
13318
13319/**
13320 * @note Locks this object for writing.
13321 */
13322HRESULT SessionMachine::endPoweringDown(LONG aResult,
13323 const com::Utf8Str &aErrMsg)
13324{
13325 HRESULT const hrcResult = (HRESULT)aResult;
13326 LogFlowThisFuncEnter();
13327
13328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13329
13330 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13331 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13332 && mConsoleTaskData.mLastState != MachineState_Null,
13333 E_FAIL);
13334
13335 /*
13336 * On failure, set the state to the state we had when BeginPoweringDown()
13337 * was called (this is expected by Console::PowerDown() and the associated
13338 * task). On success the VM process already changed the state to
13339 * MachineState_PoweredOff, so no need to do anything.
13340 */
13341 if (FAILED(hrcResult))
13342 i_setMachineState(mConsoleTaskData.mLastState);
13343
13344 /* notify the progress object about operation completion */
13345 Assert(mConsoleTaskData.mProgress);
13346 if (SUCCEEDED(hrcResult))
13347 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13348 else
13349 {
13350 if (aErrMsg.length())
13351 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13352 COM_IIDOF(ISession),
13353 getComponentName(),
13354 aErrMsg.c_str());
13355 else
13356 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13357 }
13358
13359 /* clear out the temporary saved state data */
13360 mConsoleTaskData.mLastState = MachineState_Null;
13361 mConsoleTaskData.mProgress.setNull();
13362
13363 LogFlowThisFuncLeave();
13364 return S_OK;
13365}
13366
13367
13368/**
13369 * Goes through the USB filters of the given machine to see if the given
13370 * device matches any filter or not.
13371 *
13372 * @note Locks the same as USBController::hasMatchingFilter() does.
13373 */
13374HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13375 BOOL *aMatched,
13376 ULONG *aMaskedInterfaces)
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380#ifdef VBOX_WITH_USB
13381 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13382#else
13383 NOREF(aDevice);
13384 NOREF(aMaskedInterfaces);
13385 *aMatched = FALSE;
13386#endif
13387
13388 return S_OK;
13389}
13390
13391/**
13392 * @note Locks the same as Host::captureUSBDevice() does.
13393 */
13394HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13395{
13396 LogFlowThisFunc(("\n"));
13397
13398#ifdef VBOX_WITH_USB
13399 /* if captureDeviceForVM() fails, it must have set extended error info */
13400 clearError();
13401 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13402 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13403 return hrc;
13404
13405 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13406 AssertReturn(service, E_FAIL);
13407 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13408#else
13409 RT_NOREF(aId, aCaptureFilename);
13410 return E_NOTIMPL;
13411#endif
13412}
13413
13414/**
13415 * @note Locks the same as Host::detachUSBDevice() does.
13416 */
13417HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13418 BOOL aDone)
13419{
13420 LogFlowThisFunc(("\n"));
13421
13422#ifdef VBOX_WITH_USB
13423 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13424 AssertReturn(service, E_FAIL);
13425 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13426#else
13427 NOREF(aId);
13428 NOREF(aDone);
13429 return E_NOTIMPL;
13430#endif
13431}
13432
13433/**
13434 * Inserts all machine filters to the USB proxy service and then calls
13435 * Host::autoCaptureUSBDevices().
13436 *
13437 * Called by Console from the VM process upon VM startup.
13438 *
13439 * @note Locks what called methods lock.
13440 */
13441HRESULT SessionMachine::autoCaptureUSBDevices()
13442{
13443 LogFlowThisFunc(("\n"));
13444
13445#ifdef VBOX_WITH_USB
13446 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13447 AssertComRC(hrc);
13448 NOREF(hrc);
13449
13450 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13451 AssertReturn(service, E_FAIL);
13452 return service->autoCaptureDevicesForVM(this);
13453#else
13454 return S_OK;
13455#endif
13456}
13457
13458/**
13459 * Removes all machine filters from the USB proxy service and then calls
13460 * Host::detachAllUSBDevices().
13461 *
13462 * Called by Console from the VM process upon normal VM termination or by
13463 * SessionMachine::uninit() upon abnormal VM termination (from under the
13464 * Machine/SessionMachine lock).
13465 *
13466 * @note Locks what called methods lock.
13467 */
13468HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13469{
13470 LogFlowThisFunc(("\n"));
13471
13472#ifdef VBOX_WITH_USB
13473 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13474 AssertComRC(hrc);
13475 NOREF(hrc);
13476
13477 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13478 AssertReturn(service, E_FAIL);
13479 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13480#else
13481 NOREF(aDone);
13482 return S_OK;
13483#endif
13484}
13485
13486/**
13487 * @note Locks this object for writing.
13488 */
13489HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13490 ComPtr<IProgress> &aProgress)
13491{
13492 LogFlowThisFuncEnter();
13493
13494 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13495 /*
13496 * We don't assert below because it might happen that a non-direct session
13497 * informs us it is closed right after we've been uninitialized -- it's ok.
13498 */
13499
13500 /* get IInternalSessionControl interface */
13501 ComPtr<IInternalSessionControl> control(aSession);
13502
13503 ComAssertRet(!control.isNull(), E_INVALIDARG);
13504
13505 /* Creating a Progress object requires the VirtualBox lock, and
13506 * thus locking it here is required by the lock order rules. */
13507 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13508
13509 if (control == mData->mSession.mDirectControl)
13510 {
13511 /* The direct session is being normally closed by the client process
13512 * ----------------------------------------------------------------- */
13513
13514 /* go to the closing state (essential for all open*Session() calls and
13515 * for #i_checkForDeath()) */
13516 Assert(mData->mSession.mState == SessionState_Locked);
13517 mData->mSession.mState = SessionState_Unlocking;
13518
13519 /* set direct control to NULL to release the remote instance */
13520 mData->mSession.mDirectControl.setNull();
13521 LogFlowThisFunc(("Direct control is set to NULL\n"));
13522
13523 if (mData->mSession.mProgress)
13524 {
13525 /* finalize the progress, someone might wait if a frontend
13526 * closes the session before powering on the VM. */
13527 mData->mSession.mProgress->notifyComplete(E_FAIL,
13528 COM_IIDOF(ISession),
13529 getComponentName(),
13530 tr("The VM session was closed before any attempt to power it on"));
13531 mData->mSession.mProgress.setNull();
13532 }
13533
13534 /* Create the progress object the client will use to wait until
13535 * #i_checkForDeath() is called to uninitialize this session object after
13536 * it releases the IPC semaphore.
13537 * Note! Because we're "reusing" mProgress here, this must be a proxy
13538 * object just like for LaunchVMProcess. */
13539 Assert(mData->mSession.mProgress.isNull());
13540 ComObjPtr<ProgressProxy> progress;
13541 progress.createObject();
13542 ComPtr<IUnknown> pPeer(mPeer);
13543 progress->init(mParent, pPeer,
13544 Bstr(tr("Closing session")).raw(),
13545 FALSE /* aCancelable */);
13546 progress.queryInterfaceTo(aProgress.asOutParam());
13547 mData->mSession.mProgress = progress;
13548 }
13549 else
13550 {
13551 /* the remote session is being normally closed */
13552 bool found = false;
13553 for (Data::Session::RemoteControlList::iterator
13554 it = mData->mSession.mRemoteControls.begin();
13555 it != mData->mSession.mRemoteControls.end();
13556 ++it)
13557 {
13558 if (control == *it)
13559 {
13560 found = true;
13561 // This MUST be erase(it), not remove(*it) as the latter
13562 // triggers a very nasty use after free due to the place where
13563 // the value "lives".
13564 mData->mSession.mRemoteControls.erase(it);
13565 break;
13566 }
13567 }
13568 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13569 E_INVALIDARG);
13570 }
13571
13572 /* signal the client watcher thread, because the client is going away */
13573 mParent->i_updateClientWatcher();
13574
13575 LogFlowThisFuncLeave();
13576 return S_OK;
13577}
13578
13579HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13580 std::vector<com::Utf8Str> &aValues,
13581 std::vector<LONG64> &aTimestamps,
13582 std::vector<com::Utf8Str> &aFlags)
13583{
13584 LogFlowThisFunc(("\n"));
13585
13586#ifdef VBOX_WITH_GUEST_PROPS
13587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13588
13589 size_t cEntries = mHWData->mGuestProperties.size();
13590 aNames.resize(cEntries);
13591 aValues.resize(cEntries);
13592 aTimestamps.resize(cEntries);
13593 aFlags.resize(cEntries);
13594
13595 size_t i = 0;
13596 for (HWData::GuestPropertyMap::const_iterator
13597 it = mHWData->mGuestProperties.begin();
13598 it != mHWData->mGuestProperties.end();
13599 ++it, ++i)
13600 {
13601 aNames[i] = it->first;
13602 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
13603 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13604
13605 aValues[i] = it->second.strValue;
13606 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
13607 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13608
13609 aTimestamps[i] = it->second.mTimestamp;
13610
13611 /* If it is NULL, keep it NULL. */
13612 if (it->second.mFlags)
13613 {
13614 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13615 GuestPropWriteFlags(it->second.mFlags, szFlags);
13616 aFlags[i] = szFlags;
13617 }
13618 else
13619 aFlags[i] = "";
13620 }
13621 return S_OK;
13622#else
13623 ReturnComNotImplemented();
13624#endif
13625}
13626
13627HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13628 const com::Utf8Str &aValue,
13629 LONG64 aTimestamp,
13630 const com::Utf8Str &aFlags,
13631 BOOL fWasDeleted)
13632{
13633 LogFlowThisFunc(("\n"));
13634
13635#ifdef VBOX_WITH_GUEST_PROPS
13636 try
13637 {
13638 /*
13639 * Convert input up front.
13640 */
13641 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13642 if (aFlags.length())
13643 {
13644 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13645 AssertRCReturn(vrc, E_INVALIDARG);
13646 }
13647
13648 /*
13649 * Now grab the object lock, validate the state and do the update.
13650 */
13651
13652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13653
13654 if (!Global::IsOnline(mData->mMachineState))
13655 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13656
13657 i_setModified(IsModified_MachineData);
13658 mHWData.backup();
13659
13660 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13661 if (it != mHWData->mGuestProperties.end())
13662 {
13663 if (!fWasDeleted)
13664 {
13665 it->second.strValue = aValue;
13666 it->second.mTimestamp = aTimestamp;
13667 it->second.mFlags = fFlags;
13668 }
13669 else
13670 mHWData->mGuestProperties.erase(it);
13671
13672 mData->mGuestPropertiesModified = TRUE;
13673 }
13674 else if (!fWasDeleted)
13675 {
13676 HWData::GuestProperty prop;
13677 prop.strValue = aValue;
13678 prop.mTimestamp = aTimestamp;
13679 prop.mFlags = fFlags;
13680
13681 mHWData->mGuestProperties[aName] = prop;
13682 mData->mGuestPropertiesModified = TRUE;
13683 }
13684
13685 alock.release();
13686
13687 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13688 }
13689 catch (...)
13690 {
13691 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13692 }
13693 return S_OK;
13694#else
13695 ReturnComNotImplemented();
13696#endif
13697}
13698
13699
13700HRESULT SessionMachine::lockMedia()
13701{
13702 AutoMultiWriteLock2 alock(this->lockHandle(),
13703 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13704
13705 AssertReturn( mData->mMachineState == MachineState_Starting
13706 || mData->mMachineState == MachineState_Restoring
13707 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13708
13709 clearError();
13710 alock.release();
13711 return i_lockMedia();
13712}
13713
13714HRESULT SessionMachine::unlockMedia()
13715{
13716 HRESULT hrc = i_unlockMedia();
13717 return hrc;
13718}
13719
13720HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13721 ComPtr<IMediumAttachment> &aNewAttachment)
13722{
13723 // request the host lock first, since might be calling Host methods for getting host drives;
13724 // next, protect the media tree all the while we're in here, as well as our member variables
13725 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13726 this->lockHandle(),
13727 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13728
13729 IMediumAttachment *iAttach = aAttachment;
13730 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13731
13732 Utf8Str ctrlName;
13733 LONG lPort;
13734 LONG lDevice;
13735 bool fTempEject;
13736 {
13737 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13738
13739 /* Need to query the details first, as the IMediumAttachment reference
13740 * might be to the original settings, which we are going to change. */
13741 ctrlName = pAttach->i_getControllerName();
13742 lPort = pAttach->i_getPort();
13743 lDevice = pAttach->i_getDevice();
13744 fTempEject = pAttach->i_getTempEject();
13745 }
13746
13747 if (!fTempEject)
13748 {
13749 /* Remember previously mounted medium. The medium before taking the
13750 * backup is not necessarily the same thing. */
13751 ComObjPtr<Medium> oldmedium;
13752 oldmedium = pAttach->i_getMedium();
13753
13754 i_setModified(IsModified_Storage);
13755 mMediumAttachments.backup();
13756
13757 // The backup operation makes the pAttach reference point to the
13758 // old settings. Re-get the correct reference.
13759 pAttach = i_findAttachment(*mMediumAttachments.data(),
13760 ctrlName,
13761 lPort,
13762 lDevice);
13763
13764 {
13765 AutoCaller autoAttachCaller(this);
13766 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
13767
13768 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13769 if (!oldmedium.isNull())
13770 oldmedium->i_removeBackReference(mData->mUuid);
13771
13772 pAttach->i_updateMedium(NULL);
13773 pAttach->i_updateEjected();
13774 }
13775
13776 i_setModified(IsModified_Storage);
13777 }
13778 else
13779 {
13780 {
13781 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13782 pAttach->i_updateEjected();
13783 }
13784 }
13785
13786 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13787
13788 return S_OK;
13789}
13790
13791HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13792 com::Utf8Str &aResult)
13793{
13794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13795
13796 HRESULT hrc = S_OK;
13797
13798 if (!mAuthLibCtx.hAuthLibrary)
13799 {
13800 /* Load the external authentication library. */
13801 Bstr authLibrary;
13802 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13803
13804 Utf8Str filename = authLibrary;
13805
13806 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13807 if (RT_FAILURE(vrc))
13808 hrc = setErrorBoth(E_FAIL, vrc,
13809 tr("Could not load the external authentication library '%s' (%Rrc)"),
13810 filename.c_str(), vrc);
13811 }
13812
13813 /* The auth library might need the machine lock. */
13814 alock.release();
13815
13816 if (FAILED(hrc))
13817 return hrc;
13818
13819 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13820 {
13821 enum VRDEAuthParams
13822 {
13823 parmUuid = 1,
13824 parmGuestJudgement,
13825 parmUser,
13826 parmPassword,
13827 parmDomain,
13828 parmClientId
13829 };
13830
13831 AuthResult result = AuthResultAccessDenied;
13832
13833 Guid uuid(aAuthParams[parmUuid]);
13834 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13835 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13836
13837 result = AuthLibAuthenticate(&mAuthLibCtx,
13838 uuid.raw(), guestJudgement,
13839 aAuthParams[parmUser].c_str(),
13840 aAuthParams[parmPassword].c_str(),
13841 aAuthParams[parmDomain].c_str(),
13842 u32ClientId);
13843
13844 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13845 size_t cbPassword = aAuthParams[parmPassword].length();
13846 if (cbPassword)
13847 {
13848 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13849 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13850 }
13851
13852 if (result == AuthResultAccessGranted)
13853 aResult = "granted";
13854 else
13855 aResult = "denied";
13856
13857 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13858 aAuthParams[parmUser].c_str(), aResult.c_str()));
13859 }
13860 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13861 {
13862 enum VRDEAuthDisconnectParams
13863 {
13864 parmUuid = 1,
13865 parmClientId
13866 };
13867
13868 Guid uuid(aAuthParams[parmUuid]);
13869 uint32_t u32ClientId = 0;
13870 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13871 }
13872 else
13873 {
13874 hrc = E_INVALIDARG;
13875 }
13876
13877 return hrc;
13878}
13879
13880// public methods only for internal purposes
13881/////////////////////////////////////////////////////////////////////////////
13882
13883#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13884/**
13885 * Called from the client watcher thread to check for expected or unexpected
13886 * death of the client process that has a direct session to this machine.
13887 *
13888 * On Win32 and on OS/2, this method is called only when we've got the
13889 * mutex (i.e. the client has either died or terminated normally) so it always
13890 * returns @c true (the client is terminated, the session machine is
13891 * uninitialized).
13892 *
13893 * On other platforms, the method returns @c true if the client process has
13894 * terminated normally or abnormally and the session machine was uninitialized,
13895 * and @c false if the client process is still alive.
13896 *
13897 * @note Locks this object for writing.
13898 */
13899bool SessionMachine::i_checkForDeath()
13900{
13901 Uninit::Reason reason;
13902 bool terminated = false;
13903
13904 /* Enclose autoCaller with a block because calling uninit() from under it
13905 * will deadlock. */
13906 {
13907 AutoCaller autoCaller(this);
13908 if (!autoCaller.isOk())
13909 {
13910 /* return true if not ready, to cause the client watcher to exclude
13911 * the corresponding session from watching */
13912 LogFlowThisFunc(("Already uninitialized!\n"));
13913 return true;
13914 }
13915
13916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13917
13918 /* Determine the reason of death: if the session state is Closing here,
13919 * everything is fine. Otherwise it means that the client did not call
13920 * OnSessionEnd() before it released the IPC semaphore. This may happen
13921 * either because the client process has abnormally terminated, or
13922 * because it simply forgot to call ISession::Close() before exiting. We
13923 * threat the latter also as an abnormal termination (see
13924 * Session::uninit() for details). */
13925 reason = mData->mSession.mState == SessionState_Unlocking ?
13926 Uninit::Normal :
13927 Uninit::Abnormal;
13928
13929 if (mClientToken)
13930 terminated = mClientToken->release();
13931 } /* AutoCaller block */
13932
13933 if (terminated)
13934 uninit(reason);
13935
13936 return terminated;
13937}
13938
13939void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13940{
13941 LogFlowThisFunc(("\n"));
13942
13943 strTokenId.setNull();
13944
13945 AutoCaller autoCaller(this);
13946 AssertComRCReturnVoid(autoCaller.hrc());
13947
13948 Assert(mClientToken);
13949 if (mClientToken)
13950 mClientToken->getId(strTokenId);
13951}
13952#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13953IToken *SessionMachine::i_getToken()
13954{
13955 LogFlowThisFunc(("\n"));
13956
13957 AutoCaller autoCaller(this);
13958 AssertComRCReturn(autoCaller.hrc(), NULL);
13959
13960 Assert(mClientToken);
13961 if (mClientToken)
13962 return mClientToken->getToken();
13963 else
13964 return NULL;
13965}
13966#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13967
13968Machine::ClientToken *SessionMachine::i_getClientToken()
13969{
13970 LogFlowThisFunc(("\n"));
13971
13972 AutoCaller autoCaller(this);
13973 AssertComRCReturn(autoCaller.hrc(), NULL);
13974
13975 return mClientToken;
13976}
13977
13978
13979/**
13980 * @note Locks this object for reading.
13981 */
13982HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13983{
13984 LogFlowThisFunc(("\n"));
13985
13986 AutoCaller autoCaller(this);
13987 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13988
13989 ComPtr<IInternalSessionControl> directControl;
13990 {
13991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13992 if (mData->mSession.mLockType == LockType_VM)
13993 directControl = mData->mSession.mDirectControl;
13994 }
13995
13996 /* ignore notifications sent after #OnSessionEnd() is called */
13997 if (!directControl)
13998 return S_OK;
13999
14000 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14001}
14002
14003/**
14004 * @note Locks this object for reading.
14005 */
14006HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14007 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14008 const Utf8Str &aGuestIp, LONG aGuestPort)
14009{
14010 LogFlowThisFunc(("\n"));
14011
14012 AutoCaller autoCaller(this);
14013 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14014
14015 ComPtr<IInternalSessionControl> directControl;
14016 {
14017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14018 if (mData->mSession.mLockType == LockType_VM)
14019 directControl = mData->mSession.mDirectControl;
14020 }
14021
14022 /* ignore notifications sent after #OnSessionEnd() is called */
14023 if (!directControl)
14024 return S_OK;
14025 /*
14026 * instead acting like callback we ask IVirtualBox deliver corresponding event
14027 */
14028
14029 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14030 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14031 return S_OK;
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 if (mData->mSession.mLockType == LockType_VM)
14048 directControl = mData->mSession.mDirectControl;
14049 }
14050
14051 /* ignore notifications sent after #OnSessionEnd() is called */
14052 if (!directControl)
14053 return S_OK;
14054
14055 return directControl->OnAudioAdapterChange(audioAdapter);
14056}
14057
14058/**
14059 * @note Locks this object for reading.
14060 */
14061HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14062{
14063 LogFlowThisFunc(("\n"));
14064
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14067
14068 ComPtr<IInternalSessionControl> directControl;
14069 {
14070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14071 if (mData->mSession.mLockType == LockType_VM)
14072 directControl = mData->mSession.mDirectControl;
14073 }
14074
14075 /* ignore notifications sent after #OnSessionEnd() is called */
14076 if (!directControl)
14077 return S_OK;
14078
14079 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14080}
14081
14082/**
14083 * @note Locks this object for reading.
14084 */
14085HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14086{
14087 LogFlowThisFunc(("\n"));
14088
14089 AutoCaller autoCaller(this);
14090 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14091
14092 ComPtr<IInternalSessionControl> directControl;
14093 {
14094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14095 if (mData->mSession.mLockType == LockType_VM)
14096 directControl = mData->mSession.mDirectControl;
14097 }
14098
14099 /* ignore notifications sent after #OnSessionEnd() is called */
14100 if (!directControl)
14101 return S_OK;
14102
14103 return directControl->OnSerialPortChange(serialPort);
14104}
14105
14106/**
14107 * @note Locks this object for reading.
14108 */
14109HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14110{
14111 LogFlowThisFunc(("\n"));
14112
14113 AutoCaller autoCaller(this);
14114 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14115
14116 ComPtr<IInternalSessionControl> directControl;
14117 {
14118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14119 if (mData->mSession.mLockType == LockType_VM)
14120 directControl = mData->mSession.mDirectControl;
14121 }
14122
14123 /* ignore notifications sent after #OnSessionEnd() is called */
14124 if (!directControl)
14125 return S_OK;
14126
14127 return directControl->OnParallelPortChange(parallelPort);
14128}
14129
14130/**
14131 * @note Locks this object for reading.
14132 */
14133HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14134{
14135 LogFlowThisFunc(("\n"));
14136
14137 AutoCaller autoCaller(this);
14138 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14139
14140 ComPtr<IInternalSessionControl> directControl;
14141 {
14142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14143 if (mData->mSession.mLockType == LockType_VM)
14144 directControl = mData->mSession.mDirectControl;
14145 }
14146
14147 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14148
14149 /* ignore notifications sent after #OnSessionEnd() is called */
14150 if (!directControl)
14151 return S_OK;
14152
14153 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14154}
14155
14156/**
14157 * @note Locks this object for reading.
14158 */
14159HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163 AutoCaller autoCaller(this);
14164 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14165
14166 ComPtr<IInternalSessionControl> directControl;
14167 {
14168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14169 if (mData->mSession.mLockType == LockType_VM)
14170 directControl = mData->mSession.mDirectControl;
14171 }
14172
14173 mParent->i_onMediumChanged(aAttachment);
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178
14179 return directControl->OnMediumChange(aAttachment, aForce);
14180}
14181
14182HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 if (mData->mSession.mLockType == LockType_VM)
14193 directControl = mData->mSession.mDirectControl;
14194 }
14195
14196 /* ignore notifications sent after #OnSessionEnd() is called */
14197 if (!directControl)
14198 return S_OK;
14199
14200 return directControl->OnVMProcessPriorityChange(aPriority);
14201}
14202
14203/**
14204 * @note Locks this object for reading.
14205 */
14206HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
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->OnCPUChange(aCPU, aRemove);
14225}
14226
14227HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14228{
14229 LogFlowThisFunc(("\n"));
14230
14231 AutoCaller autoCaller(this);
14232 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14233
14234 ComPtr<IInternalSessionControl> directControl;
14235 {
14236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14237 if (mData->mSession.mLockType == LockType_VM)
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* ignore notifications sent after #OnSessionEnd() is called */
14242 if (!directControl)
14243 return S_OK;
14244
14245 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14246}
14247
14248/**
14249 * @note Locks this object for reading.
14250 */
14251HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14252{
14253 LogFlowThisFunc(("\n"));
14254
14255 AutoCaller autoCaller(this);
14256 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14257
14258 ComPtr<IInternalSessionControl> directControl;
14259 {
14260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14261 if (mData->mSession.mLockType == LockType_VM)
14262 directControl = mData->mSession.mDirectControl;
14263 }
14264
14265 /* ignore notifications sent after #OnSessionEnd() is called */
14266 if (!directControl)
14267 return S_OK;
14268
14269 return directControl->OnVRDEServerChange(aRestart);
14270}
14271
14272/**
14273 * @note Caller needs to take the machine's lock if needed.
14274 */
14275HRESULT SessionMachine::i_onRecordingStateChange(BOOL aEnable, IProgress **aProgress)
14276{
14277 LogFlowThisFunc(("\n"));
14278
14279 AutoCaller autoCaller(this);
14280 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14281
14282 ComPtr<IInternalSessionControl> directControl;
14283 {
14284 if (mData->mSession.mLockType == LockType_VM)
14285 directControl = mData->mSession.mDirectControl;
14286 }
14287
14288 /* ignore notifications sent after #OnSessionEnd() is called */
14289 if (!directControl)
14290 return S_OK;
14291
14292 return directControl->OnRecordingStateChange(aEnable, aProgress);
14293}
14294
14295/**
14296 * @note Locks this object for reading.
14297 */
14298HRESULT SessionMachine::i_onRecordingScreenStateChange(BOOL aEnable, ULONG aScreen)
14299{
14300 LogFlowThisFunc(("\n"));
14301
14302 AutoCaller autoCaller(this);
14303 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14304
14305 ComPtr<IInternalSessionControl> directControl;
14306 {
14307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14308 if (mData->mSession.mLockType == LockType_VM)
14309 directControl = mData->mSession.mDirectControl;
14310 }
14311
14312 /* ignore notifications sent after #OnSessionEnd() is called */
14313 if (!directControl)
14314 return S_OK;
14315
14316 return directControl->OnRecordingScreenStateChange(aEnable, aScreen);
14317}
14318
14319/**
14320 * @note Locks this object for reading.
14321 */
14322HRESULT SessionMachine::i_onUSBControllerChange()
14323{
14324 LogFlowThisFunc(("\n"));
14325
14326 AutoCaller autoCaller(this);
14327 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14328
14329 ComPtr<IInternalSessionControl> directControl;
14330 {
14331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14332 if (mData->mSession.mLockType == LockType_VM)
14333 directControl = mData->mSession.mDirectControl;
14334 }
14335
14336 /* ignore notifications sent after #OnSessionEnd() is called */
14337 if (!directControl)
14338 return S_OK;
14339
14340 return directControl->OnUSBControllerChange();
14341}
14342
14343/**
14344 * @note Locks this object for reading.
14345 */
14346HRESULT SessionMachine::i_onSharedFolderChange(BOOL aGlobal)
14347{
14348 LogFlowThisFunc(("\n"));
14349
14350 AutoCaller autoCaller(this);
14351 AssertComRCReturnRC(autoCaller.hrc());
14352
14353 ComPtr<IInternalSessionControl> directControl;
14354 {
14355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14356 if (mData->mSession.mLockType == LockType_VM)
14357 directControl = mData->mSession.mDirectControl;
14358 }
14359
14360 /* ignore notifications sent after #OnSessionEnd() is called */
14361 if (!directControl)
14362 return S_OK;
14363
14364 return directControl->OnSharedFolderChange(aGlobal);
14365}
14366
14367/**
14368 * @note Locks this object for reading.
14369 */
14370HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14371{
14372 LogFlowThisFunc(("\n"));
14373
14374 AutoCaller autoCaller(this);
14375 AssertComRCReturnRC(autoCaller.hrc());
14376
14377 ComPtr<IInternalSessionControl> directControl;
14378 {
14379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14380 if (mData->mSession.mLockType == LockType_VM)
14381 directControl = mData->mSession.mDirectControl;
14382 }
14383
14384 /* ignore notifications sent after #OnSessionEnd() is called */
14385 if (!directControl)
14386 return S_OK;
14387
14388 return directControl->OnClipboardModeChange(aClipboardMode);
14389}
14390
14391/**
14392 * @note Locks this object for reading.
14393 */
14394HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14395{
14396 LogFlowThisFunc(("\n"));
14397
14398 AutoCaller autoCaller(this);
14399 AssertComRCReturnRC(autoCaller.hrc());
14400
14401 ComPtr<IInternalSessionControl> directControl;
14402 {
14403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14404 if (mData->mSession.mLockType == LockType_VM)
14405 directControl = mData->mSession.mDirectControl;
14406 }
14407
14408 /* ignore notifications sent after #OnSessionEnd() is called */
14409 if (!directControl)
14410 return S_OK;
14411
14412 return directControl->OnClipboardFileTransferModeChange(aEnable);
14413}
14414
14415/**
14416 * @note Locks this object for reading.
14417 */
14418HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14419{
14420 LogFlowThisFunc(("\n"));
14421
14422 AutoCaller autoCaller(this);
14423 AssertComRCReturnRC(autoCaller.hrc());
14424
14425 ComPtr<IInternalSessionControl> directControl;
14426 {
14427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14428 if (mData->mSession.mLockType == LockType_VM)
14429 directControl = mData->mSession.mDirectControl;
14430 }
14431
14432 /* ignore notifications sent after #OnSessionEnd() is called */
14433 if (!directControl)
14434 return S_OK;
14435
14436 return directControl->OnDnDModeChange(aDnDMode);
14437}
14438
14439/**
14440 * @note Locks this object for reading.
14441 */
14442HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14443{
14444 LogFlowThisFunc(("\n"));
14445
14446 AutoCaller autoCaller(this);
14447 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14448
14449 ComPtr<IInternalSessionControl> directControl;
14450 {
14451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14452 if (mData->mSession.mLockType == LockType_VM)
14453 directControl = mData->mSession.mDirectControl;
14454 }
14455
14456 /* ignore notifications sent after #OnSessionEnd() is called */
14457 if (!directControl)
14458 return S_OK;
14459
14460 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14461}
14462
14463/**
14464 * @note Locks this object for reading.
14465 */
14466HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14467{
14468 LogFlowThisFunc(("\n"));
14469
14470 AutoCaller autoCaller(this);
14471 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14472
14473 ComPtr<IInternalSessionControl> directControl;
14474 {
14475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14476 if (mData->mSession.mLockType == LockType_VM)
14477 directControl = mData->mSession.mDirectControl;
14478 }
14479
14480 /* ignore notifications sent after #OnSessionEnd() is called */
14481 if (!directControl)
14482 return S_OK;
14483
14484 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14485}
14486
14487/**
14488 * @note Locks this object for reading.
14489 */
14490HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14491{
14492 LogFlowThisFunc(("\n"));
14493
14494 AutoCaller autoCaller(this);
14495 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14496
14497 ComPtr<IInternalSessionControl> directControl;
14498 {
14499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14500 if (mData->mSession.mLockType == LockType_VM)
14501 directControl = mData->mSession.mDirectControl;
14502 }
14503
14504 /* ignore notifications sent after #OnSessionEnd() is called */
14505 if (!directControl)
14506 return S_OK;
14507
14508 return directControl->OnGuestDebugControlChange(guestDebugControl);
14509}
14510
14511/**
14512 * Returns @c true if this machine's USB controller reports it has a matching
14513 * filter for the given USB device and @c false otherwise.
14514 *
14515 * @note locks this object for reading.
14516 */
14517bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14518{
14519 AutoCaller autoCaller(this);
14520 /* silently return if not ready -- this method may be called after the
14521 * direct machine session has been called */
14522 if (!autoCaller.isOk())
14523 return false;
14524
14525#ifdef VBOX_WITH_USB
14526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14527
14528 switch (mData->mMachineState)
14529 {
14530 case MachineState_Starting:
14531 case MachineState_Restoring:
14532 case MachineState_TeleportingIn:
14533 case MachineState_Paused:
14534 case MachineState_Running:
14535 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14536 * elsewhere... */
14537 alock.release();
14538 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14539 default: break;
14540 }
14541#else
14542 NOREF(aDevice);
14543 NOREF(aMaskedIfs);
14544#endif
14545 return false;
14546}
14547
14548/**
14549 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14550 */
14551HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14552 IVirtualBoxErrorInfo *aError,
14553 ULONG aMaskedIfs,
14554 const com::Utf8Str &aCaptureFilename)
14555{
14556 LogFlowThisFunc(("\n"));
14557
14558 AutoCaller autoCaller(this);
14559
14560 /* This notification may happen after the machine object has been
14561 * uninitialized (the session was closed), so don't assert. */
14562 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14563
14564 ComPtr<IInternalSessionControl> directControl;
14565 {
14566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14567 if (mData->mSession.mLockType == LockType_VM)
14568 directControl = mData->mSession.mDirectControl;
14569 }
14570
14571 /* fail on notifications sent after #OnSessionEnd() is called, it is
14572 * expected by the caller */
14573 if (!directControl)
14574 return E_FAIL;
14575
14576 /* No locks should be held at this point. */
14577 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14578 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14579
14580 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14581}
14582
14583/**
14584 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14585 */
14586HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14587 IVirtualBoxErrorInfo *aError)
14588{
14589 LogFlowThisFunc(("\n"));
14590
14591 AutoCaller autoCaller(this);
14592
14593 /* This notification may happen after the machine object has been
14594 * uninitialized (the session was closed), so don't assert. */
14595 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14596
14597 ComPtr<IInternalSessionControl> directControl;
14598 {
14599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14600 if (mData->mSession.mLockType == LockType_VM)
14601 directControl = mData->mSession.mDirectControl;
14602 }
14603
14604 /* fail on notifications sent after #OnSessionEnd() is called, it is
14605 * expected by the caller */
14606 if (!directControl)
14607 return E_FAIL;
14608
14609 /* No locks should be held at this point. */
14610 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14611 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14612
14613 return directControl->OnUSBDeviceDetach(aId, aError);
14614}
14615
14616// protected methods
14617/////////////////////////////////////////////////////////////////////////////
14618
14619/**
14620 * Deletes the given file if it is no longer in use by either the current machine state
14621 * (if the machine is "saved") or any of the machine's snapshots.
14622 *
14623 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14624 * but is different for each SnapshotMachine. When calling this, the order of calling this
14625 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14626 * is therefore critical. I know, it's all rather messy.
14627 *
14628 * @param strStateFile
14629 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14630 * the test for whether the saved state file is in use.
14631 */
14632void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14633 Snapshot *pSnapshotToIgnore)
14634{
14635 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14636 if ( (strStateFile.isNotEmpty())
14637 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14638 )
14639 // ... and it must also not be shared with other snapshots
14640 if ( !mData->mFirstSnapshot
14641 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14642 // this checks the SnapshotMachine's state file paths
14643 )
14644 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
14645}
14646
14647/**
14648 * Locks the attached media.
14649 *
14650 * All attached hard disks are locked for writing and DVD/floppy are locked for
14651 * reading. Parents of attached hard disks (if any) are locked for reading.
14652 *
14653 * This method also performs accessibility check of all media it locks: if some
14654 * media is inaccessible, the method will return a failure and a bunch of
14655 * extended error info objects per each inaccessible medium.
14656 *
14657 * Note that this method is atomic: if it returns a success, all media are
14658 * locked as described above; on failure no media is locked at all (all
14659 * succeeded individual locks will be undone).
14660 *
14661 * The caller is responsible for doing the necessary state sanity checks.
14662 *
14663 * The locks made by this method must be undone by calling #unlockMedia() when
14664 * no more needed.
14665 */
14666HRESULT SessionMachine::i_lockMedia()
14667{
14668 AutoCaller autoCaller(this);
14669 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14670
14671 AutoMultiWriteLock2 alock(this->lockHandle(),
14672 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14673
14674 /* bail out if trying to lock things with already set up locking */
14675 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14676
14677 MultiResult hrcMult(S_OK);
14678
14679 /* Collect locking information for all medium objects attached to the VM. */
14680 for (MediumAttachmentList::const_iterator
14681 it = mMediumAttachments->begin();
14682 it != mMediumAttachments->end();
14683 ++it)
14684 {
14685 MediumAttachment *pAtt = *it;
14686 DeviceType_T devType = pAtt->i_getType();
14687 Medium *pMedium = pAtt->i_getMedium();
14688
14689 MediumLockList *pMediumLockList(new MediumLockList());
14690 // There can be attachments without a medium (floppy/dvd), and thus
14691 // it's impossible to create a medium lock list. It still makes sense
14692 // to have the empty medium lock list in the map in case a medium is
14693 // attached later.
14694 if (pMedium != NULL)
14695 {
14696 MediumType_T mediumType = pMedium->i_getType();
14697 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14698 || mediumType == MediumType_Shareable;
14699 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14700
14701 alock.release();
14702 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14703 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14704 false /* fMediumLockWriteAll */,
14705 NULL,
14706 *pMediumLockList);
14707 alock.acquire();
14708 if (FAILED(hrcMult))
14709 {
14710 delete pMediumLockList;
14711 mData->mSession.mLockedMedia.Clear();
14712 break;
14713 }
14714 }
14715
14716 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14717 if (FAILED(hrc))
14718 {
14719 mData->mSession.mLockedMedia.Clear();
14720 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
14721 break;
14722 }
14723 }
14724
14725 if (SUCCEEDED(hrcMult))
14726 {
14727 /* Now lock all media. If this fails, nothing is locked. */
14728 alock.release();
14729 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
14730 alock.acquire();
14731 if (FAILED(hrc))
14732 hrcMult = setError(hrc,
14733 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14734 }
14735
14736 return hrcMult;
14737}
14738
14739/**
14740 * Undoes the locks made by by #lockMedia().
14741 */
14742HRESULT SessionMachine::i_unlockMedia()
14743{
14744 AutoCaller autoCaller(this);
14745 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
14746
14747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14748
14749 /* we may be holding important error info on the current thread;
14750 * preserve it */
14751 ErrorInfoKeeper eik;
14752
14753 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
14754 AssertComRC(hrc);
14755 return hrc;
14756}
14757
14758/**
14759 * Helper to change the machine state (reimplementation).
14760 *
14761 * @note Locks this object for writing.
14762 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14763 * it can cause crashes in random places due to unexpectedly committing
14764 * the current settings. The caller is responsible for that. The call
14765 * to saveStateSettings is fine, because this method does not commit.
14766 */
14767HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14768{
14769 LogFlowThisFuncEnter();
14770
14771 AutoCaller autoCaller(this);
14772 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14773
14774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14775
14776 MachineState_T oldMachineState = mData->mMachineState;
14777
14778 AssertMsgReturn(oldMachineState != aMachineState,
14779 ("oldMachineState=%s, aMachineState=%s\n",
14780 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14781 E_FAIL);
14782
14783 HRESULT hrc = S_OK;
14784
14785 int stsFlags = 0;
14786 bool deleteSavedState = false;
14787
14788 /* detect some state transitions */
14789
14790 if ( ( ( oldMachineState == MachineState_Saved
14791 || oldMachineState == MachineState_AbortedSaved
14792 )
14793 && aMachineState == MachineState_Restoring
14794 )
14795 || ( ( oldMachineState == MachineState_PoweredOff
14796 || oldMachineState == MachineState_Teleported
14797 || oldMachineState == MachineState_Aborted
14798 )
14799 && ( aMachineState == MachineState_TeleportingIn
14800 || aMachineState == MachineState_Starting
14801 )
14802 )
14803 )
14804 {
14805 /* The EMT thread is about to start */
14806
14807 /* Nothing to do here for now... */
14808
14809 /// @todo NEWMEDIA don't let mDVDDrive and other children
14810 /// change anything when in the Starting/Restoring state
14811 }
14812 else if ( ( oldMachineState == MachineState_Running
14813 || oldMachineState == MachineState_Paused
14814 || oldMachineState == MachineState_Teleporting
14815 || oldMachineState == MachineState_OnlineSnapshotting
14816 || oldMachineState == MachineState_LiveSnapshotting
14817 || oldMachineState == MachineState_Stuck
14818 || oldMachineState == MachineState_Starting
14819 || oldMachineState == MachineState_Stopping
14820 || oldMachineState == MachineState_Saving
14821 || oldMachineState == MachineState_Restoring
14822 || oldMachineState == MachineState_TeleportingPausedVM
14823 || oldMachineState == MachineState_TeleportingIn
14824 )
14825 && ( aMachineState == MachineState_PoweredOff
14826 || aMachineState == MachineState_Saved
14827 || aMachineState == MachineState_Teleported
14828 || aMachineState == MachineState_Aborted
14829 || aMachineState == MachineState_AbortedSaved
14830 )
14831 )
14832 {
14833 /* The EMT thread has just stopped, unlock attached media. Note that as
14834 * opposed to locking that is done from Console, we do unlocking here
14835 * because the VM process may have aborted before having a chance to
14836 * properly unlock all media it locked. */
14837
14838 unlockMedia();
14839 }
14840
14841 if (oldMachineState == MachineState_Restoring)
14842 {
14843 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14844 {
14845 /*
14846 * delete the saved state file once the machine has finished
14847 * restoring from it (note that Console sets the state from
14848 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14849 * to give the user an ability to fix an error and retry --
14850 * we keep the saved state file in this case)
14851 */
14852 deleteSavedState = true;
14853 }
14854 }
14855 else if ( ( oldMachineState == MachineState_Saved
14856 || oldMachineState == MachineState_AbortedSaved
14857 )
14858 && ( aMachineState == MachineState_PoweredOff
14859 || aMachineState == MachineState_Teleported
14860 )
14861 )
14862 {
14863 /* delete the saved state after SessionMachine::discardSavedState() is called */
14864 deleteSavedState = true;
14865 mData->mCurrentStateModified = TRUE;
14866 stsFlags |= SaveSTS_CurStateModified;
14867 }
14868 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14869 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14870
14871 if ( aMachineState == MachineState_Starting
14872 || aMachineState == MachineState_Restoring
14873 || aMachineState == MachineState_TeleportingIn
14874 )
14875 {
14876 /* set the current state modified flag to indicate that the current
14877 * state is no more identical to the state in the
14878 * current snapshot */
14879 if (!mData->mCurrentSnapshot.isNull())
14880 {
14881 mData->mCurrentStateModified = TRUE;
14882 stsFlags |= SaveSTS_CurStateModified;
14883 }
14884 }
14885
14886 if (deleteSavedState)
14887 {
14888 if (mRemoveSavedState)
14889 {
14890 Assert(!mSSData->strStateFilePath.isEmpty());
14891
14892 // it is safe to delete the saved state file if ...
14893 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14894 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14895 // ... none of the snapshots share the saved state file
14896 )
14897 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
14898 }
14899
14900 mSSData->strStateFilePath.setNull();
14901 stsFlags |= SaveSTS_StateFilePath;
14902 }
14903
14904 /* redirect to the underlying peer machine */
14905 mPeer->i_setMachineState(aMachineState);
14906
14907 if ( oldMachineState != MachineState_RestoringSnapshot
14908 && ( aMachineState == MachineState_PoweredOff
14909 || aMachineState == MachineState_Teleported
14910 || aMachineState == MachineState_Aborted
14911 || aMachineState == MachineState_AbortedSaved
14912 || aMachineState == MachineState_Saved))
14913 {
14914 /* the machine has stopped execution
14915 * (or the saved state file was adopted) */
14916 stsFlags |= SaveSTS_StateTimeStamp;
14917 }
14918
14919 if ( ( oldMachineState == MachineState_PoweredOff
14920 || oldMachineState == MachineState_Aborted
14921 || oldMachineState == MachineState_Teleported
14922 )
14923 && aMachineState == MachineState_Saved)
14924 {
14925 /* the saved state file was adopted */
14926 Assert(!mSSData->strStateFilePath.isEmpty());
14927 stsFlags |= SaveSTS_StateFilePath;
14928 }
14929
14930#ifdef VBOX_WITH_GUEST_PROPS
14931 if ( aMachineState == MachineState_PoweredOff
14932 || aMachineState == MachineState_Aborted
14933 || aMachineState == MachineState_Teleported)
14934 {
14935 /* Make sure any transient guest properties get removed from the
14936 * property store on shutdown. */
14937 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14938
14939 /* remove it from the settings representation */
14940 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14941 for (settings::GuestPropertiesList::iterator
14942 it = llGuestProperties.begin();
14943 it != llGuestProperties.end();
14944 /*nothing*/)
14945 {
14946 const settings::GuestProperty &prop = *it;
14947 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14948 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14949 {
14950 it = llGuestProperties.erase(it);
14951 fNeedsSaving = true;
14952 }
14953 else
14954 {
14955 ++it;
14956 }
14957 }
14958
14959 /* Additionally remove it from the HWData representation. Required to
14960 * keep everything in sync, as this is what the API keeps using. */
14961 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14962 for (HWData::GuestPropertyMap::iterator
14963 it = llHWGuestProperties.begin();
14964 it != llHWGuestProperties.end();
14965 /*nothing*/)
14966 {
14967 uint32_t fFlags = it->second.mFlags;
14968 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14969 {
14970 /* iterator where we need to continue after the erase call
14971 * (C++03 is a fact still, and it doesn't return the iterator
14972 * which would allow continuing) */
14973 HWData::GuestPropertyMap::iterator it2 = it;
14974 ++it2;
14975 llHWGuestProperties.erase(it);
14976 it = it2;
14977 fNeedsSaving = true;
14978 }
14979 else
14980 {
14981 ++it;
14982 }
14983 }
14984
14985 if (fNeedsSaving)
14986 {
14987 mData->mCurrentStateModified = TRUE;
14988 stsFlags |= SaveSTS_CurStateModified;
14989 }
14990 }
14991#endif /* VBOX_WITH_GUEST_PROPS */
14992
14993 hrc = i_saveStateSettings(stsFlags);
14994
14995 if ( ( oldMachineState != MachineState_PoweredOff
14996 && oldMachineState != MachineState_Aborted
14997 && oldMachineState != MachineState_Teleported
14998 )
14999 && ( aMachineState == MachineState_PoweredOff
15000 || aMachineState == MachineState_Aborted
15001 || aMachineState == MachineState_Teleported
15002 )
15003 )
15004 {
15005 /* we've been shut down for any reason */
15006 /* no special action so far */
15007 }
15008
15009 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
15010 LogFlowThisFuncLeave();
15011 return hrc;
15012}
15013
15014/**
15015 * Sends the current machine state value to the VM process.
15016 *
15017 * @note Locks this object for reading, then calls a client process.
15018 */
15019HRESULT SessionMachine::i_updateMachineStateOnClient()
15020{
15021 AutoCaller autoCaller(this);
15022 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15023
15024 ComPtr<IInternalSessionControl> directControl;
15025 {
15026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15027 AssertReturn(!!mData, E_FAIL);
15028 if (mData->mSession.mLockType == LockType_VM)
15029 directControl = mData->mSession.mDirectControl;
15030
15031 /* directControl may be already set to NULL here in #OnSessionEnd()
15032 * called too early by the direct session process while there is still
15033 * some operation (like deleting the snapshot) in progress. The client
15034 * process in this case is waiting inside Session::close() for the
15035 * "end session" process object to complete, while #uninit() called by
15036 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15037 * operation to complete. For now, we accept this inconsistent behavior
15038 * and simply do nothing here. */
15039
15040 if (mData->mSession.mState == SessionState_Unlocking)
15041 return S_OK;
15042 }
15043
15044 /* ignore notifications sent after #OnSessionEnd() is called */
15045 if (!directControl)
15046 return S_OK;
15047
15048 return directControl->UpdateMachineState(mData->mMachineState);
15049}
15050
15051
15052/*static*/
15053HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15054{
15055 va_list args;
15056 va_start(args, pcszMsg);
15057 HRESULT hrc = setErrorInternalV(aResultCode,
15058 getStaticClassIID(),
15059 getStaticComponentName(),
15060 pcszMsg, args,
15061 false /* aWarning */,
15062 true /* aLogIt */);
15063 va_end(args);
15064 return hrc;
15065}
15066
15067
15068HRESULT Machine::updateState(MachineState_T aState)
15069{
15070 NOREF(aState);
15071 ReturnComNotImplemented();
15072}
15073
15074HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15075{
15076 NOREF(aProgress);
15077 ReturnComNotImplemented();
15078}
15079
15080HRESULT Machine::endPowerUp(LONG aResult)
15081{
15082 NOREF(aResult);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15087{
15088 NOREF(aProgress);
15089 ReturnComNotImplemented();
15090}
15091
15092HRESULT Machine::endPoweringDown(LONG aResult,
15093 const com::Utf8Str &aErrMsg)
15094{
15095 NOREF(aResult);
15096 NOREF(aErrMsg);
15097 ReturnComNotImplemented();
15098}
15099
15100HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15101 BOOL *aMatched,
15102 ULONG *aMaskedInterfaces)
15103{
15104 NOREF(aDevice);
15105 NOREF(aMatched);
15106 NOREF(aMaskedInterfaces);
15107 ReturnComNotImplemented();
15108
15109}
15110
15111HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15112{
15113 NOREF(aId); NOREF(aCaptureFilename);
15114 ReturnComNotImplemented();
15115}
15116
15117HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15118 BOOL aDone)
15119{
15120 NOREF(aId);
15121 NOREF(aDone);
15122 ReturnComNotImplemented();
15123}
15124
15125HRESULT Machine::autoCaptureUSBDevices()
15126{
15127 ReturnComNotImplemented();
15128}
15129
15130HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15131{
15132 NOREF(aDone);
15133 ReturnComNotImplemented();
15134}
15135
15136HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15137 ComPtr<IProgress> &aProgress)
15138{
15139 NOREF(aSession);
15140 NOREF(aProgress);
15141 ReturnComNotImplemented();
15142}
15143
15144HRESULT Machine::finishOnlineMergeMedium()
15145{
15146 ReturnComNotImplemented();
15147}
15148
15149HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15150 std::vector<com::Utf8Str> &aValues,
15151 std::vector<LONG64> &aTimestamps,
15152 std::vector<com::Utf8Str> &aFlags)
15153{
15154 NOREF(aNames);
15155 NOREF(aValues);
15156 NOREF(aTimestamps);
15157 NOREF(aFlags);
15158 ReturnComNotImplemented();
15159}
15160
15161HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15162 const com::Utf8Str &aValue,
15163 LONG64 aTimestamp,
15164 const com::Utf8Str &aFlags,
15165 BOOL fWasDeleted)
15166{
15167 NOREF(aName);
15168 NOREF(aValue);
15169 NOREF(aTimestamp);
15170 NOREF(aFlags);
15171 NOREF(fWasDeleted);
15172 ReturnComNotImplemented();
15173}
15174
15175HRESULT Machine::lockMedia()
15176{
15177 ReturnComNotImplemented();
15178}
15179
15180HRESULT Machine::unlockMedia()
15181{
15182 ReturnComNotImplemented();
15183}
15184
15185HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15186 ComPtr<IMediumAttachment> &aNewAttachment)
15187{
15188 NOREF(aAttachment);
15189 NOREF(aNewAttachment);
15190 ReturnComNotImplemented();
15191}
15192
15193HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15194 ULONG aCpuUser,
15195 ULONG aCpuKernel,
15196 ULONG aCpuIdle,
15197 ULONG aMemTotal,
15198 ULONG aMemFree,
15199 ULONG aMemBalloon,
15200 ULONG aMemShared,
15201 ULONG aMemCache,
15202 ULONG aPagedTotal,
15203 ULONG aMemAllocTotal,
15204 ULONG aMemFreeTotal,
15205 ULONG aMemBalloonTotal,
15206 ULONG aMemSharedTotal,
15207 ULONG aVmNetRx,
15208 ULONG aVmNetTx)
15209{
15210 NOREF(aValidStats);
15211 NOREF(aCpuUser);
15212 NOREF(aCpuKernel);
15213 NOREF(aCpuIdle);
15214 NOREF(aMemTotal);
15215 NOREF(aMemFree);
15216 NOREF(aMemBalloon);
15217 NOREF(aMemShared);
15218 NOREF(aMemCache);
15219 NOREF(aPagedTotal);
15220 NOREF(aMemAllocTotal);
15221 NOREF(aMemFreeTotal);
15222 NOREF(aMemBalloonTotal);
15223 NOREF(aMemSharedTotal);
15224 NOREF(aVmNetRx);
15225 NOREF(aVmNetTx);
15226 ReturnComNotImplemented();
15227}
15228
15229HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15230 com::Utf8Str &aResult)
15231{
15232 NOREF(aAuthParams);
15233 NOREF(aResult);
15234 ReturnComNotImplemented();
15235}
15236
15237HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15238{
15239 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15240
15241 AutoCaller autoCaller(this);
15242 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15243
15244 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15245 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15246 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15247 if (FAILED(hrc)) return hrc;
15248
15249 NOREF(aFlags);
15250 com::Utf8Str osTypeId;
15251 ComObjPtr<GuestOSType> osType = NULL;
15252
15253 /* Get the guest os type as a string from the VB. */
15254 hrc = getOSTypeId(osTypeId);
15255 if (FAILED(hrc)) return hrc;
15256
15257 /* Get the os type obj that coresponds, can be used to get
15258 * the defaults for this guest OS. */
15259 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15260 if (FAILED(hrc)) return hrc;
15261
15262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15263
15264 mPlatform->i_applyDefaults(osType);
15265
15266 /* This one covers IOAPICEnabled. */
15267 mFirmwareSettings->i_applyDefaults(osType);
15268
15269 /* Initialize default record settings. */
15270 mRecordingSettings->i_applyDefaults();
15271
15272 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15273 if (FAILED(hrc)) return hrc;
15274
15275 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15276 if (FAILED(hrc)) return hrc;
15277
15278 /* Graphics stuff. */
15279 GraphicsControllerType_T graphicsController;
15280 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15281 if (FAILED(hrc)) return hrc;
15282
15283 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15284 if (FAILED(hrc)) return hrc;
15285
15286 ULONG vramSize;
15287 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15288 if (FAILED(hrc)) return hrc;
15289
15290 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15291 if (FAILED(hrc)) return hrc;
15292
15293 BOOL fAccelerate2DVideoEnabled;
15294 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15295 if (FAILED(hrc)) return hrc;
15296
15297 hrc = mGraphicsAdapter->SetFeature(GraphicsFeature_Acceleration2DVideo, fAccelerate2DVideoEnabled);
15298 if (FAILED(hrc))
15299 {
15300 if (hrc != VBOX_E_NOT_SUPPORTED)
15301 return hrc;
15302 }
15303
15304 BOOL fAccelerate3DEnabled;
15305 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15306 if (FAILED(hrc)) return hrc;
15307
15308 hrc = mGraphicsAdapter->SetFeature(GraphicsFeature_Acceleration2DVideo, fAccelerate3DEnabled);
15309 if (FAILED(hrc))
15310 {
15311 if (hrc != VBOX_E_NOT_SUPPORTED)
15312 return hrc;
15313 }
15314
15315 /* Apply network adapters defaults */
15316 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15317 mNetworkAdapters[slot]->i_applyDefaults(osType);
15318
15319 /* Apply serial port defaults */
15320 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15321 mSerialPorts[slot]->i_applyDefaults(osType);
15322
15323 /* Apply parallel port defaults - not OS dependent*/
15324 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15325 mParallelPorts[slot]->i_applyDefaults();
15326
15327 /* This one covers the TPM type. */
15328 mTrustedPlatformModule->i_applyDefaults(osType);
15329
15330 /* This one covers secure boot. */
15331 hrc = mNvramStore->i_applyDefaults(osType);
15332 if (FAILED(hrc)) return hrc;
15333
15334 /* Audio stuff. */
15335 hrc = mAudioSettings->i_applyDefaults(osType);
15336 if (FAILED(hrc)) return hrc;
15337
15338 /* Storage Controllers */
15339 StorageControllerType_T hdStorageControllerType;
15340 StorageBus_T hdStorageBusType;
15341 StorageControllerType_T dvdStorageControllerType;
15342 StorageBus_T dvdStorageBusType;
15343 BOOL recommendedFloppy;
15344 ComPtr<IStorageController> floppyController;
15345 ComPtr<IStorageController> hdController;
15346 ComPtr<IStorageController> dvdController;
15347 Utf8Str strFloppyName, strDVDName, strHDName;
15348
15349 /* GUI auto generates controller names using bus type. Do the same*/
15350 strFloppyName = StorageController::i_controllerNameFromBusType(StorageBus_Floppy);
15351
15352 /* Floppy recommended? add one. */
15353 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15354 if (FAILED(hrc)) return hrc;
15355 if (recommendedFloppy)
15356 {
15357 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15358 if (FAILED(hrc)) return hrc;
15359 }
15360
15361 /* Setup one DVD storage controller. */
15362 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15363 if (FAILED(hrc)) return hrc;
15364
15365 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15366 if (FAILED(hrc)) return hrc;
15367
15368 strDVDName = StorageController::i_controllerNameFromBusType(dvdStorageBusType);
15369
15370 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15371 if (FAILED(hrc)) return hrc;
15372
15373 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15374 if (FAILED(hrc)) return hrc;
15375
15376 /* Setup one HDD storage controller. */
15377 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15378 if (FAILED(hrc)) return hrc;
15379
15380 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15381 if (FAILED(hrc)) return hrc;
15382
15383 strHDName = StorageController::i_controllerNameFromBusType(hdStorageBusType);
15384
15385 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15386 {
15387 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15388 if (FAILED(hrc)) return hrc;
15389
15390 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15391 if (FAILED(hrc)) return hrc;
15392 }
15393 else
15394 {
15395 /* The HD controller is the same as DVD: */
15396 hdController = dvdController;
15397 }
15398
15399 /* Limit the AHCI port count if it's used because windows has trouble with
15400 * too many ports and other guest (OS X in particular) may take extra long
15401 * boot: */
15402
15403 // pParent = static_cast<Medium*>(aP)
15404 IStorageController *temp = hdController;
15405 ComObjPtr<StorageController> storageController;
15406 storageController = static_cast<StorageController *>(temp);
15407
15408 // tempHDController = aHDController;
15409 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15410 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15411 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15412 storageController->COMSETTER(PortCount)(1);
15413
15414 /* VirtioSCSI configures only one port per default -- set two ports here, one for HDD and one for DVD drive. */
15415 if (hdStorageControllerType == StorageControllerType_VirtioSCSI)
15416 {
15417 hrc = storageController->COMSETTER(PortCount)(2);
15418 if (FAILED(hrc)) return hrc;
15419 }
15420
15421 /* USB stuff */
15422
15423 bool ohciEnabled = false;
15424
15425 ComPtr<IUSBController> usbController;
15426 BOOL recommendedUSB3;
15427 BOOL recommendedUSB;
15428 BOOL usbProxyAvailable;
15429
15430 getUSBProxyAvailable(&usbProxyAvailable);
15431 if (FAILED(hrc)) return hrc;
15432
15433 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15434 if (FAILED(hrc)) return hrc;
15435 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15436 if (FAILED(hrc)) return hrc;
15437
15438 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15439 {
15440 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15441 if (FAILED(hrc)) return hrc;
15442
15443 /* xHci includes OHCI */
15444 ohciEnabled = true;
15445 }
15446 if ( !ohciEnabled
15447 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15448 {
15449 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15450 if (FAILED(hrc)) return hrc;
15451 ohciEnabled = true;
15452
15453 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15454 if (FAILED(hrc)) return hrc;
15455 }
15456
15457 /* Set recommended human interface device types: */
15458 BOOL recommendedUSBHID;
15459 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15460 if (FAILED(hrc)) return hrc;
15461
15462 if (recommendedUSBHID)
15463 {
15464 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15465 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15466 if (!ohciEnabled && !usbDeviceFilters.isNull())
15467 {
15468 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15469 if (FAILED(hrc)) return hrc;
15470 }
15471 }
15472
15473 BOOL recommendedUSBTablet;
15474 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15475 if (FAILED(hrc)) return hrc;
15476
15477 if (recommendedUSBTablet)
15478 {
15479 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15480 if (!ohciEnabled && !usbDeviceFilters.isNull())
15481 {
15482 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15483 if (FAILED(hrc)) return hrc;
15484 }
15485 }
15486
15487 /* Enable the VMMDev testing feature for bootsector VMs: */
15488 if (osTypeId == GUEST_OS_ID_STR_X64("VBoxBS"))
15489 {
15490 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15491 if (FAILED(hrc))
15492 return hrc;
15493 }
15494
15495 return S_OK;
15496}
15497
15498#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15499/**
15500 * Task record for change encryption settins.
15501 */
15502class Machine::ChangeEncryptionTask
15503 : public Machine::Task
15504{
15505public:
15506 ChangeEncryptionTask(Machine *m,
15507 Progress *p,
15508 const Utf8Str &t,
15509 const com::Utf8Str &aCurrentPassword,
15510 const com::Utf8Str &aCipher,
15511 const com::Utf8Str &aNewPassword,
15512 const com::Utf8Str &aNewPasswordId,
15513 const BOOL aForce,
15514 const MediaList &llMedia)
15515 : Task(m, p, t),
15516 mstrNewPassword(aNewPassword),
15517 mstrCurrentPassword(aCurrentPassword),
15518 mstrCipher(aCipher),
15519 mstrNewPasswordId(aNewPasswordId),
15520 mForce(aForce),
15521 mllMedia(llMedia),
15522 m_pCryptoIf(NULL)
15523 {}
15524
15525 ~ChangeEncryptionTask()
15526 {
15527 if (mstrNewPassword.length())
15528 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15529 if (mstrCurrentPassword.length())
15530 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15531 if (m_pCryptoIf)
15532 {
15533 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15534 m_pCryptoIf = NULL;
15535 }
15536 }
15537
15538 Utf8Str mstrNewPassword;
15539 Utf8Str mstrCurrentPassword;
15540 Utf8Str mstrCipher;
15541 Utf8Str mstrNewPasswordId;
15542 BOOL mForce;
15543 MediaList mllMedia;
15544 PCVBOXCRYPTOIF m_pCryptoIf;
15545private:
15546 void handler()
15547 {
15548 try
15549 {
15550 m_pMachine->i_changeEncryptionHandler(*this);
15551 }
15552 catch (...)
15553 {
15554 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
15555 }
15556 }
15557
15558 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
15559};
15560
15561/**
15562 * Scans specified directory and fills list by files found
15563 *
15564 * @returns VBox status code.
15565 * @param lstFiles
15566 * @param strDir
15567 * @param filePattern
15568 */
15569int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
15570 const com::Utf8Str &strPattern)
15571{
15572 /* To get all entries including subdirectories. */
15573 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
15574 if (!pszFilePattern)
15575 return VERR_NO_STR_MEMORY;
15576
15577 PRTDIRENTRYEX pDirEntry = NULL;
15578 RTDIR hDir;
15579 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
15580 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
15581 if (RT_SUCCESS(vrc))
15582 {
15583 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
15584 if (pDirEntry)
15585 {
15586 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
15587 != VERR_NO_MORE_FILES)
15588 {
15589 char *pszFilePath = NULL;
15590
15591 if (vrc == VERR_BUFFER_OVERFLOW)
15592 {
15593 /* allocate new buffer. */
15594 RTMemFree(pDirEntry);
15595 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
15596 if (!pDirEntry)
15597 {
15598 vrc = VERR_NO_MEMORY;
15599 break;
15600 }
15601 /* Retry. */
15602 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
15603 if (RT_FAILURE(vrc))
15604 break;
15605 }
15606 else if (RT_FAILURE(vrc))
15607 break;
15608
15609 /* Exclude . and .. */
15610 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
15611 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
15612 continue;
15613 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
15614 {
15615 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15616 if (!pszSubDirPath)
15617 {
15618 vrc = VERR_NO_STR_MEMORY;
15619 break;
15620 }
15621 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
15622 RTMemFree(pszSubDirPath);
15623 if (RT_FAILURE(vrc))
15624 break;
15625 continue;
15626 }
15627
15628 /* We got the new entry. */
15629 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
15630 continue;
15631
15632 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
15633 continue;
15634
15635 /* Prepend the path to the libraries. */
15636 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15637 if (!pszFilePath)
15638 {
15639 vrc = VERR_NO_STR_MEMORY;
15640 break;
15641 }
15642
15643 lstFiles.push_back(pszFilePath);
15644 RTStrFree(pszFilePath);
15645 }
15646
15647 RTMemFree(pDirEntry);
15648 }
15649 else
15650 vrc = VERR_NO_MEMORY;
15651
15652 RTDirClose(hDir);
15653 }
15654 else
15655 {
15656 /* On Windows the above immediately signals that there are no
15657 * files matching, while on other platforms enumerating the
15658 * files below fails. Either way: stop searching. */
15659 }
15660
15661 if ( vrc == VERR_NO_MORE_FILES
15662 || vrc == VERR_FILE_NOT_FOUND
15663 || vrc == VERR_PATH_NOT_FOUND)
15664 vrc = VINF_SUCCESS;
15665 RTStrFree(pszFilePattern);
15666 return vrc;
15667}
15668
15669/**
15670 * Helper to set up an I/O stream to read or write a possibly encrypted file.
15671 *
15672 * @returns VBox status code.
15673 * @param pszFilename The file to open.
15674 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
15675 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
15676 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
15677 * @param fOpen The open flags for the file.
15678 * @param phVfsIos Where to store the handle to the I/O stream on success.
15679 */
15680int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
15681 const char *pszKeyStore, const char *pszPassword,
15682 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
15683{
15684 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
15685 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
15686 if (RT_SUCCESS(vrc))
15687 {
15688 if (pCryptoIf)
15689 {
15690 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
15691 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
15692 if (RT_SUCCESS(vrc))
15693 {
15694 RTVfsFileRelease(hVfsFile);
15695 hVfsFile = hVfsFileCrypto;
15696 }
15697 }
15698
15699 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
15700 RTVfsFileRelease(hVfsFile);
15701 }
15702
15703 return vrc;
15704}
15705
15706/**
15707 * Helper function processing all actions for one component (saved state files,
15708 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
15709 *
15710 * @param task
15711 * @param strDirectory
15712 * @param strFilePattern
15713 * @param strMagic
15714 * @param strKeyStore
15715 * @param strKeyId
15716 * @return
15717 */
15718HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
15719 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
15720 com::Utf8Str &strKeyId, int iCipherMode)
15721{
15722 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
15723 && task.mstrCipher.isEmpty()
15724 && task.mstrNewPassword.isEmpty()
15725 && task.mstrNewPasswordId.isEmpty();
15726 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
15727 && task.mstrCipher.isNotEmpty()
15728 && task.mstrNewPassword.isNotEmpty()
15729 && task.mstrNewPasswordId.isNotEmpty();
15730
15731 /* check if the cipher is changed which causes the reencryption*/
15732
15733 const char *pszTaskCipher = NULL;
15734 if (task.mstrCipher.isNotEmpty())
15735 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
15736
15737 if (!task.mForce && !fDecrypt && !fEncrypt)
15738 {
15739 char *pszCipher = NULL;
15740 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
15741 NULL /*pszPassword*/,
15742 NULL /*ppbKey*/,
15743 NULL /*pcbKey*/,
15744 &pszCipher);
15745 if (RT_SUCCESS(vrc))
15746 {
15747 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
15748 RTMemFree(pszCipher);
15749 }
15750 else
15751 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
15752 strFilePattern.c_str(), vrc);
15753 }
15754
15755 /* Only the password needs to be changed */
15756 if (!task.mForce && !fDecrypt && !fEncrypt)
15757 {
15758 Assert(task.m_pCryptoIf);
15759
15760 VBOXCRYPTOCTX hCryptoCtx;
15761 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
15762 if (RT_FAILURE(vrc))
15763 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
15764 strFilePattern.c_str(), vrc);
15765 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15766 if (RT_FAILURE(vrc))
15767 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
15768 strFilePattern.c_str(), vrc);
15769
15770 char *pszKeyStore = NULL;
15771 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15772 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15773 if (RT_FAILURE(vrc))
15774 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
15775 strFilePattern.c_str(), vrc);
15776 strKeyStore = pszKeyStore;
15777 RTMemFree(pszKeyStore);
15778 strKeyId = task.mstrNewPasswordId;
15779 return S_OK;
15780 }
15781
15782 /* Reencryption required */
15783 HRESULT hrc = S_OK;
15784 int vrc = VINF_SUCCESS;
15785
15786 std::list<com::Utf8Str> lstFiles;
15787 if (SUCCEEDED(hrc))
15788 {
15789 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
15790 if (RT_FAILURE(vrc))
15791 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
15792 }
15793 com::Utf8Str strNewKeyStore;
15794 if (SUCCEEDED(hrc))
15795 {
15796 if (!fDecrypt)
15797 {
15798 VBOXCRYPTOCTX hCryptoCtx;
15799 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
15800 if (RT_FAILURE(vrc))
15801 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
15802 strFilePattern.c_str(), vrc);
15803
15804 char *pszKeyStore = NULL;
15805 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15806 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15807 if (RT_FAILURE(vrc))
15808 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
15809 strFilePattern.c_str(), vrc);
15810 strNewKeyStore = pszKeyStore;
15811 RTMemFree(pszKeyStore);
15812 }
15813
15814 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15815 it != lstFiles.end();
15816 ++it)
15817 {
15818 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
15819 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
15820
15821 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
15822 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
15823
15824 vrc = i_createIoStreamForFile((*it).c_str(),
15825 fEncrypt ? NULL : task.m_pCryptoIf,
15826 fEncrypt ? NULL : strKeyStore.c_str(),
15827 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
15828 fOpenForRead, &hVfsIosOld);
15829 if (RT_SUCCESS(vrc))
15830 {
15831 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
15832 fDecrypt ? NULL : task.m_pCryptoIf,
15833 fDecrypt ? NULL : strNewKeyStore.c_str(),
15834 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
15835 fOpenForWrite, &hVfsIosNew);
15836 if (RT_FAILURE(vrc))
15837 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
15838 (*it + ".tmp").c_str(), vrc);
15839 }
15840 else
15841 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
15842
15843 if (RT_SUCCESS(vrc))
15844 {
15845 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
15846 if (RT_FAILURE(vrc))
15847 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
15848 (*it).c_str(), vrc);
15849 }
15850
15851 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
15852 RTVfsIoStrmRelease(hVfsIosOld);
15853 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
15854 RTVfsIoStrmRelease(hVfsIosNew);
15855 }
15856 }
15857
15858 if (SUCCEEDED(hrc))
15859 {
15860 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15861 it != lstFiles.end();
15862 ++it)
15863 {
15864 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
15865 if (RT_FAILURE(vrc))
15866 {
15867 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
15868 break;
15869 }
15870 }
15871 }
15872
15873 if (SUCCEEDED(hrc))
15874 {
15875 strKeyStore = strNewKeyStore;
15876 strKeyId = task.mstrNewPasswordId;
15877 }
15878
15879 return hrc;
15880}
15881
15882/**
15883 * Task thread implementation for Machine::changeEncryption(), called from
15884 * Machine::taskHandler().
15885 *
15886 * @note Locks this object for writing.
15887 *
15888 * @param task
15889 * @return
15890 */
15891void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
15892{
15893 LogFlowThisFuncEnter();
15894
15895 AutoCaller autoCaller(this);
15896 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
15897 if (FAILED(autoCaller.hrc()))
15898 {
15899 /* we might have been uninitialized because the session was accidentally
15900 * closed by the client, so don't assert */
15901 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
15902 task.m_pProgress->i_notifyComplete(hrc);
15903 LogFlowThisFuncLeave();
15904 return;
15905 }
15906
15907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15908
15909 HRESULT hrc = S_OK;
15910 com::Utf8Str strOldKeyId = mData->mstrKeyId;
15911 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
15912 try
15913 {
15914 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
15915 if (FAILED(hrc))
15916 throw hrc;
15917
15918 if (task.mstrCurrentPassword.isEmpty())
15919 {
15920 if (mData->mstrKeyStore.isNotEmpty())
15921 throw setError(VBOX_E_PASSWORD_INCORRECT,
15922 tr("The password given for the encrypted VM is incorrect"));
15923 }
15924 else
15925 {
15926 if (mData->mstrKeyStore.isEmpty())
15927 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15928 tr("The VM is not configured for encryption"));
15929 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
15930 if (hrc == VBOX_E_PASSWORD_INCORRECT)
15931 throw setError(VBOX_E_PASSWORD_INCORRECT,
15932 tr("The password to decrypt the VM is incorrect"));
15933 }
15934
15935 if (task.mstrCipher.isNotEmpty())
15936 {
15937 if ( task.mstrNewPassword.isEmpty()
15938 && task.mstrNewPasswordId.isEmpty()
15939 && task.mstrCurrentPassword.isNotEmpty())
15940 {
15941 /* An empty password and password ID will default to the current password. */
15942 task.mstrNewPassword = task.mstrCurrentPassword;
15943 }
15944 else if (task.mstrNewPassword.isEmpty())
15945 throw setError(VBOX_E_OBJECT_NOT_FOUND,
15946 tr("A password must be given for the VM encryption"));
15947 else if (task.mstrNewPasswordId.isEmpty())
15948 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15949 tr("A valid identifier for the password must be given"));
15950 }
15951 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
15952 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15953 tr("The password and password identifier must be empty if the output should be unencrypted"));
15954
15955 /*
15956 * Save config.
15957 * Must be first operation to prevent making encrypted copies
15958 * for old version of the config file.
15959 */
15960 int fSave = Machine::SaveS_Force;
15961 if (task.mstrNewPassword.isNotEmpty())
15962 {
15963 VBOXCRYPTOCTX hCryptoCtx;
15964
15965 int vrc = VINF_SUCCESS;
15966 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
15967 {
15968 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
15969 task.mstrNewPassword.c_str(), &hCryptoCtx);
15970 if (RT_FAILURE(vrc))
15971 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
15972 }
15973 else
15974 {
15975 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
15976 task.mstrCurrentPassword.c_str(),
15977 &hCryptoCtx);
15978 if (RT_FAILURE(vrc))
15979 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
15980 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15981 if (RT_FAILURE(vrc))
15982 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
15983 }
15984
15985 char *pszKeyStore;
15986 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15987 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15988 if (RT_FAILURE(vrc))
15989 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
15990 mData->mstrKeyStore = pszKeyStore;
15991 RTStrFree(pszKeyStore);
15992 mData->mstrKeyId = task.mstrNewPasswordId;
15993 size_t cbPassword = task.mstrNewPassword.length() + 1;
15994 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
15995 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
15996 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
15997 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
15998
15999 /*
16000 * Remove backuped config after saving because it can contain
16001 * unencrypted version of the config
16002 */
16003 fSave |= Machine::SaveS_RemoveBackup;
16004 }
16005 else
16006 {
16007 mData->mstrKeyId.setNull();
16008 mData->mstrKeyStore.setNull();
16009 }
16010
16011 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16012 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16013 Bstr bstrNewPassword(task.mstrNewPassword);
16014 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16015 /* encrypt media */
16016 alock.release();
16017 for (MediaList::iterator it = task.mllMedia.begin();
16018 it != task.mllMedia.end();
16019 ++it)
16020 {
16021 ComPtr<IProgress> pProgress1;
16022 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16023 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16024 pProgress1.asOutParam());
16025 if (FAILED(hrc)) throw hrc;
16026 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16027 if (FAILED(hrc)) throw hrc;
16028 }
16029 alock.acquire();
16030
16031 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16032
16033 Utf8Str strFullSnapshotFolder;
16034 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16035
16036 /* .sav files (main and snapshots) */
16037 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16038 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16039 if (FAILED(hrc))
16040 /* the helper function already sets error object */
16041 throw hrc;
16042
16043 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16044
16045 /* .nvram files */
16046 com::Utf8Str strNVRAMKeyId;
16047 com::Utf8Str strNVRAMKeyStore;
16048 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16049 if (FAILED(hrc))
16050 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16051
16052 Utf8Str strMachineFolder;
16053 i_calculateFullPath(".", strMachineFolder);
16054
16055 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16056 if (FAILED(hrc))
16057 /* the helper function already sets error object */
16058 throw hrc;
16059
16060 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16061 if (FAILED(hrc))
16062 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16063
16064 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16065
16066 /* .log files */
16067 com::Utf8Str strLogFolder;
16068 i_getLogFolder(strLogFolder);
16069 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16070 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16071 if (FAILED(hrc))
16072 /* the helper function already sets error object */
16073 throw hrc;
16074
16075 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16076
16077 i_saveSettings(NULL, alock, fSave);
16078 }
16079 catch (HRESULT hrcXcpt)
16080 {
16081 hrc = hrcXcpt;
16082 mData->mstrKeyId = strOldKeyId;
16083 mData->mstrKeyStore = strOldKeyStore;
16084 }
16085
16086 task.m_pProgress->i_notifyComplete(hrc);
16087
16088 LogFlowThisFuncLeave();
16089}
16090#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16091
16092HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16093 const com::Utf8Str &aCipher,
16094 const com::Utf8Str &aNewPassword,
16095 const com::Utf8Str &aNewPasswordId,
16096 BOOL aForce,
16097 ComPtr<IProgress> &aProgress)
16098{
16099 LogFlowFuncEnter();
16100
16101#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16102 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16103 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16104#else
16105 /* make the VM accessible */
16106 if (!mData->mAccessible)
16107 {
16108 if ( aCurrentPassword.isEmpty()
16109 || mData->mstrKeyId.isEmpty())
16110 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16111
16112 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16113 if (FAILED(hrc))
16114 return hrc;
16115 }
16116
16117 AutoLimitedCaller autoCaller(this);
16118 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16119
16120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16121
16122 /* define media to be change encryption */
16123
16124 MediaList llMedia;
16125 for (MediumAttachmentList::iterator
16126 it = mMediumAttachments->begin();
16127 it != mMediumAttachments->end();
16128 ++it)
16129 {
16130 ComObjPtr<MediumAttachment> &pAttach = *it;
16131 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16132
16133 if (!pMedium.isNull())
16134 {
16135 AutoCaller mac(pMedium);
16136 if (FAILED(mac.hrc())) return mac.hrc();
16137 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16138 DeviceType_T devType = pMedium->i_getDeviceType();
16139 if (devType == DeviceType_HardDisk)
16140 {
16141 /*
16142 * We need to move to last child because the Medium::changeEncryption
16143 * encrypts all chain of specified medium with its parents.
16144 * Also we perform cheking of back reference and children for
16145 * all media in the chain to raise error before we start any action.
16146 * So, we first move into root parent and then we will move to last child
16147 * keeping latter in the list for encryption.
16148 */
16149
16150 /* move to root parent */
16151 ComObjPtr<Medium> pTmpMedium = pMedium;
16152 while (pTmpMedium.isNotNull())
16153 {
16154 AutoCaller mediumAC(pTmpMedium);
16155 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16156 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16157
16158 /* Cannot encrypt media which are attached to more than one virtual machine. */
16159 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16160 if (cBackRefs > 1)
16161 return setError(VBOX_E_INVALID_OBJECT_STATE,
16162 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16163 pTmpMedium->i_getName().c_str(), cBackRefs);
16164
16165 size_t cChildren = pTmpMedium->i_getChildren().size();
16166 if (cChildren > 1)
16167 return setError(VBOX_E_INVALID_OBJECT_STATE,
16168 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16169 pTmpMedium->i_getName().c_str(), cChildren);
16170
16171 pTmpMedium = pTmpMedium->i_getParent();
16172 }
16173 /* move to last child */
16174 pTmpMedium = pMedium;
16175 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16176 {
16177 AutoCaller mediumAC(pTmpMedium);
16178 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16179 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16180
16181 /* Cannot encrypt media which are attached to more than one virtual machine. */
16182 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16183 if (cBackRefs > 1)
16184 return setError(VBOX_E_INVALID_OBJECT_STATE,
16185 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16186 pTmpMedium->i_getName().c_str(), cBackRefs);
16187
16188 size_t cChildren = pTmpMedium->i_getChildren().size();
16189 if (cChildren > 1)
16190 return setError(VBOX_E_INVALID_OBJECT_STATE,
16191 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16192 pTmpMedium->i_getName().c_str(), cChildren);
16193
16194 pTmpMedium = pTmpMedium->i_getChildren().front();
16195 }
16196 llMedia.push_back(pTmpMedium);
16197 }
16198 }
16199 }
16200
16201 ComObjPtr<Progress> pProgress;
16202 pProgress.createObject();
16203 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16204 static_cast<IMachine*>(this) /* aInitiator */,
16205 tr("Change encryption"),
16206 TRUE /* fCancellable */,
16207 (ULONG)(4 + + llMedia.size()), // cOperations
16208 tr("Change encryption of the mediuma"));
16209 if (FAILED(hrc))
16210 return hrc;
16211
16212 /* create and start the task on a separate thread (note that it will not
16213 * start working until we release alock) */
16214 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16215 aCurrentPassword, aCipher, aNewPassword,
16216 aNewPasswordId, aForce, llMedia);
16217 hrc = pTask->createThread();
16218 pTask = NULL;
16219 if (FAILED(hrc))
16220 return hrc;
16221
16222 pProgress.queryInterfaceTo(aProgress.asOutParam());
16223
16224 LogFlowFuncLeave();
16225
16226 return S_OK;
16227#endif
16228}
16229
16230HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16231 com::Utf8Str &aPasswordId)
16232{
16233#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16234 RT_NOREF(aCipher, aPasswordId);
16235 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16236#else
16237 AutoLimitedCaller autoCaller(this);
16238 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16239
16240 PCVBOXCRYPTOIF pCryptoIf = NULL;
16241 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16242 if (FAILED(hrc)) return hrc; /* Error is set */
16243
16244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16245
16246 if (mData->mstrKeyStore.isNotEmpty())
16247 {
16248 char *pszCipher = NULL;
16249 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16250 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16251 if (RT_SUCCESS(vrc))
16252 {
16253 aCipher = getCipherStringWithoutMode(pszCipher);
16254 RTStrFree(pszCipher);
16255 aPasswordId = mData->mstrKeyId;
16256 }
16257 else
16258 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16259 tr("Failed to query the encryption settings with %Rrc"),
16260 vrc);
16261 }
16262 else
16263 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16264
16265 mParent->i_releaseCryptoIf(pCryptoIf);
16266
16267 return hrc;
16268#endif
16269}
16270
16271HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16272{
16273#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16274 RT_NOREF(aPassword);
16275 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16276#else
16277 AutoLimitedCaller autoCaller(this);
16278 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16279
16280 PCVBOXCRYPTOIF pCryptoIf = NULL;
16281 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16282 if (FAILED(hrc)) return hrc; /* Error is set */
16283
16284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16285
16286 if (mData->mstrKeyStore.isNotEmpty())
16287 {
16288 char *pszCipher = NULL;
16289 uint8_t *pbDek = NULL;
16290 size_t cbDek = 0;
16291 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16292 &pbDek, &cbDek, &pszCipher);
16293 if (RT_SUCCESS(vrc))
16294 {
16295 RTStrFree(pszCipher);
16296 RTMemSaferFree(pbDek, cbDek);
16297 }
16298 else
16299 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16300 tr("The password supplied for the encrypted machine is incorrect"));
16301 }
16302 else
16303 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16304
16305 mParent->i_releaseCryptoIf(pCryptoIf);
16306
16307 return hrc;
16308#endif
16309}
16310
16311HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16312 const com::Utf8Str &aPassword)
16313{
16314#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16315 RT_NOREF(aId, aPassword);
16316 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16317#else
16318 AutoLimitedCaller autoCaller(this);
16319 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16320
16321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16322
16323 size_t cbPassword = aPassword.length() + 1;
16324 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16325
16326 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16327
16328 if ( mData->mAccessible
16329 && mData->mSession.mState == SessionState_Locked
16330 && mData->mSession.mLockType == LockType_VM
16331 && mData->mSession.mDirectControl != NULL)
16332 {
16333 /* get the console from the direct session */
16334 ComPtr<IConsole> console;
16335 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16336 ComAssertComRC(hrc);
16337 /* send passsword to console */
16338 console->AddEncryptionPassword(Bstr(aId).raw(),
16339 Bstr(aPassword).raw(),
16340 TRUE);
16341 }
16342
16343 if (mData->mstrKeyId == aId)
16344 {
16345 HRESULT hrc = checkEncryptionPassword(aPassword);
16346 if (FAILED(hrc))
16347 return hrc;
16348
16349 if (SUCCEEDED(hrc))
16350 {
16351 /*
16352 * Encryption is used and password is correct,
16353 * Reinit the machine if required.
16354 */
16355 BOOL fAccessible;
16356 alock.release();
16357 getAccessible(&fAccessible);
16358 alock.acquire();
16359 }
16360 }
16361
16362 /*
16363 * Add the password into the NvramStore only after
16364 * the machine becomes accessible and the NvramStore
16365 * contains key id and key store.
16366 */
16367 if (mNvramStore.isNotNull())
16368 mNvramStore->i_addPassword(aId, aPassword);
16369
16370 return S_OK;
16371#endif
16372}
16373
16374HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16375 const std::vector<com::Utf8Str> &aPasswords)
16376{
16377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16378 RT_NOREF(aIds, aPasswords);
16379 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16380#else
16381 if (aIds.size() != aPasswords.size())
16382 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16383
16384 HRESULT hrc = S_OK;
16385 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16386 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16387
16388 return hrc;
16389#endif
16390}
16391
16392HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16393{
16394#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16395 RT_NOREF(autoCaller, aId);
16396 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16397#else
16398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16399
16400 if ( mData->mAccessible
16401 && mData->mSession.mState == SessionState_Locked
16402 && mData->mSession.mLockType == LockType_VM
16403 && mData->mSession.mDirectControl != NULL)
16404 {
16405 /* get the console from the direct session */
16406 ComPtr<IConsole> console;
16407 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16408 ComAssertComRC(hrc);
16409 /* send passsword to console */
16410 console->RemoveEncryptionPassword(Bstr(aId).raw());
16411 }
16412
16413 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16414 {
16415 if (Global::IsOnlineOrTransient(mData->mMachineState))
16416 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16417 alock.release();
16418 autoCaller.release();
16419 /* return because all passwords are purged when machine becomes inaccessible; */
16420 return i_setInaccessible();
16421 }
16422
16423 if (mNvramStore.isNotNull())
16424 mNvramStore->i_removePassword(aId);
16425 mData->mpKeyStore->deleteSecretKey(aId);
16426 return S_OK;
16427#endif
16428}
16429
16430HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16431{
16432#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16433 RT_NOREF(autoCaller);
16434 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16435#else
16436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16437
16438 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16439 {
16440 if (Global::IsOnlineOrTransient(mData->mMachineState))
16441 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16442 alock.release();
16443 autoCaller.release();
16444 /* return because all passwords are purged when machine becomes inaccessible; */
16445 return i_setInaccessible();
16446 }
16447
16448 mNvramStore->i_removeAllPasswords();
16449 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16450 return S_OK;
16451#endif
16452}
16453
16454#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16455HRESULT Machine::i_setInaccessible()
16456{
16457 if (!mData->mAccessible)
16458 return S_OK;
16459
16460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16461 VirtualBox *pParent = mParent;
16462 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16463 Guid id(i_getId());
16464
16465 alock.release();
16466
16467 uninit();
16468 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16469
16470 alock.acquire();
16471 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16472 return hrc;
16473}
16474#endif
16475
16476/* This isn't handled entirely by the wrapper generator yet. */
16477#ifdef VBOX_WITH_XPCOM
16478NS_DECL_CLASSINFO(SessionMachine)
16479NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16480
16481NS_DECL_CLASSINFO(SnapshotMachine)
16482NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16483#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