VirtualBox

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

Last change on this file since 107262 was 107262, checked in by vboxsync, 6 weeks ago

bugref:10806. Set the idle time for Medium and Machine tracked objects to 2 hours.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 584.3 KB
Line 
1/* $Id: MachineImpl.cpp 107262 2024-12-09 15:22:48Z 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.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "StorageControllerImpl.h"
61#include "MachineImplMoveVM.h"
62#include "ExtPackManagerImpl.h"
63#include "MachineLaunchVMCommonWorker.h"
64#include "CryptoUtils.h"
65
66// generated header
67#include "VBoxEvents.h"
68
69#ifdef VBOX_WITH_USB
70# include "USBProxyService.h"
71#endif
72
73#include "AutoCaller.h"
74#include "HashedPw.h"
75#include "Performance.h"
76#include "StringifyEnums.h"
77
78#include <iprt/asm.h>
79#include <iprt/path.h>
80#include <iprt/dir.h>
81#include <iprt/env.h>
82#include <iprt/lockvalidator.h>
83#include <iprt/memsafer.h>
84#include <iprt/process.h>
85#include <iprt/cpp/utils.h>
86#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
87#include <iprt/sha.h>
88#include <iprt/string.h>
89#include <iprt/ctype.h>
90
91#include <VBox/com/array.h>
92#include <VBox/com/list.h>
93#include <VBox/VBoxCryptoIf.h>
94
95#include <VBox/err.h>
96#include <VBox/param.h>
97#include <VBox/settings.h>
98#include <VBox/VMMDev.h>
99#include <VBox/vmm/ssm.h>
100
101#ifdef VBOX_WITH_GUEST_PROPS
102# include <VBox/HostServices/GuestPropertySvc.h>
103# include <VBox/com/array.h>
104#endif
105
106#ifdef VBOX_WITH_SHARED_CLIPBOARD
107# include <VBox/HostServices/VBoxClipboardSvc.h>
108#endif
109
110#include "VBox/com/MultiResult.h"
111
112#include <algorithm>
113
114#ifdef VBOX_WITH_DTRACE_R3_MAIN
115# include "dtrace/VBoxAPI.h"
116#endif
117
118#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
119# define HOSTSUFF_EXE ".exe"
120#else /* !RT_OS_WINDOWS */
121# define HOSTSUFF_EXE ""
122#endif /* !RT_OS_WINDOWS */
123
124// defines / prototypes
125/////////////////////////////////////////////////////////////////////////////
126
127#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
128# define BUF_DATA_SIZE _64K
129
130enum CipherMode
131{
132 CipherModeGcm = 0,
133 CipherModeCtr,
134 CipherModeXts,
135 CipherModeMax
136};
137
138enum AesSize
139{
140 Aes128 = 0,
141 Aes256,
142 AesMax
143};
144
145const char *g_apszCipher[AesMax][CipherModeMax] =
146{
147 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
148 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
149};
150const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
151
152static const char *getCipherString(const char *pszAlgo, const int iMode)
153{
154 if (iMode >= CipherModeMax)
155 return pszAlgo;
156
157 for (int i = 0; i < AesMax; i++)
158 {
159 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
160 return g_apszCipher[i][iMode];
161 }
162 return pszAlgo;
163}
164
165static const char *getCipherStringWithoutMode(const char *pszAlgo)
166{
167 for (int i = 0; i < AesMax; i++)
168 {
169 for (int j = 0; j < CipherModeMax; j++)
170 {
171 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
172 return g_apszCipherAlgo[i];
173 }
174 }
175 return pszAlgo;
176}
177#endif
178
179/////////////////////////////////////////////////////////////////////////////
180// Machine::Data structure
181/////////////////////////////////////////////////////////////////////////////
182
183Machine::Data::Data()
184{
185 mRegistered = FALSE;
186 pMachineConfigFile = NULL;
187 /* Contains hints on what has changed when the user is using the VM (config
188 * changes, running the VM, ...). This is used to decide if a config needs
189 * to be written to disk. */
190 flModifications = 0;
191 /* VM modification usually also trigger setting the current state to
192 * "Modified". Although this is not always the case. An e.g. is the VM
193 * initialization phase or when snapshot related data is changed. The
194 * actually behavior is controlled by the following flag. */
195 m_fAllowStateModification = false;
196 mAccessible = FALSE;
197 /* mUuid is initialized in Machine::init() */
198
199 mMachineState = MachineState_PoweredOff;
200 RTTimeNow(&mLastStateChange);
201
202 mMachineStateDeps = 0;
203 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
204 mMachineStateChangePending = 0;
205
206 mCurrentStateModified = TRUE;
207 mGuestPropertiesModified = FALSE;
208
209 mSession.mPID = NIL_RTPROCESS;
210 mSession.mLockType = LockType_Null;
211 mSession.mState = SessionState_Unlocked;
212
213#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
214 mpKeyStore = NULL;
215#endif
216}
217
218Machine::Data::~Data()
219{
220 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
221 {
222 RTSemEventMultiDestroy(mMachineStateDepsSem);
223 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
224 }
225 if (pMachineConfigFile)
226 {
227 delete pMachineConfigFile;
228 pMachineConfigFile = NULL;
229 }
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine::HWData structure
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::HWData::HWData()
237{
238 /* default values for a newly created machine for x86. */
239 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
240 mMemorySize = 128;
241 mCPUCount = 1;
242 mCPUHotPlugEnabled = false;
243 mMemoryBalloonSize = 0;
244 mPageFusionEnabled = false;
245 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
246 mCpuIdPortabilityLevel = 0;
247 mCpuProfile = "host";
248
249 /* default boot order: floppy - DVD - HDD */
250 mBootOrder[0] = DeviceType_Floppy;
251 mBootOrder[1] = DeviceType_DVD;
252 mBootOrder[2] = DeviceType_HardDisk;
253 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
254 mBootOrder[i] = DeviceType_Null;
255
256 mClipboardMode = ClipboardMode_Disabled;
257 mClipboardFileTransfersEnabled = FALSE;
258
259 mDnDMode = DnDMode_Disabled;
260
261 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
262 mPointingHIDType = PointingHIDType_PS2Mouse;
263 mParavirtProvider = ParavirtProvider_Default;
264 mEmulatedUSBCardReaderEnabled = FALSE;
265
266 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
267 mCPUAttached[i] = false;
268
269 mIOCacheEnabled = true;
270 mIOCacheSize = 5; /* 5MB */
271}
272
273Machine::HWData::~HWData()
274{
275}
276
277/////////////////////////////////////////////////////////////////////////////
278// Machine class
279/////////////////////////////////////////////////////////////////////////////
280
281// constructor / destructor
282/////////////////////////////////////////////////////////////////////////////
283
284Machine::Machine() :
285#ifdef VBOX_WITH_RESOURCE_USAGE_API
286 mCollectorGuest(NULL),
287#endif
288 mPeer(NULL),
289 mParent(NULL),
290 mSerialPorts(),
291 mParallelPorts(),
292 uRegistryNeedsSaving(0)
293{}
294
295Machine::~Machine()
296{}
297
298HRESULT Machine::FinalConstruct()
299{
300 LogFlowThisFunc(("\n"));
301 setTracked(0, 7200);//infinite, 2 hours
302 return BaseFinalConstruct();
303}
304
305void Machine::FinalRelease()
306{
307 LogFlowThisFunc(("\n"));
308 uninit();
309 BaseFinalRelease();
310}
311
312/**
313 * Initializes a new machine instance; this init() variant creates a new, empty machine.
314 * This gets called from VirtualBox::CreateMachine().
315 *
316 * @param aParent Associated parent object.
317 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
318 * @param strName Name for the machine.
319 * @param aArchitecture Architecture to use for the machine.
320 * If a valid guest OS type is set via \a aOsType, the guest OS' type will be used instead then.
321 * @param llGroups list of groups for the machine.
322 * @param strOsType OS Type string (stored as is if aOsType is NULL).
323 * @param aOsType OS Type of this machine or NULL.
324 * @param aId UUID for the new machine.
325 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
326 * @param fDirectoryIncludesUUID
327 * Whether the use a special VM directory naming scheme (includes the UUID).
328 * @param aCipher The cipher to encrypt the VM with.
329 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
330 * @param aPassword The password to encrypt the VM with.
331 *
332 * @return Success indicator. if not S_OK, the machine object is invalid.
333 */
334HRESULT Machine::init(VirtualBox *aParent,
335 const Utf8Str &strConfigFile,
336 const Utf8Str &strName,
337 PlatformArchitecture_T aArchitecture,
338 const StringsList &llGroups,
339 const Utf8Str &strOsType,
340 GuestOSType *aOsType,
341 const Guid &aId,
342 bool fForceOverwrite,
343 bool fDirectoryIncludesUUID,
344 const com::Utf8Str &aCipher,
345 const com::Utf8Str &aPasswordId,
346 const com::Utf8Str &aPassword)
347{
348 LogFlowThisFuncEnter();
349 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
350
351#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
352 RT_NOREF(aCipher);
353 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
354 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
355#endif
356
357 /* Enclose the state transition NotReady->InInit->Ready */
358 AutoInitSpan autoInitSpan(this);
359 AssertReturn(autoInitSpan.isOk(), E_FAIL);
360
361 HRESULT hrc = initImpl(aParent, strConfigFile);
362 if (FAILED(hrc)) return hrc;
363
364#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
365 com::Utf8Str strSsmKeyId;
366 com::Utf8Str strSsmKeyStore;
367 com::Utf8Str strNVRAMKeyId;
368 com::Utf8Str strNVRAMKeyStore;
369
370 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
371 {
372 /* Resolve the cryptographic interface. */
373 PCVBOXCRYPTOIF pCryptoIf = NULL;
374 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
375 if (SUCCEEDED(hrc))
376 {
377 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
378 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
379 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
380
381 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
382 {
383 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
384 if (!pszCipher)
385 {
386 hrc = setError(VBOX_E_NOT_SUPPORTED,
387 tr("The cipher '%s' is not supported"), aCipher.c_str());
388 break;
389 }
390
391 VBOXCRYPTOCTX hCryptoCtx;
392 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
393 if (RT_FAILURE(vrc))
394 {
395 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
396 break;
397 }
398
399 char *pszKeyStore;
400 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
401 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
402 AssertRC(vrc2);
403
404 if (RT_FAILURE(vrc))
405 {
406 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
407 break;
408 }
409
410 *(astrKeyStore[i]) = pszKeyStore;
411 RTMemFree(pszKeyStore);
412 *(astrKeyId[i]) = aPasswordId;
413 }
414
415 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
416 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
417
418 if (FAILED(hrc))
419 return hrc; /* Error is set. */
420 }
421 else
422 return hrc; /* Error is set. */
423 }
424#endif
425
426 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
427 if (FAILED(hrc)) return hrc;
428
429 if (SUCCEEDED(hrc))
430 {
431 // create an empty machine config
432 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
433
434 hrc = initDataAndChildObjects();
435 }
436
437 if (SUCCEEDED(hrc))
438 {
439#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
440 mSSData->strStateKeyId = strSsmKeyId;
441 mSSData->strStateKeyStore = strSsmKeyStore;
442#endif
443
444 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
445 mData->mAccessible = TRUE;
446
447 unconst(mData->mUuid) = aId;
448
449 mUserData->s.strName = strName;
450
451 if (llGroups.size())
452 mUserData->s.llGroups = llGroups;
453
454 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
455 // the "name sync" flag determines whether the machine directory gets renamed along
456 // with the machine file; say so if the settings file name is the same as the
457 // settings file parent directory (machine directory)
458 mUserData->s.fNameSync = i_isInOwnDir();
459
460 // initialize the default snapshots folder
461 hrc = COMSETTER(SnapshotFolder)(NULL);
462 AssertComRC(hrc);
463
464 /* Use the platform architecture which was handed-in by default. */
465 PlatformArchitecture_T enmPlatformArch = aArchitecture;
466
467 if (aOsType)
468 {
469 /* Store OS type */
470 mUserData->s.strOsType = aOsType->i_id();
471
472 /* Use the platform architecture of the found guest OS type. */
473 enmPlatformArch = aOsType->i_platformArchitecture();
474 }
475 else if (!strOsType.isEmpty())
476 {
477 /* Store OS type */
478 mUserData->s.strOsType = strOsType;
479 }
480
481 /* Set the platform architecture first before applying the defaults below. */
482 hrc = mPlatform->i_initArchitecture(enmPlatformArch);
483 if (FAILED(hrc)) return hrc;
484
485 /* Apply platform defaults. */
486 mPlatform->i_applyDefaults(aOsType);
487 i_platformPropertiesUpdate();
488
489 /* Apply firmware defaults. */
490 mFirmwareSettings->i_applyDefaults(aOsType);
491
492 /* Apply TPM defaults. */
493 mTrustedPlatformModule->i_applyDefaults(aOsType);
494
495 /* Apply recording defaults. */
496 mRecordingSettings->i_applyDefaults();
497
498 /* Apply network adapters defaults */
499 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
500 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
501
502 /* Apply serial port defaults */
503 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
504 mSerialPorts[slot]->i_applyDefaults(aOsType);
505
506 /* Apply parallel port defaults */
507 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
508 mParallelPorts[slot]->i_applyDefaults();
509
510 /* Enable the VMMDev testing feature for bootsector VMs: */
511 if (aOsType && aOsType->i_id() == GUEST_OS_ID_STR_X64("VBoxBS"))
512 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
513
514#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
515 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
516#endif
517 if (SUCCEEDED(hrc))
518 {
519 /* At this point the changing of the current state modification
520 * flag is allowed. */
521 i_allowStateModification();
522
523 /* commit all changes made during the initialization */
524 i_commit();
525 }
526 }
527
528 /* Confirm a successful initialization when it's the case */
529 if (SUCCEEDED(hrc))
530 {
531#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
532 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
533 {
534 size_t cbPassword = aPassword.length() + 1;
535 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
536 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
537 }
538#endif
539
540 if (mData->mAccessible)
541 autoInitSpan.setSucceeded();
542 else
543 autoInitSpan.setLimited();
544 }
545
546 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
547 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
548 mData->mRegistered,
549 mData->mAccessible,
550 hrc));
551
552 LogFlowThisFuncLeave();
553
554 return hrc;
555}
556
557/**
558 * Initializes a new instance with data from machine XML (formerly Init_Registered).
559 * Gets called in two modes:
560 *
561 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
562 * UUID is specified and we mark the machine as "registered";
563 *
564 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
565 * and the machine remains unregistered until RegisterMachine() is called.
566 *
567 * @param aParent Associated parent object
568 * @param strConfigFile Local file system path to the VM settings file (can
569 * be relative to the VirtualBox config directory).
570 * @param aId UUID of the machine or NULL (see above).
571 * @param strPassword Password for decrypting the config
572 *
573 * @return Success indicator. if not S_OK, the machine object is invalid
574 */
575HRESULT Machine::initFromSettings(VirtualBox *aParent,
576 const Utf8Str &strConfigFile,
577 const Guid *aId,
578 const com::Utf8Str &strPassword)
579{
580 LogFlowThisFuncEnter();
581 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
582
583 PCVBOXCRYPTOIF pCryptoIf = NULL;
584#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
585 if (strPassword.isNotEmpty())
586 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
587#else
588 if (strPassword.isNotEmpty())
589 {
590 /* Get at the crpytographic interface. */
591 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
592 if (FAILED(hrc))
593 return hrc; /* Error is set. */
594 }
595#endif
596
597 /* Enclose the state transition NotReady->InInit->Ready */
598 AutoInitSpan autoInitSpan(this);
599 AssertReturn(autoInitSpan.isOk(), E_FAIL);
600
601 HRESULT hrc = initImpl(aParent, strConfigFile);
602 if (FAILED(hrc)) return hrc;
603
604 if (aId)
605 {
606 // loading a registered VM:
607 unconst(mData->mUuid) = *aId;
608 mData->mRegistered = TRUE;
609 // now load the settings from XML:
610 hrc = i_registeredInit();
611 // this calls initDataAndChildObjects() and loadSettings()
612 }
613 else
614 {
615 // opening an unregistered VM (VirtualBox::OpenMachine()):
616 hrc = initDataAndChildObjects();
617 if (SUCCEEDED(hrc))
618 {
619 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
620 mData->mAccessible = TRUE;
621
622 try
623 {
624 // load and parse machine XML; this will throw on XML or logic errors
625 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
626 pCryptoIf,
627 strPassword.c_str());
628
629 // reject VM UUID duplicates, they can happen if someone
630 // tries to register an already known VM config again
631 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
632 true /* fPermitInaccessible */,
633 false /* aDoSetError */,
634 NULL) != VBOX_E_OBJECT_NOT_FOUND)
635 {
636 throw setError(E_FAIL,
637 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
638 mData->m_strConfigFile.c_str());
639 }
640
641 // use UUID from machine config
642 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
643
644#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
645 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
646 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
647 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
648 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
649#endif
650
651 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
652 {
653 // We just set the inaccessible state and fill the error info allowing the caller
654 // to register the machine with encrypted config even if the password is incorrect
655 mData->mAccessible = FALSE;
656
657 /* fetch the current error info */
658 mData->mAccessError = com::ErrorInfo();
659
660 setError(VBOX_E_PASSWORD_INCORRECT,
661 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
662 mData->pMachineConfigFile->uuid.raw());
663 }
664 else
665 {
666#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
667 if (strPassword.isNotEmpty())
668 {
669 size_t cbKey = strPassword.length() + 1; /* Include terminator */
670 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
671 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
672 }
673#endif
674
675 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
676 if (FAILED(hrc)) throw hrc;
677
678 /* At this point the changing of the current state modification
679 * flag is allowed. */
680 i_allowStateModification();
681
682 i_commit();
683 }
684 }
685 catch (HRESULT err)
686 {
687 /* we assume that error info is set by the thrower */
688 hrc = err;
689 }
690 catch (...)
691 {
692 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
693 }
694 }
695 }
696
697 /* Confirm a successful initialization when it's the case */
698 if (SUCCEEDED(hrc))
699 {
700 if (mData->mAccessible)
701 autoInitSpan.setSucceeded();
702 else
703 {
704 autoInitSpan.setLimited();
705
706 // uninit media from this machine's media registry, or else
707 // reloading the settings will fail
708 mParent->i_unregisterMachineMedia(i_getId());
709 }
710 }
711
712#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
713 if (pCryptoIf)
714 {
715 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
716 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
717 }
718#endif
719
720 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
721 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
722
723 LogFlowThisFuncLeave();
724
725 return hrc;
726}
727
728/**
729 * Initializes a new instance from a machine config that is already in memory
730 * (import OVF case). Since we are importing, the UUID in the machine
731 * config is ignored and we always generate a fresh one.
732 *
733 * @param aParent Associated parent object.
734 * @param strName Name for the new machine; this overrides what is specified in config.
735 * @param strSettingsFilename File name of .vbox file.
736 * @param config Machine configuration loaded and parsed from XML.
737 *
738 * @return Success indicator. if not S_OK, the machine object is invalid
739 */
740HRESULT Machine::init(VirtualBox *aParent,
741 const Utf8Str &strName,
742 const Utf8Str &strSettingsFilename,
743 const settings::MachineConfigFile &config)
744{
745 LogFlowThisFuncEnter();
746
747 /* Enclose the state transition NotReady->InInit->Ready */
748 AutoInitSpan autoInitSpan(this);
749 AssertReturn(autoInitSpan.isOk(), E_FAIL);
750
751 HRESULT hrc = initImpl(aParent, strSettingsFilename);
752 if (FAILED(hrc)) return hrc;
753
754 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
755 if (FAILED(hrc)) return hrc;
756
757 hrc = initDataAndChildObjects();
758 if (SUCCEEDED(hrc))
759 {
760 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
761 mData->mAccessible = TRUE;
762
763 // create empty machine config for instance data
764 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
765
766 // generate fresh UUID, ignore machine config
767 unconst(mData->mUuid).create();
768
769 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
770
771 // override VM name as well, it may be different
772 mUserData->s.strName = strName;
773
774 if (SUCCEEDED(hrc))
775 {
776 /* At this point the changing of the current state modification
777 * flag is allowed. */
778 i_allowStateModification();
779
780 /* commit all changes made during the initialization */
781 i_commit();
782 }
783 }
784
785 /* Confirm a successful initialization when it's the case */
786 if (SUCCEEDED(hrc))
787 {
788 if (mData->mAccessible)
789 autoInitSpan.setSucceeded();
790 else
791 {
792 /* Ignore all errors from unregistering, they would destroy
793- * the more interesting error information we already have,
794- * pinpointing the issue with the VM config. */
795 ErrorInfoKeeper eik;
796
797 autoInitSpan.setLimited();
798
799 // uninit media from this machine's media registry, or else
800 // reloading the settings will fail
801 mParent->i_unregisterMachineMedia(i_getId());
802 }
803 }
804
805 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
806 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
807
808 LogFlowThisFuncLeave();
809
810 return hrc;
811}
812
813/**
814 * Shared code between the various init() implementations.
815 *
816 * @returns HRESULT
817 * @param aParent The VirtualBox object.
818 * @param strConfigFile Settings file.
819 */
820HRESULT Machine::initImpl(VirtualBox *aParent,
821 const Utf8Str &strConfigFile)
822{
823 LogFlowThisFuncEnter();
824
825 AssertReturn(aParent, E_INVALIDARG);
826 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
827
828 HRESULT hrc = S_OK;
829
830 /* share the parent weakly */
831 unconst(mParent) = aParent;
832
833 /* allocate the essential machine data structure (the rest will be
834 * allocated later by initDataAndChildObjects() */
835 mData.allocate();
836
837 /* memorize the config file name (as provided) */
838 mData->m_strConfigFile = strConfigFile;
839
840 /* get the full file name */
841 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
842 if (RT_FAILURE(vrc1))
843 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
844 tr("Invalid machine settings file name '%s' (%Rrc)"),
845 strConfigFile.c_str(),
846 vrc1);
847
848#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
849 /** @todo Only create when the machine is going to be encrypted. */
850 /* Non-pageable memory is not accessible for non-VM process */
851 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
852 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
853#endif
854
855 LogFlowThisFuncLeave();
856
857 return hrc;
858}
859
860/**
861 * Tries to create a machine settings file in the path stored in the machine
862 * instance data. Used when a new machine is created to fail gracefully if
863 * the settings file could not be written (e.g. because machine dir is read-only).
864 * @return
865 */
866HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
867{
868 HRESULT hrc = S_OK;
869
870 // when we create a new machine, we must be able to create the settings file
871 RTFILE f = NIL_RTFILE;
872 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
873 if ( RT_SUCCESS(vrc)
874 || vrc == VERR_SHARING_VIOLATION
875 )
876 {
877 if (RT_SUCCESS(vrc))
878 RTFileClose(f);
879 if (!fForceOverwrite)
880 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
881 else
882 {
883 /* try to delete the config file, as otherwise the creation
884 * of a new settings file will fail. */
885 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
886 }
887 }
888 else if ( vrc != VERR_FILE_NOT_FOUND
889 && vrc != VERR_PATH_NOT_FOUND
890 )
891 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
892 mData->m_strConfigFileFull.c_str(), vrc);
893 return hrc;
894}
895
896/**
897 * Initializes the registered machine by loading the settings file.
898 * This method is separated from #init() in order to make it possible to
899 * retry the operation after VirtualBox startup instead of refusing to
900 * startup the whole VirtualBox server in case if the settings file of some
901 * registered VM is invalid or inaccessible.
902 *
903 * @note Must be always called from this object's write lock
904 * (unless called from #init() that doesn't need any locking).
905 * @note Locks the mUSBController method for writing.
906 * @note Subclasses must not call this method.
907 */
908HRESULT Machine::i_registeredInit()
909{
910 AssertReturn(!i_isSessionMachine(), E_FAIL);
911 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
912 AssertReturn(mData->mUuid.isValid(), E_FAIL);
913 AssertReturn(!mData->mAccessible, E_FAIL);
914
915 HRESULT hrc = initDataAndChildObjects();
916 if (SUCCEEDED(hrc))
917 {
918 /* Temporarily reset the registered flag in order to let setters
919 * potentially called from loadSettings() succeed (isMutable() used in
920 * all setters will return FALSE for a Machine instance if mRegistered
921 * is TRUE). */
922 mData->mRegistered = FALSE;
923
924 PCVBOXCRYPTOIF pCryptoIf = NULL;
925 SecretKey *pKey = NULL;
926 const char *pszPassword = NULL;
927#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
928 /* Resolve password and cryptographic support interface if machine is encrypted. */
929 if (mData->mstrKeyId.isNotEmpty())
930 {
931 /* Get at the crpytographic interface. */
932 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
933 if (SUCCEEDED(hrc))
934 {
935 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
936 if (RT_SUCCESS(vrc))
937 pszPassword = (const char *)pKey->getKeyBuffer();
938 else
939 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
940 mData->mstrKeyId.c_str(), vrc);
941 }
942 }
943#else
944 RT_NOREF(pKey);
945#endif
946
947 if (SUCCEEDED(hrc))
948 {
949 try
950 {
951 // load and parse machine XML; this will throw on XML or logic errors
952 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
953 pCryptoIf, pszPassword);
954
955 if (mData->mUuid != mData->pMachineConfigFile->uuid)
956 throw setError(E_FAIL,
957 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
958 mData->pMachineConfigFile->uuid.raw(),
959 mData->m_strConfigFileFull.c_str(),
960 mData->mUuid.toString().c_str(),
961 mParent->i_settingsFilePath().c_str());
962
963#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
964 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
965 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
966 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
967 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
968
969 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
970 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
971 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
972 mData->pMachineConfigFile->uuid.raw());
973 else
974#endif
975 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
976 if (FAILED(hrc)) throw hrc;
977 }
978 catch (HRESULT err)
979 {
980 /* we assume that error info is set by the thrower */
981 hrc = err;
982 }
983 catch (...)
984 {
985 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
986 }
987
988 /* Restore the registered flag (even on failure) */
989 mData->mRegistered = TRUE;
990 }
991
992#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
993 if (pCryptoIf)
994 mParent->i_releaseCryptoIf(pCryptoIf);
995 if (pKey)
996 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
997#endif
998 }
999
1000 if (SUCCEEDED(hrc))
1001 {
1002 /* Set mAccessible to TRUE only if we successfully locked and loaded
1003 * the settings file */
1004 mData->mAccessible = TRUE;
1005
1006 /* commit all changes made during loading the settings file */
1007 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1008 /// @todo r=klaus for some reason the settings loading logic backs up
1009 // the settings, and therefore a commit is needed. Should probably be changed.
1010 }
1011 else
1012 {
1013 /* If the machine is registered, then, instead of returning a
1014 * failure, we mark it as inaccessible and set the result to
1015 * success to give it a try later */
1016
1017 /* fetch the current error info */
1018 mData->mAccessError = com::ErrorInfo();
1019 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1020
1021 /* rollback all changes */
1022 i_rollback(false /* aNotify */);
1023
1024 // uninit media from this machine's media registry, or else
1025 // reloading the settings will fail
1026 mParent->i_unregisterMachineMedia(i_getId());
1027
1028 /* uninitialize the common part to make sure all data is reset to
1029 * default (null) values */
1030 uninitDataAndChildObjects();
1031
1032 hrc = S_OK;
1033 }
1034
1035 return hrc;
1036}
1037
1038/**
1039 * Uninitializes the instance.
1040 * Called either from FinalRelease() or by the parent when it gets destroyed.
1041 *
1042 * @note The caller of this method must make sure that this object
1043 * a) doesn't have active callers on the current thread and b) is not locked
1044 * by the current thread; otherwise uninit() will hang either a) due to
1045 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1046 * a dead-lock caused by this thread waiting for all callers on the other
1047 * threads are done but preventing them from doing so by holding a lock.
1048 */
1049void Machine::uninit()
1050{
1051 LogFlowThisFuncEnter();
1052
1053 Assert(!isWriteLockOnCurrentThread());
1054
1055 Assert(!uRegistryNeedsSaving);
1056 if (uRegistryNeedsSaving)
1057 {
1058 AutoCaller autoCaller(this);
1059 if (SUCCEEDED(autoCaller.hrc()))
1060 {
1061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1062 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1063 }
1064 }
1065
1066 /* Enclose the state transition Ready->InUninit->NotReady */
1067 AutoUninitSpan autoUninitSpan(this);
1068 if (autoUninitSpan.uninitDone())
1069 return;
1070
1071 Assert(!i_isSnapshotMachine());
1072 Assert(!i_isSessionMachine());
1073 Assert(!!mData);
1074
1075 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1076 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1077
1078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 if (!mData->mSession.mMachine.isNull())
1081 {
1082 /* Theoretically, this can only happen if the VirtualBox server has been
1083 * terminated while there were clients running that owned open direct
1084 * sessions. Since in this case we are definitely called by
1085 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1086 * won't happen on the client watcher thread (because it has a
1087 * VirtualBox caller for the duration of the
1088 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1089 * cannot happen until the VirtualBox caller is released). This is
1090 * important, because SessionMachine::uninit() cannot correctly operate
1091 * after we return from this method (it expects the Machine instance is
1092 * still valid). We'll call it ourselves below.
1093 */
1094 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1095 (SessionMachine*)mData->mSession.mMachine));
1096
1097 if (Global::IsOnlineOrTransient(mData->mMachineState))
1098 {
1099 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1100 /* set machine state using SessionMachine reimplementation */
1101 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1102 }
1103
1104 /*
1105 * Uninitialize SessionMachine using public uninit() to indicate
1106 * an unexpected uninitialization.
1107 */
1108 mData->mSession.mMachine->uninit();
1109 /* SessionMachine::uninit() must set mSession.mMachine to null */
1110 Assert(mData->mSession.mMachine.isNull());
1111 }
1112
1113 // uninit media from this machine's media registry, if they're still there
1114 Guid uuidMachine(i_getId());
1115
1116 /* the lock is no more necessary (SessionMachine is uninitialized) */
1117 alock.release();
1118
1119 /* XXX This will fail with
1120 * "cannot be closed because it is still attached to 1 virtual machines"
1121 * because at this point we did not call uninitDataAndChildObjects() yet
1122 * and therefore also removeBackReference() for all these media was not called! */
1123
1124 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1125 mParent->i_unregisterMachineMedia(uuidMachine);
1126
1127 // has machine been modified?
1128 if (mData->flModifications)
1129 {
1130 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1131 i_rollback(false /* aNotify */);
1132 }
1133
1134 if (mData->mAccessible)
1135 uninitDataAndChildObjects();
1136
1137#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1138 if (mData->mpKeyStore != NULL)
1139 delete mData->mpKeyStore;
1140#endif
1141
1142 /* free the essential data structure last */
1143 mData.free();
1144
1145 LogFlowThisFuncLeave();
1146}
1147
1148// Wrapped IMachine properties
1149/////////////////////////////////////////////////////////////////////////////
1150HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1151{
1152 /* mParent is constant during life time, no need to lock */
1153 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1154 aParent = pVirtualBox;
1155
1156 return S_OK;
1157}
1158
1159HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1160{
1161 /* mPlatform is constant during life time, no need to lock */
1162 ComObjPtr<Platform> pPlatform(mPlatform);
1163 aPlatform = pPlatform;
1164
1165 return S_OK;
1166}
1167
1168HRESULT Machine::getAccessible(BOOL *aAccessible)
1169{
1170 /* In some cases (medium registry related), it is necessary to be able to
1171 * go through the list of all machines. Happens when an inaccessible VM
1172 * has a sensible medium registry. */
1173 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1175
1176 HRESULT hrc = S_OK;
1177
1178 if (!mData->mAccessible)
1179 {
1180 /* try to initialize the VM once more if not accessible */
1181
1182 AutoReinitSpan autoReinitSpan(this);
1183 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1184
1185#ifdef DEBUG
1186 LogFlowThisFunc(("Dumping media backreferences\n"));
1187 mParent->i_dumpAllBackRefs();
1188#endif
1189
1190 if (mData->pMachineConfigFile)
1191 {
1192 // reset the XML file to force loadSettings() (called from i_registeredInit())
1193 // to parse it again; the file might have changed
1194 delete mData->pMachineConfigFile;
1195 mData->pMachineConfigFile = NULL;
1196 }
1197
1198 hrc = i_registeredInit();
1199
1200 if (SUCCEEDED(hrc) && mData->mAccessible)
1201 {
1202 autoReinitSpan.setSucceeded();
1203
1204 /* make sure interesting parties will notice the accessibility
1205 * state change */
1206 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1207 mParent->i_onMachineDataChanged(mData->mUuid);
1208 }
1209 }
1210
1211 if (SUCCEEDED(hrc))
1212 *aAccessible = mData->mAccessible;
1213
1214 LogFlowThisFuncLeave();
1215
1216 return hrc;
1217}
1218
1219HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1220{
1221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1224 {
1225 /* return shortly */
1226 aAccessError = NULL;
1227 return S_OK;
1228 }
1229
1230 HRESULT hrc = S_OK;
1231
1232 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1233 hrc = errorInfo.createObject();
1234 if (SUCCEEDED(hrc))
1235 {
1236 errorInfo->init(mData->mAccessError.getResultCode(),
1237 mData->mAccessError.getInterfaceID().ref(),
1238 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1239 Utf8Str(mData->mAccessError.getText()));
1240 aAccessError = errorInfo;
1241 }
1242
1243 return hrc;
1244}
1245
1246HRESULT Machine::getName(com::Utf8Str &aName)
1247{
1248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 aName = mUserData->s.strName;
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::setName(const com::Utf8Str &aName)
1256{
1257 // prohibit setting a UUID only as the machine name, or else it can
1258 // never be found by findMachine()
1259 Guid test(aName);
1260
1261 if (test.isValid())
1262 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1263
1264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1265
1266 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1267 if (FAILED(hrc)) return hrc;
1268
1269 i_setModified(IsModified_MachineData);
1270 mUserData.backup();
1271 mUserData->s.strName = aName;
1272
1273 return S_OK;
1274}
1275
1276HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1277{
1278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 aDescription = mUserData->s.strDescription;
1281
1282 return S_OK;
1283}
1284
1285HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1286{
1287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1288
1289 // this can be done in principle in any state as it doesn't affect the VM
1290 // significantly, but play safe by not messing around while complex
1291 // activities are going on
1292 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1293 if (FAILED(hrc)) return hrc;
1294
1295 i_setModified(IsModified_MachineData);
1296 mUserData.backup();
1297 mUserData->s.strDescription = aDescription;
1298
1299 return S_OK;
1300}
1301
1302HRESULT Machine::getId(com::Guid &aId)
1303{
1304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1305
1306 aId = mData->mUuid;
1307
1308 return S_OK;
1309}
1310
1311HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1312{
1313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1314 aGroups.resize(mUserData->s.llGroups.size());
1315 size_t i = 0;
1316 for (StringsList::const_iterator
1317 it = mUserData->s.llGroups.begin();
1318 it != mUserData->s.llGroups.end();
1319 ++it, ++i)
1320 aGroups[i] = (*it);
1321
1322 return S_OK;
1323}
1324
1325HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1326{
1327 StringsList llGroups;
1328 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1329 if (FAILED(hrc))
1330 return hrc;
1331
1332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1333
1334 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1335 if (FAILED(hrc)) return hrc;
1336
1337 i_setModified(IsModified_MachineData);
1338 mUserData.backup();
1339 mUserData->s.llGroups = llGroups;
1340
1341 mParent->i_onMachineGroupsChanged(mData->mUuid);
1342 return S_OK;
1343}
1344
1345HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1346{
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 aOSTypeId = mUserData->s.strOsType;
1350
1351 return S_OK;
1352}
1353
1354HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1355{
1356 /* look up the object by Id to check it is valid */
1357 ComObjPtr<GuestOSType> pGuestOSType;
1358 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1359
1360 /* when setting, always use the "etalon" value for consistency -- lookup
1361 * by ID is case-insensitive and the input value may have different case */
1362 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1363
1364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1365
1366 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1367 if (FAILED(hrc)) return hrc;
1368
1369 i_setModified(IsModified_MachineData);
1370 mUserData.backup();
1371 mUserData->s.strOsType = osTypeId;
1372
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1386{
1387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1390 if (FAILED(hrc)) return hrc;
1391
1392 i_setModified(IsModified_MachineData);
1393 mHWData.backup();
1394 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1395
1396 return S_OK;
1397}
1398
1399HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1400{
1401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1402
1403 *aPointingHIDType = mHWData->mPointingHIDType;
1404
1405 return S_OK;
1406}
1407
1408HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1409{
1410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1413 if (FAILED(hrc)) return hrc;
1414
1415 i_setModified(IsModified_MachineData);
1416 mHWData.backup();
1417 mHWData->mPointingHIDType = aPointingHIDType;
1418
1419 return S_OK;
1420}
1421
1422HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1423{
1424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1425
1426 aParavirtDebug = mHWData->mParavirtDebug;
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1431{
1432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1435 if (FAILED(hrc)) return hrc;
1436
1437 /** @todo Parse/validate options? */
1438 if (aParavirtDebug != mHWData->mParavirtDebug)
1439 {
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 mHWData->mParavirtDebug = aParavirtDebug;
1443 }
1444
1445 return S_OK;
1446}
1447
1448HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1449{
1450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1451
1452 *aParavirtProvider = mHWData->mParavirtProvider;
1453
1454 return S_OK;
1455}
1456
1457HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1458{
1459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1462 if (FAILED(hrc)) return hrc;
1463
1464 if (aParavirtProvider != mHWData->mParavirtProvider)
1465 {
1466 i_setModified(IsModified_MachineData);
1467 mHWData.backup();
1468 mHWData->mParavirtProvider = aParavirtProvider;
1469 }
1470
1471 return S_OK;
1472}
1473
1474HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1475{
1476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1477
1478 *aParavirtProvider = mHWData->mParavirtProvider;
1479 switch (mHWData->mParavirtProvider)
1480 {
1481 case ParavirtProvider_None:
1482 case ParavirtProvider_HyperV:
1483 case ParavirtProvider_KVM:
1484 case ParavirtProvider_Minimal:
1485 break;
1486
1487 /* Resolve dynamic provider types to the effective types. */
1488 default:
1489 {
1490 ComObjPtr<GuestOSType> pGuestOSType;
1491 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1492 pGuestOSType);
1493 if (FAILED(hrc2) || pGuestOSType.isNull())
1494 {
1495 *aParavirtProvider = ParavirtProvider_None;
1496 break;
1497 }
1498
1499 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1500 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1501
1502 switch (mHWData->mParavirtProvider)
1503 {
1504 case ParavirtProvider_Legacy:
1505 {
1506 if (fOsXGuest)
1507 *aParavirtProvider = ParavirtProvider_Minimal;
1508 else
1509 *aParavirtProvider = ParavirtProvider_None;
1510 break;
1511 }
1512
1513 case ParavirtProvider_Default:
1514 {
1515 Assert(strlen(GUEST_OS_ID_STR_X64("")) > 0);
1516 if (fOsXGuest)
1517 *aParavirtProvider = ParavirtProvider_Minimal;
1518 else if ( mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows11")
1519 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows10")
1520 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows10")
1521 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows81")
1522 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows81")
1523 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows8")
1524 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows8")
1525 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows7")
1526 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows7")
1527 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("WindowsVista")
1528 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("WindowsVista")
1529 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1530 || mUserData->s.strOsType.startsWith("Windows201"))
1531 && mUserData->s.strOsType.endsWith(GUEST_OS_ID_STR_X64("")))
1532 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2012")
1533 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2012")
1534 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2008")
1535 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2008"))
1536 *aParavirtProvider = ParavirtProvider_HyperV;
1537 else if ( guestTypeFamilyId == "Linux"
1538 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux22") // Linux22 and Linux24{_64} excluded as they're too old
1539 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux24") // to have any KVM paravirtualization support.
1540 && mUserData->s.strOsType != GUEST_OS_ID_STR_X64("Linux24"))
1541 *aParavirtProvider = ParavirtProvider_KVM;
1542 else
1543 *aParavirtProvider = ParavirtProvider_None;
1544 break;
1545 }
1546
1547 default: AssertFailedBreak(); /* Shut up MSC. */
1548 }
1549 break;
1550 }
1551 }
1552
1553 Assert( *aParavirtProvider == ParavirtProvider_None
1554 || *aParavirtProvider == ParavirtProvider_Minimal
1555 || *aParavirtProvider == ParavirtProvider_HyperV
1556 || *aParavirtProvider == ParavirtProvider_KVM);
1557 return S_OK;
1558}
1559
1560HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1561{
1562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1563
1564 aHardwareVersion = mHWData->mHWVersion;
1565
1566 return S_OK;
1567}
1568
1569HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1570{
1571 /* check known version */
1572 Utf8Str hwVersion = aHardwareVersion;
1573 if ( hwVersion.compare("1") != 0
1574 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1575 return setError(E_INVALIDARG,
1576 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1577
1578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1579
1580 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1581 if (FAILED(hrc)) return hrc;
1582
1583 i_setModified(IsModified_MachineData);
1584 mHWData.backup();
1585 mHWData->mHWVersion = aHardwareVersion;
1586
1587 return S_OK;
1588}
1589
1590HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1591{
1592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1593
1594 if (!mHWData->mHardwareUUID.isZero())
1595 aHardwareUUID = mHWData->mHardwareUUID;
1596 else
1597 aHardwareUUID = mData->mUuid;
1598
1599 return S_OK;
1600}
1601
1602HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1603{
1604 if (!aHardwareUUID.isValid())
1605 return E_INVALIDARG;
1606
1607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1608
1609 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1610 if (FAILED(hrc)) return hrc;
1611
1612 i_setModified(IsModified_MachineData);
1613 mHWData.backup();
1614 if (aHardwareUUID == mData->mUuid)
1615 mHWData->mHardwareUUID.clear();
1616 else
1617 mHWData->mHardwareUUID = aHardwareUUID;
1618
1619 return S_OK;
1620}
1621
1622HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1623{
1624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 *aMemorySize = mHWData->mMemorySize;
1627
1628 return S_OK;
1629}
1630
1631HRESULT Machine::setMemorySize(ULONG aMemorySize)
1632{
1633 /* check RAM limits */
1634 if ( aMemorySize < MM_RAM_MIN_IN_MB
1635 || aMemorySize > MM_RAM_MAX_IN_MB
1636 )
1637 return setError(E_INVALIDARG,
1638 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1639 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1640
1641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1644 if (FAILED(hrc)) return hrc;
1645
1646 i_setModified(IsModified_MachineData);
1647 mHWData.backup();
1648 mHWData->mMemorySize = aMemorySize;
1649
1650 return S_OK;
1651}
1652
1653HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1654{
1655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1656
1657 *aCPUCount = mHWData->mCPUCount;
1658
1659 return S_OK;
1660}
1661
1662HRESULT Machine::setCPUCount(ULONG aCPUCount)
1663{
1664 /* check CPU limits */
1665 if ( aCPUCount < SchemaDefs::MinCPUCount
1666 || aCPUCount > SchemaDefs::MaxCPUCount
1667 )
1668 return setError(E_INVALIDARG,
1669 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1670 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1671
1672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1675 if (mHWData->mCPUHotPlugEnabled)
1676 {
1677 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1678 {
1679 if (mHWData->mCPUAttached[idx])
1680 return setError(E_INVALIDARG,
1681 tr("There is still a CPU attached to socket %lu."
1682 "Detach the CPU before removing the socket"),
1683 aCPUCount, idx+1);
1684 }
1685 }
1686
1687 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1688 if (FAILED(hrc)) return hrc;
1689
1690 i_setModified(IsModified_MachineData);
1691 mHWData.backup();
1692 mHWData->mCPUCount = aCPUCount;
1693
1694 return S_OK;
1695}
1696
1697HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1702
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1707{
1708 /* check throttle limits */
1709 if ( aCPUExecutionCap < 1
1710 || aCPUExecutionCap > 100
1711 )
1712 return setError(E_INVALIDARG,
1713 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1714 aCPUExecutionCap, 1, 100);
1715
1716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1719 if (FAILED(hrc)) return hrc;
1720
1721 alock.release();
1722 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1723 alock.acquire();
1724 if (FAILED(hrc)) return hrc;
1725
1726 i_setModified(IsModified_MachineData);
1727 mHWData.backup();
1728 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1729
1730 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1731 if (Global::IsOnline(mData->mMachineState))
1732 i_saveSettings(NULL, alock);
1733
1734 return S_OK;
1735}
1736
1737HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1738{
1739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
1741 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1742
1743 return S_OK;
1744}
1745
1746HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1747{
1748 HRESULT hrc = S_OK;
1749
1750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1751
1752 hrc = i_checkStateDependency(MutableStateDep);
1753 if (FAILED(hrc)) return hrc;
1754
1755 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1756 {
1757 if (aCPUHotPlugEnabled)
1758 {
1759 i_setModified(IsModified_MachineData);
1760 mHWData.backup();
1761
1762 /* Add the amount of CPUs currently attached */
1763 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1764 mHWData->mCPUAttached[i] = true;
1765 }
1766 else
1767 {
1768 /*
1769 * We can disable hotplug only if the amount of maximum CPUs is equal
1770 * to the amount of attached CPUs
1771 */
1772 unsigned cCpusAttached = 0;
1773 unsigned iHighestId = 0;
1774
1775 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1776 {
1777 if (mHWData->mCPUAttached[i])
1778 {
1779 cCpusAttached++;
1780 iHighestId = i;
1781 }
1782 }
1783
1784 if ( (cCpusAttached != mHWData->mCPUCount)
1785 || (iHighestId >= mHWData->mCPUCount))
1786 return setError(E_INVALIDARG,
1787 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1788
1789 i_setModified(IsModified_MachineData);
1790 mHWData.backup();
1791 }
1792 }
1793
1794 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1795
1796 return hrc;
1797}
1798
1799HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1800{
1801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1802
1803 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1804
1805 return S_OK;
1806}
1807
1808HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1809{
1810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1811
1812 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1813 if (SUCCEEDED(hrc))
1814 {
1815 i_setModified(IsModified_MachineData);
1816 mHWData.backup();
1817 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1818 }
1819 return hrc;
1820}
1821
1822HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1823{
1824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1825 aCPUProfile = mHWData->mCpuProfile;
1826 return S_OK;
1827}
1828
1829HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1830{
1831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1833 if (SUCCEEDED(hrc))
1834 {
1835 i_setModified(IsModified_MachineData);
1836 mHWData.backup();
1837 /* Empty equals 'host'. */
1838 if (aCPUProfile.isNotEmpty())
1839 mHWData->mCpuProfile = aCPUProfile;
1840 else
1841 mHWData->mCpuProfile = "host";
1842 }
1843 return hrc;
1844}
1845
1846HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1847{
1848#ifdef VBOX_WITH_USB_CARDREADER
1849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1852
1853 return S_OK;
1854#else
1855 NOREF(aEmulatedUSBCardReaderEnabled);
1856 return E_NOTIMPL;
1857#endif
1858}
1859
1860HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1861{
1862#ifdef VBOX_WITH_USB_CARDREADER
1863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1864
1865 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1866 if (FAILED(hrc)) return hrc;
1867
1868 i_setModified(IsModified_MachineData);
1869 mHWData.backup();
1870 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1871
1872 return S_OK;
1873#else
1874 NOREF(aEmulatedUSBCardReaderEnabled);
1875 return E_NOTIMPL;
1876#endif
1877}
1878
1879/** @todo this method should not be public */
1880HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1881{
1882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1885
1886 return S_OK;
1887}
1888
1889/**
1890 * Set the memory balloon size.
1891 *
1892 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1893 * we have to make sure that we never call IGuest from here.
1894 */
1895HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1896{
1897 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1898#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1899 /* check limits */
1900 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1901 return setError(E_INVALIDARG,
1902 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1903 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1904
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1908 if (FAILED(hrc)) return hrc;
1909
1910 i_setModified(IsModified_MachineData);
1911 mHWData.backup();
1912 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1913
1914 return S_OK;
1915#else
1916 NOREF(aMemoryBalloonSize);
1917 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1918#endif
1919}
1920
1921HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1922{
1923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1926 return S_OK;
1927}
1928
1929HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1930{
1931#ifdef VBOX_WITH_PAGE_SHARING
1932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1933
1934 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1935 if (FAILED(hrc)) return hrc;
1936
1937 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1941 return S_OK;
1942#else
1943 NOREF(aPageFusionEnabled);
1944 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1945#endif
1946}
1947
1948HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1949{
1950 /* mFirmwareSettings is constant during life time, no need to lock */
1951 aFirmwareSettings = mFirmwareSettings;
1952
1953 return S_OK;
1954}
1955
1956HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1957{
1958 /* mTrustedPlatformModule is constant during life time, no need to lock */
1959 aTrustedPlatformModule = mTrustedPlatformModule;
1960
1961 return S_OK;
1962}
1963
1964HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1965{
1966 /* mNvramStore is constant during life time, no need to lock */
1967 aNvramStore = mNvramStore;
1968
1969 return S_OK;
1970}
1971
1972HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1973{
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 aRecordingSettings = mRecordingSettings;
1977
1978 return S_OK;
1979}
1980
1981HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1982{
1983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 aGraphicsAdapter = mGraphicsAdapter;
1986
1987 return S_OK;
1988}
1989
1990HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
1991{
1992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
1995
1996 return S_OK;
1997}
1998
1999HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2000{
2001 /** @todo (r=dmik):
2002 * 1. Allow to change the name of the snapshot folder containing snapshots
2003 * 2. Rename the folder on disk instead of just changing the property
2004 * value (to be smart and not to leave garbage). Note that it cannot be
2005 * done here because the change may be rolled back. Thus, the right
2006 * place is #saveSettings().
2007 */
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2012 if (FAILED(hrc)) return hrc;
2013
2014 if (!mData->mCurrentSnapshot.isNull())
2015 return setError(E_FAIL,
2016 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2017
2018 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2019
2020 if (strSnapshotFolder.isEmpty())
2021 strSnapshotFolder = "Snapshots";
2022 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2023 if (RT_FAILURE(vrc))
2024 return setErrorBoth(E_FAIL, vrc,
2025 tr("Invalid snapshot folder '%s' (%Rrc)"),
2026 strSnapshotFolder.c_str(), vrc);
2027
2028 i_setModified(IsModified_MachineData);
2029 mUserData.backup();
2030
2031 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2037{
2038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2039
2040 aMediumAttachments.resize(mMediumAttachments->size());
2041 size_t i = 0;
2042 for (MediumAttachmentList::const_iterator
2043 it = mMediumAttachments->begin();
2044 it != mMediumAttachments->end();
2045 ++it, ++i)
2046 aMediumAttachments[i] = *it;
2047
2048 return S_OK;
2049}
2050
2051HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2052{
2053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2054
2055 Assert(!!mVRDEServer);
2056
2057 aVRDEServer = mVRDEServer;
2058
2059 return S_OK;
2060}
2061
2062HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2063{
2064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2065
2066 aAudioSettings = mAudioSettings;
2067
2068 return S_OK;
2069}
2070
2071HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2072{
2073#ifdef VBOX_WITH_VUSB
2074 clearError();
2075 MultiResult hrcMult(S_OK);
2076
2077# ifdef VBOX_WITH_USB
2078 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2079 if (FAILED(hrcMult)) return hrcMult;
2080# endif
2081
2082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 aUSBControllers.resize(mUSBControllers->size());
2085 size_t i = 0;
2086 for (USBControllerList::const_iterator
2087 it = mUSBControllers->begin();
2088 it != mUSBControllers->end();
2089 ++it, ++i)
2090 aUSBControllers[i] = *it;
2091
2092 return S_OK;
2093#else
2094 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2095 * extended error info to indicate that USB is simply not available
2096 * (w/o treating it as a failure), for example, as in OSE */
2097 NOREF(aUSBControllers);
2098 ReturnComNotImplemented();
2099#endif /* VBOX_WITH_VUSB */
2100}
2101
2102HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2103{
2104#ifdef VBOX_WITH_VUSB
2105 clearError();
2106 MultiResult hrcMult(S_OK);
2107
2108# ifdef VBOX_WITH_USB
2109 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2110 if (FAILED(hrcMult)) return hrcMult;
2111# endif
2112
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 aUSBDeviceFilters = mUSBDeviceFilters;
2116 return hrcMult;
2117#else
2118 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2119 * extended error info to indicate that USB is simply not available
2120 * (w/o treating it as a failure), for example, as in OSE */
2121 NOREF(aUSBDeviceFilters);
2122 ReturnComNotImplemented();
2123#endif /* VBOX_WITH_VUSB */
2124}
2125
2126HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2127{
2128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 aSettingsFilePath = mData->m_strConfigFileFull;
2131
2132 return S_OK;
2133}
2134
2135HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2136{
2137 RT_NOREF(aSettingsFilePath);
2138 ReturnComNotImplemented();
2139}
2140
2141HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2142{
2143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2144
2145 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2146 if (FAILED(hrc)) return hrc;
2147
2148 if (!mData->pMachineConfigFile->fileExists())
2149 // this is a new machine, and no config file exists yet:
2150 *aSettingsModified = TRUE;
2151 else
2152 *aSettingsModified = (mData->flModifications != 0);
2153
2154 return S_OK;
2155}
2156
2157HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2158{
2159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 *aSessionState = mData->mSession.mState;
2162
2163 return S_OK;
2164}
2165
2166HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2167{
2168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 aSessionName = mData->mSession.mName;
2171
2172 return S_OK;
2173}
2174
2175HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2176{
2177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2178
2179 *aSessionPID = mData->mSession.mPID;
2180
2181 return S_OK;
2182}
2183
2184HRESULT Machine::getState(MachineState_T *aState)
2185{
2186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 *aState = mData->mMachineState;
2189 Assert(mData->mMachineState != MachineState_Null);
2190
2191 return S_OK;
2192}
2193
2194HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2195{
2196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2197
2198 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2199
2200 return S_OK;
2201}
2202
2203HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2204{
2205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 aStateFilePath = mSSData->strStateFilePath;
2208
2209 return S_OK;
2210}
2211
2212HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2213{
2214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2215
2216 i_getLogFolder(aLogFolder);
2217
2218 return S_OK;
2219}
2220
2221HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2222{
2223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 aCurrentSnapshot = mData->mCurrentSnapshot;
2226
2227 return S_OK;
2228}
2229
2230HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2231{
2232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
2234 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2235 ? 0
2236 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2237
2238 return S_OK;
2239}
2240
2241HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2242{
2243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 /* Note: for machines with no snapshots, we always return FALSE
2246 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2247 * reasons :) */
2248
2249 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2250 ? FALSE
2251 : mData->mCurrentStateModified;
2252
2253 return S_OK;
2254}
2255
2256HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2257{
2258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2259
2260 aSharedFolders.resize(mHWData->mSharedFolders.size());
2261 size_t i = 0;
2262 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2263 it = mHWData->mSharedFolders.begin();
2264 it != mHWData->mSharedFolders.end();
2265 ++it, ++i)
2266 aSharedFolders[i] = *it;
2267
2268 return S_OK;
2269}
2270
2271HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2272{
2273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2274
2275 *aClipboardMode = mHWData->mClipboardMode;
2276
2277 return S_OK;
2278}
2279
2280HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2281{
2282 HRESULT hrc = S_OK;
2283
2284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2285
2286 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2287 if (FAILED(hrc)) return hrc;
2288
2289 alock.release();
2290 hrc = i_onClipboardModeChange(aClipboardMode);
2291 alock.acquire();
2292 if (FAILED(hrc)) return hrc;
2293
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mClipboardMode = aClipboardMode;
2297
2298 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2299 if (Global::IsOnline(mData->mMachineState))
2300 i_saveSettings(NULL, alock);
2301
2302 return S_OK;
2303}
2304
2305HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2306{
2307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2310
2311 return S_OK;
2312}
2313
2314HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2315{
2316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2317
2318 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2319 if (FAILED(hrc)) return hrc;
2320
2321 alock.release();
2322 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2323 alock.acquire();
2324 if (FAILED(hrc)) return hrc;
2325
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2329
2330 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2331 if (Global::IsOnline(mData->mMachineState))
2332 i_saveSettings(NULL, alock);
2333
2334 return S_OK;
2335}
2336
2337HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2338{
2339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2340
2341 *aDnDMode = mHWData->mDnDMode;
2342
2343 return S_OK;
2344}
2345
2346HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2347{
2348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2349
2350 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2351 if (FAILED(hrc)) return hrc;
2352
2353 alock.release();
2354 hrc = i_onDnDModeChange(aDnDMode);
2355
2356 alock.acquire();
2357 if (FAILED(hrc)) return hrc;
2358
2359 i_setModified(IsModified_MachineData);
2360 mHWData.backup();
2361 mHWData->mDnDMode = aDnDMode;
2362
2363 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2364 if (Global::IsOnline(mData->mMachineState))
2365 i_saveSettings(NULL, alock);
2366
2367 return S_OK;
2368}
2369
2370HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2371{
2372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 aStorageControllers.resize(mStorageControllers->size());
2375 size_t i = 0;
2376 for (StorageControllerList::const_iterator
2377 it = mStorageControllers->begin();
2378 it != mStorageControllers->end();
2379 ++it, ++i)
2380 aStorageControllers[i] = *it;
2381
2382 return S_OK;
2383}
2384
2385HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2386{
2387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2388
2389 *aEnabled = mUserData->s.fTeleporterEnabled;
2390
2391 return S_OK;
2392}
2393
2394HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2395{
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 /* Only allow it to be set to true when PoweredOff or Aborted.
2399 (Clearing it is always permitted.) */
2400 if ( aTeleporterEnabled
2401 && mData->mRegistered
2402 && ( !i_isSessionMachine()
2403 || ( mData->mMachineState != MachineState_PoweredOff
2404 && mData->mMachineState != MachineState_Teleported
2405 && mData->mMachineState != MachineState_Aborted
2406 )
2407 )
2408 )
2409 return setError(VBOX_E_INVALID_VM_STATE,
2410 tr("The machine is not powered off (state is %s)"),
2411 Global::stringifyMachineState(mData->mMachineState));
2412
2413 i_setModified(IsModified_MachineData);
2414 mUserData.backup();
2415 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2416
2417 return S_OK;
2418}
2419
2420HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2421{
2422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2425
2426 return S_OK;
2427}
2428
2429HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2430{
2431 if (aTeleporterPort >= _64K)
2432 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2433
2434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2435
2436 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2437 if (FAILED(hrc)) return hrc;
2438
2439 i_setModified(IsModified_MachineData);
2440 mUserData.backup();
2441 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2442
2443 return S_OK;
2444}
2445
2446HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2447{
2448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2449
2450 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2451
2452 return S_OK;
2453}
2454
2455HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2456{
2457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2460 if (FAILED(hrc)) return hrc;
2461
2462 i_setModified(IsModified_MachineData);
2463 mUserData.backup();
2464 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2465
2466 return S_OK;
2467}
2468
2469HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2470{
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2473
2474 return S_OK;
2475}
2476
2477HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2478{
2479 /*
2480 * Hash the password first.
2481 */
2482 com::Utf8Str aT = aTeleporterPassword;
2483
2484 if (!aT.isEmpty())
2485 {
2486 if (VBoxIsPasswordHashed(&aT))
2487 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2488 VBoxHashPassword(&aT);
2489 }
2490
2491 /*
2492 * Do the update.
2493 */
2494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2495 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2496 if (SUCCEEDED(hrc))
2497 {
2498 i_setModified(IsModified_MachineData);
2499 mUserData.backup();
2500 mUserData->s.strTeleporterPassword = aT;
2501 }
2502
2503 return hrc;
2504}
2505
2506HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2507{
2508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2511
2512 return S_OK;
2513}
2514
2515HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2516{
2517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2518
2519 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2520 if (FAILED(hrc)) return hrc;
2521
2522 i_setModified(IsModified_MachineData);
2523 mHWData.backup();
2524 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 *aIOCacheSize = mHWData->mIOCacheSize;
2534
2535 return S_OK;
2536}
2537
2538HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2539{
2540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2543 if (FAILED(hrc)) return hrc;
2544
2545 i_setModified(IsModified_MachineData);
2546 mHWData.backup();
2547 mHWData->mIOCacheSize = aIOCacheSize;
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2557 aKeyId = mSSData->strStateKeyId;
2558#else
2559 aKeyId = com::Utf8Str::Empty;
2560#endif
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2570 aKeyStore = mSSData->strStateKeyStore;
2571#else
2572 aKeyStore = com::Utf8Str::Empty;
2573#endif
2574
2575 return S_OK;
2576}
2577
2578HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2579{
2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2583 aKeyId = mData->mstrLogKeyId;
2584#else
2585 aKeyId = com::Utf8Str::Empty;
2586#endif
2587
2588 return S_OK;
2589}
2590
2591HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2592{
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2596 aKeyStore = mData->mstrLogKeyStore;
2597#else
2598 aKeyStore = com::Utf8Str::Empty;
2599#endif
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2605{
2606 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2607
2608 return S_OK;
2609}
2610
2611
2612/**
2613 * @note Locks objects!
2614 */
2615HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2616 LockType_T aLockType)
2617{
2618 /* check the session state */
2619 SessionState_T state;
2620 HRESULT hrc = aSession->COMGETTER(State)(&state);
2621 if (FAILED(hrc)) return hrc;
2622
2623 if (state != SessionState_Unlocked)
2624 return setError(VBOX_E_INVALID_OBJECT_STATE,
2625 tr("The given session is busy"));
2626
2627 // get the client's IInternalSessionControl interface
2628 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2629 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2630 E_INVALIDARG);
2631
2632 // session name (only used in some code paths)
2633 Utf8Str strSessionName;
2634
2635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 if (!mData->mRegistered)
2638 return setError(E_UNEXPECTED,
2639 tr("The machine '%s' is not registered"),
2640 mUserData->s.strName.c_str());
2641
2642 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2643
2644 SessionState_T oldState = mData->mSession.mState;
2645 /* Hack: in case the session is closing and there is a progress object
2646 * which allows waiting for the session to be closed, take the opportunity
2647 * and do a limited wait (max. 1 second). This helps a lot when the system
2648 * is busy and thus session closing can take a little while. */
2649 if ( mData->mSession.mState == SessionState_Unlocking
2650 && mData->mSession.mProgress)
2651 {
2652 alock.release();
2653 mData->mSession.mProgress->WaitForCompletion(1000);
2654 alock.acquire();
2655 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2656 }
2657
2658 // try again now
2659 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2660 // (i.e. session machine exists)
2661 && (aLockType == LockType_Shared) // caller wants a shared link to the
2662 // existing session that holds the write lock:
2663 )
2664 {
2665 // OK, share the session... we are now dealing with three processes:
2666 // 1) VBoxSVC (where this code runs);
2667 // 2) process C: the caller's client process (who wants a shared session);
2668 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2669
2670 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2671 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2672 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2673 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2674 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2675
2676 /*
2677 * Release the lock before calling the client process. It's safe here
2678 * since the only thing to do after we get the lock again is to add
2679 * the remote control to the list (which doesn't directly influence
2680 * anything).
2681 */
2682 alock.release();
2683
2684 // get the console of the session holding the write lock (this is a remote call)
2685 ComPtr<IConsole> pConsoleW;
2686 if (mData->mSession.mLockType == LockType_VM)
2687 {
2688 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2689 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2690 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2691 if (FAILED(hrc))
2692 // the failure may occur w/o any error info (from RPC), so provide one
2693 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2694 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2695 }
2696
2697 // share the session machine and W's console with the caller's session
2698 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2699 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2700 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2701
2702 if (FAILED(hrc))
2703 // the failure may occur w/o any error info (from RPC), so provide one
2704 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2705 alock.acquire();
2706
2707 // need to revalidate the state after acquiring the lock again
2708 if (mData->mSession.mState != SessionState_Locked)
2709 {
2710 pSessionControl->Uninitialize();
2711 return setError(VBOX_E_INVALID_SESSION_STATE,
2712 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2713 mUserData->s.strName.c_str());
2714 }
2715
2716 // add the caller's session to the list
2717 mData->mSession.mRemoteControls.push_back(pSessionControl);
2718 }
2719 else if ( mData->mSession.mState == SessionState_Locked
2720 || mData->mSession.mState == SessionState_Unlocking
2721 )
2722 {
2723 // sharing not permitted, or machine still unlocking:
2724 return setError(VBOX_E_INVALID_OBJECT_STATE,
2725 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2726 mUserData->s.strName.c_str());
2727 }
2728 else
2729 {
2730 // machine is not locked: then write-lock the machine (create the session machine)
2731
2732 // must not be busy
2733 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2734
2735 // get the caller's session PID
2736 RTPROCESS pid = NIL_RTPROCESS;
2737 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2738 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2739 Assert(pid != NIL_RTPROCESS);
2740
2741 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2742
2743 if (fLaunchingVMProcess)
2744 {
2745 if (mData->mSession.mPID == NIL_RTPROCESS)
2746 {
2747 // two or more clients racing for a lock, the one which set the
2748 // session state to Spawning will win, the others will get an
2749 // error as we can't decide here if waiting a little would help
2750 // (only for shared locks this would avoid an error)
2751 return setError(VBOX_E_INVALID_OBJECT_STATE,
2752 tr("The machine '%s' already has a lock request pending"),
2753 mUserData->s.strName.c_str());
2754 }
2755
2756 // this machine is awaiting for a spawning session to be opened:
2757 // then the calling process must be the one that got started by
2758 // LaunchVMProcess()
2759
2760 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2761 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2762
2763#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2764 /* Hardened windows builds spawns three processes when a VM is
2765 launched, the 3rd one is the one that will end up here. */
2766 RTPROCESS pidParent;
2767 int vrc = RTProcQueryParent(pid, &pidParent);
2768 if (RT_SUCCESS(vrc))
2769 vrc = RTProcQueryParent(pidParent, &pidParent);
2770 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2771 || vrc == VERR_ACCESS_DENIED)
2772 {
2773 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2774 mData->mSession.mPID = pid;
2775 }
2776#endif
2777
2778 if (mData->mSession.mPID != pid)
2779 return setError(E_ACCESSDENIED,
2780 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2781 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2782 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2783 }
2784
2785 // create the mutable SessionMachine from the current machine
2786 ComObjPtr<SessionMachine> sessionMachine;
2787 sessionMachine.createObject();
2788 hrc = sessionMachine->init(this);
2789 AssertComRC(hrc);
2790
2791 /* NOTE: doing return from this function after this point but
2792 * before the end is forbidden since it may call SessionMachine::uninit()
2793 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2794 * lock while still holding the Machine lock in alock so that a deadlock
2795 * is possible due to the wrong lock order. */
2796
2797 if (SUCCEEDED(hrc))
2798 {
2799 /*
2800 * Set the session state to Spawning to protect against subsequent
2801 * attempts to open a session and to unregister the machine after
2802 * we release the lock.
2803 */
2804 SessionState_T origState = mData->mSession.mState;
2805 mData->mSession.mState = SessionState_Spawning;
2806
2807#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2808 /* Get the client token ID to be passed to the client process */
2809 Utf8Str strTokenId;
2810 sessionMachine->i_getTokenId(strTokenId);
2811 Assert(!strTokenId.isEmpty());
2812#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2813 /* Get the client token to be passed to the client process */
2814 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2815 /* The token is now "owned" by pToken, fix refcount */
2816 if (!pToken.isNull())
2817 pToken->Release();
2818#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2819
2820 /*
2821 * Release the lock before calling the client process -- it will call
2822 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2823 * because the state is Spawning, so that LaunchVMProcess() and
2824 * LockMachine() calls will fail. This method, called before we
2825 * acquire the lock again, will fail because of the wrong PID.
2826 *
2827 * Note that mData->mSession.mRemoteControls accessed outside
2828 * the lock may not be modified when state is Spawning, so it's safe.
2829 */
2830 alock.release();
2831
2832 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2833#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2834 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2835#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2836 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2837 /* Now the token is owned by the client process. */
2838 pToken.setNull();
2839#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2840 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2841
2842 /* The failure may occur w/o any error info (from RPC), so provide one */
2843 if (FAILED(hrc))
2844 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2845
2846 // get session name, either to remember or to compare against
2847 // the already known session name.
2848 {
2849 Bstr bstrSessionName;
2850 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2851 if (SUCCEEDED(hrc2))
2852 strSessionName = bstrSessionName;
2853 }
2854
2855 if ( SUCCEEDED(hrc)
2856 && fLaunchingVMProcess
2857 )
2858 {
2859 /* complete the remote session initialization */
2860
2861 /* get the console from the direct session */
2862 ComPtr<IConsole> console;
2863 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2864 ComAssertComRC(hrc);
2865
2866 if (SUCCEEDED(hrc) && !console)
2867 {
2868 ComAssert(!!console);
2869 hrc = E_FAIL;
2870 }
2871
2872 /* assign machine & console to the remote session */
2873 if (SUCCEEDED(hrc))
2874 {
2875 /*
2876 * after LaunchVMProcess(), the first and the only
2877 * entry in remoteControls is that remote session
2878 */
2879 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2880 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2881 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2882
2883 /* The failure may occur w/o any error info (from RPC), so provide one */
2884 if (FAILED(hrc))
2885 setError(VBOX_E_VM_ERROR,
2886 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2887 }
2888
2889 if (FAILED(hrc))
2890 pSessionControl->Uninitialize();
2891 }
2892
2893 /* acquire the lock again */
2894 alock.acquire();
2895
2896 /* Restore the session state */
2897 mData->mSession.mState = origState;
2898 }
2899
2900 // finalize spawning anyway (this is why we don't return on errors above)
2901 if (fLaunchingVMProcess)
2902 {
2903 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2904 /* Note that the progress object is finalized later */
2905 /** @todo Consider checking mData->mSession.mProgress for cancellation
2906 * around here. */
2907
2908 /* We don't reset mSession.mPID here because it is necessary for
2909 * SessionMachine::uninit() to reap the child process later. */
2910
2911 if (FAILED(hrc))
2912 {
2913 /* Close the remote session, remove the remote control from the list
2914 * and reset session state to Closed (@note keep the code in sync
2915 * with the relevant part in checkForSpawnFailure()). */
2916
2917 Assert(mData->mSession.mRemoteControls.size() == 1);
2918 if (mData->mSession.mRemoteControls.size() == 1)
2919 {
2920 ErrorInfoKeeper eik;
2921 mData->mSession.mRemoteControls.front()->Uninitialize();
2922 }
2923
2924 mData->mSession.mRemoteControls.clear();
2925 mData->mSession.mState = SessionState_Unlocked;
2926 }
2927 }
2928 else
2929 {
2930 /* memorize PID of the directly opened session */
2931 if (SUCCEEDED(hrc))
2932 mData->mSession.mPID = pid;
2933 }
2934
2935 if (SUCCEEDED(hrc))
2936 {
2937 mData->mSession.mLockType = aLockType;
2938 /* memorize the direct session control and cache IUnknown for it */
2939 mData->mSession.mDirectControl = pSessionControl;
2940 mData->mSession.mState = SessionState_Locked;
2941 if (!fLaunchingVMProcess)
2942 mData->mSession.mName = strSessionName;
2943 /* associate the SessionMachine with this Machine */
2944 mData->mSession.mMachine = sessionMachine;
2945
2946 /* request an IUnknown pointer early from the remote party for later
2947 * identity checks (it will be internally cached within mDirectControl
2948 * at least on XPCOM) */
2949 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2950 NOREF(unk);
2951
2952#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2953 if (aLockType == LockType_VM)
2954 {
2955 /* get the console from the direct session */
2956 ComPtr<IConsole> console;
2957 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2958 ComAssertComRC(hrc);
2959 /* send passswords to console */
2960 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2961 it != mData->mpKeyStore->end();
2962 ++it)
2963 {
2964 SecretKey *pKey = it->second;
2965 pKey->retain();
2966 console->AddEncryptionPassword(Bstr(it->first).raw(),
2967 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2968 TRUE);
2969 pKey->release();
2970 }
2971
2972 }
2973#endif
2974 }
2975
2976 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2977 * would break the lock order */
2978 alock.release();
2979
2980 /* uninitialize the created session machine on failure */
2981 if (FAILED(hrc))
2982 sessionMachine->uninit();
2983 }
2984
2985 if (SUCCEEDED(hrc))
2986 {
2987 /*
2988 * tell the client watcher thread to update the set of
2989 * machines that have open sessions
2990 */
2991 mParent->i_updateClientWatcher();
2992
2993 if (oldState != SessionState_Locked)
2994 /* fire an event */
2995 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
2996 }
2997
2998 return hrc;
2999}
3000
3001/**
3002 * @note Locks objects!
3003 */
3004HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3005 const com::Utf8Str &aName,
3006 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3007 ComPtr<IProgress> &aProgress)
3008{
3009 Utf8Str strFrontend(aName);
3010 /* "emergencystop" doesn't need the session, so skip the checks/interface
3011 * retrieval. This code doesn't quite fit in here, but introducing a
3012 * special API method would be even more effort, and would require explicit
3013 * support by every API client. It's better to hide the feature a bit. */
3014 if (strFrontend != "emergencystop")
3015 CheckComArgNotNull(aSession);
3016
3017 HRESULT hrc = S_OK;
3018 if (strFrontend.isEmpty())
3019 {
3020 Bstr bstrFrontend;
3021 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3022 if (FAILED(hrc))
3023 return hrc;
3024 strFrontend = bstrFrontend;
3025 if (strFrontend.isEmpty())
3026 {
3027 ComPtr<ISystemProperties> systemProperties;
3028 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3029 if (FAILED(hrc))
3030 return hrc;
3031 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3032 if (FAILED(hrc))
3033 return hrc;
3034 strFrontend = bstrFrontend;
3035 }
3036 /* paranoia - emergencystop is not a valid default */
3037 if (strFrontend == "emergencystop")
3038 strFrontend = Utf8Str::Empty;
3039 }
3040 /* default frontend: Qt GUI */
3041 if (strFrontend.isEmpty())
3042 strFrontend = "GUI/Qt";
3043
3044 if (strFrontend != "emergencystop")
3045 {
3046 /* check the session state */
3047 SessionState_T state;
3048 hrc = aSession->COMGETTER(State)(&state);
3049 if (FAILED(hrc))
3050 return hrc;
3051
3052 if (state != SessionState_Unlocked)
3053 return setError(VBOX_E_INVALID_OBJECT_STATE,
3054 tr("The given session is busy"));
3055
3056 /* get the IInternalSessionControl interface */
3057 ComPtr<IInternalSessionControl> control(aSession);
3058 ComAssertMsgRet(!control.isNull(),
3059 ("No IInternalSessionControl interface"),
3060 E_INVALIDARG);
3061
3062 /* get the teleporter enable state for the progress object init. */
3063 BOOL fTeleporterEnabled;
3064 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3065 if (FAILED(hrc))
3066 return hrc;
3067
3068 /* create a progress object */
3069 ComObjPtr<ProgressProxy> progress;
3070 progress.createObject();
3071 hrc = progress->init(mParent,
3072 static_cast<IMachine*>(this),
3073 Bstr(tr("Starting VM")).raw(),
3074 TRUE /* aCancelable */,
3075 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3076 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3077 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3078 2 /* uFirstOperationWeight */,
3079 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3080 if (SUCCEEDED(hrc))
3081 {
3082 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3083 if (SUCCEEDED(hrc))
3084 {
3085 aProgress = progress;
3086
3087 /* signal the client watcher thread */
3088 mParent->i_updateClientWatcher();
3089
3090 /* fire an event */
3091 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3092 }
3093 }
3094 }
3095 else
3096 {
3097 /* no progress object - either instant success or failure */
3098 aProgress = NULL;
3099
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 if (mData->mSession.mState != SessionState_Locked)
3103 return setError(VBOX_E_INVALID_OBJECT_STATE,
3104 tr("The machine '%s' is not locked by a session"),
3105 mUserData->s.strName.c_str());
3106
3107 /* must have a VM process associated - do not kill normal API clients
3108 * with an open session */
3109 if (!Global::IsOnline(mData->mMachineState))
3110 return setError(VBOX_E_INVALID_OBJECT_STATE,
3111 tr("The machine '%s' does not have a VM process"),
3112 mUserData->s.strName.c_str());
3113
3114 /* forcibly terminate the VM process */
3115 if (mData->mSession.mPID != NIL_RTPROCESS)
3116 RTProcTerminate(mData->mSession.mPID);
3117
3118 /* signal the client watcher thread, as most likely the client has
3119 * been terminated */
3120 mParent->i_updateClientWatcher();
3121 }
3122
3123 return hrc;
3124}
3125
3126HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3127{
3128 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3129 return setError(E_INVALIDARG,
3130 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3131 aPosition, SchemaDefs::MaxBootPosition);
3132
3133 if (aDevice == DeviceType_USB)
3134 return setError(E_NOTIMPL,
3135 tr("Booting from USB device is currently not supported"));
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3140 if (FAILED(hrc)) return hrc;
3141
3142 i_setModified(IsModified_MachineData);
3143 mHWData.backup();
3144 mHWData->mBootOrder[aPosition - 1] = aDevice;
3145
3146 return S_OK;
3147}
3148
3149HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3150{
3151 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3152 return setError(E_INVALIDARG,
3153 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3154 aPosition, SchemaDefs::MaxBootPosition);
3155
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aDevice = mHWData->mBootOrder[aPosition - 1];
3159
3160 return S_OK;
3161}
3162
3163HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3164 LONG aControllerPort,
3165 LONG aDevice,
3166 DeviceType_T aType,
3167 const ComPtr<IMedium> &aMedium)
3168{
3169 IMedium *aM = aMedium;
3170 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3171 aName.c_str(), aControllerPort, aDevice, aType, aM));
3172
3173 // request the host lock first, since might be calling Host methods for getting host drives;
3174 // next, protect the media tree all the while we're in here, as well as our member variables
3175 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3176 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3177
3178 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3179 if (FAILED(hrc)) return hrc;
3180
3181 /// @todo NEWMEDIA implicit machine registration
3182 if (!mData->mRegistered)
3183 return setError(VBOX_E_INVALID_OBJECT_STATE,
3184 tr("Cannot attach storage devices to an unregistered machine"));
3185
3186 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3187
3188 /* Check for an existing controller. */
3189 ComObjPtr<StorageController> ctl;
3190 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3191 if (FAILED(hrc)) return hrc;
3192
3193 StorageControllerType_T ctrlType;
3194 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3195 if (FAILED(hrc))
3196 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3197
3198 bool fSilent = false;
3199
3200 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3201 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3202 if ( mData->mMachineState == MachineState_Paused
3203 && strReconfig == "1")
3204 fSilent = true;
3205
3206 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3207 bool fHotplug = false;
3208 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3209 fHotplug = true;
3210
3211 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3212 return setError(VBOX_E_INVALID_VM_STATE,
3213 tr("Controller '%s' does not support hot-plugging"),
3214 aName.c_str());
3215
3216 // check that the port and device are not out of range
3217 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3218 if (FAILED(hrc)) return hrc;
3219
3220 /* check if the device slot is already busy */
3221 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3222 aName,
3223 aControllerPort,
3224 aDevice);
3225 if (pAttachTemp)
3226 {
3227 Medium *pMedium = pAttachTemp->i_getMedium();
3228 if (pMedium)
3229 {
3230 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3231 return setError(VBOX_E_OBJECT_IN_USE,
3232 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3233 pMedium->i_getLocationFull().c_str(),
3234 aControllerPort,
3235 aDevice,
3236 aName.c_str());
3237 }
3238 else
3239 return setError(VBOX_E_OBJECT_IN_USE,
3240 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3241 aControllerPort, aDevice, aName.c_str());
3242 }
3243
3244 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3245 if (aMedium && medium.isNull())
3246 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3247
3248 AutoCaller mediumCaller(medium);
3249 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3250
3251 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3252
3253 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3254 if ( pAttachTemp
3255 && !medium.isNull()
3256 && ( medium->i_getType() != MediumType_Readonly
3257 || medium->i_getDeviceType() != DeviceType_DVD)
3258 )
3259 return setError(VBOX_E_OBJECT_IN_USE,
3260 tr("Medium '%s' is already attached to this virtual machine"),
3261 medium->i_getLocationFull().c_str());
3262
3263 if (!medium.isNull())
3264 {
3265 MediumType_T mtype = medium->i_getType();
3266 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3267 // For DVDs it's not written to the config file, so needs no global config
3268 // version bump. For floppies it's a new attribute "type", which is ignored
3269 // by older VirtualBox version, so needs no global config version bump either.
3270 // For hard disks this type is not accepted.
3271 if (mtype == MediumType_MultiAttach)
3272 {
3273 // This type is new with VirtualBox 4.0 and therefore requires settings
3274 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3275 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3276 // two reasons: The medium type is a property of the media registry tree, which
3277 // can reside in the global config file (for pre-4.0 media); we would therefore
3278 // possibly need to bump the global config version. We don't want to do that though
3279 // because that might make downgrading to pre-4.0 impossible.
3280 // As a result, we can only use these two new types if the medium is NOT in the
3281 // global registry:
3282 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3283 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3284 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3285 )
3286 return setError(VBOX_E_INVALID_OBJECT_STATE,
3287 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3288 "to machines that were created with VirtualBox 4.0 or later"),
3289 medium->i_getLocationFull().c_str());
3290 }
3291 }
3292
3293 bool fIndirect = false;
3294 if (!medium.isNull())
3295 fIndirect = medium->i_isReadOnly();
3296 bool associate = true;
3297
3298 do
3299 {
3300 if ( aType == DeviceType_HardDisk
3301 && mMediumAttachments.isBackedUp())
3302 {
3303 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3304
3305 /* check if the medium was attached to the VM before we started
3306 * changing attachments in which case the attachment just needs to
3307 * be restored */
3308 pAttachTemp = i_findAttachment(oldAtts, medium);
3309 if (pAttachTemp)
3310 {
3311 AssertReturn(!fIndirect, E_FAIL);
3312
3313 /* see if it's the same bus/channel/device */
3314 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3315 {
3316 /* the simplest case: restore the whole attachment
3317 * and return, nothing else to do */
3318 mMediumAttachments->push_back(pAttachTemp);
3319
3320 /* Reattach the medium to the VM. */
3321 if (fHotplug || fSilent)
3322 {
3323 mediumLock.release();
3324 treeLock.release();
3325 alock.release();
3326
3327 MediumLockList *pMediumLockList(new MediumLockList());
3328
3329 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3330 medium /* pToLockWrite */,
3331 false /* fMediumLockWriteAll */,
3332 NULL,
3333 *pMediumLockList);
3334 alock.acquire();
3335 if (FAILED(hrc))
3336 delete pMediumLockList;
3337 else
3338 {
3339 Assert(mData->mSession.mLockedMedia.IsLocked());
3340 mData->mSession.mLockedMedia.Unlock();
3341 alock.release();
3342 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3343 mData->mSession.mLockedMedia.Lock();
3344 alock.acquire();
3345 }
3346 alock.release();
3347
3348 if (SUCCEEDED(hrc))
3349 {
3350 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3351 /* Remove lock list in case of error. */
3352 if (FAILED(hrc))
3353 {
3354 mData->mSession.mLockedMedia.Unlock();
3355 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3356 mData->mSession.mLockedMedia.Lock();
3357 }
3358 }
3359 }
3360
3361 return S_OK;
3362 }
3363
3364 /* bus/channel/device differ; we need a new attachment object,
3365 * but don't try to associate it again */
3366 associate = false;
3367 break;
3368 }
3369 }
3370
3371 /* go further only if the attachment is to be indirect */
3372 if (!fIndirect)
3373 break;
3374
3375 /* perform the so called smart attachment logic for indirect
3376 * attachments. Note that smart attachment is only applicable to base
3377 * hard disks. */
3378
3379 if (medium->i_getParent().isNull())
3380 {
3381 /* first, investigate the backup copy of the current hard disk
3382 * attachments to make it possible to re-attach existing diffs to
3383 * another device slot w/o losing their contents */
3384 if (mMediumAttachments.isBackedUp())
3385 {
3386 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3387
3388 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3389 uint32_t foundLevel = 0;
3390
3391 for (MediumAttachmentList::const_iterator
3392 it = oldAtts.begin();
3393 it != oldAtts.end();
3394 ++it)
3395 {
3396 uint32_t level = 0;
3397 MediumAttachment *pAttach = *it;
3398 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3399 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3400 if (pMedium.isNull())
3401 continue;
3402
3403 if (pMedium->i_getBase(&level) == medium)
3404 {
3405 /* skip the hard disk if its currently attached (we
3406 * cannot attach the same hard disk twice) */
3407 if (i_findAttachment(*mMediumAttachments.data(),
3408 pMedium))
3409 continue;
3410
3411 /* matched device, channel and bus (i.e. attached to the
3412 * same place) will win and immediately stop the search;
3413 * otherwise the attachment that has the youngest
3414 * descendant of medium will be used
3415 */
3416 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3417 {
3418 /* the simplest case: restore the whole attachment
3419 * and return, nothing else to do */
3420 mMediumAttachments->push_back(*it);
3421
3422 /* Reattach the medium to the VM. */
3423 if (fHotplug || fSilent)
3424 {
3425 mediumLock.release();
3426 treeLock.release();
3427 alock.release();
3428
3429 MediumLockList *pMediumLockList(new MediumLockList());
3430
3431 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3432 medium /* pToLockWrite */,
3433 false /* fMediumLockWriteAll */,
3434 NULL,
3435 *pMediumLockList);
3436 alock.acquire();
3437 if (FAILED(hrc))
3438 delete pMediumLockList;
3439 else
3440 {
3441 Assert(mData->mSession.mLockedMedia.IsLocked());
3442 mData->mSession.mLockedMedia.Unlock();
3443 alock.release();
3444 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3445 mData->mSession.mLockedMedia.Lock();
3446 alock.acquire();
3447 }
3448 alock.release();
3449
3450 if (SUCCEEDED(hrc))
3451 {
3452 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3453 /* Remove lock list in case of error. */
3454 if (FAILED(hrc))
3455 {
3456 mData->mSession.mLockedMedia.Unlock();
3457 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3458 mData->mSession.mLockedMedia.Lock();
3459 }
3460 }
3461 }
3462
3463 return S_OK;
3464 }
3465 else if ( foundIt == oldAtts.end()
3466 || level > foundLevel /* prefer younger */
3467 )
3468 {
3469 foundIt = it;
3470 foundLevel = level;
3471 }
3472 }
3473 }
3474
3475 if (foundIt != oldAtts.end())
3476 {
3477 /* use the previously attached hard disk */
3478 medium = (*foundIt)->i_getMedium();
3479 mediumCaller.attach(medium);
3480 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3481 mediumLock.attach(medium);
3482 /* not implicit, doesn't require association with this VM */
3483 fIndirect = false;
3484 associate = false;
3485 /* go right to the MediumAttachment creation */
3486 break;
3487 }
3488 }
3489
3490 /* must give up the medium lock and medium tree lock as below we
3491 * go over snapshots, which needs a lock with higher lock order. */
3492 mediumLock.release();
3493 treeLock.release();
3494
3495 /* then, search through snapshots for the best diff in the given
3496 * hard disk's chain to base the new diff on */
3497
3498 ComObjPtr<Medium> base;
3499 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3500 while (snap)
3501 {
3502 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3503
3504 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3505
3506 MediumAttachment *pAttachFound = NULL;
3507 uint32_t foundLevel = 0;
3508
3509 for (MediumAttachmentList::const_iterator
3510 it = snapAtts.begin();
3511 it != snapAtts.end();
3512 ++it)
3513 {
3514 MediumAttachment *pAttach = *it;
3515 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3516 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3517 if (pMedium.isNull())
3518 continue;
3519
3520 uint32_t level = 0;
3521 if (pMedium->i_getBase(&level) == medium)
3522 {
3523 /* matched device, channel and bus (i.e. attached to the
3524 * same place) will win and immediately stop the search;
3525 * otherwise the attachment that has the youngest
3526 * descendant of medium will be used
3527 */
3528 if ( pAttach->i_getDevice() == aDevice
3529 && pAttach->i_getPort() == aControllerPort
3530 && pAttach->i_getControllerName() == aName
3531 )
3532 {
3533 pAttachFound = pAttach;
3534 break;
3535 }
3536 else if ( !pAttachFound
3537 || level > foundLevel /* prefer younger */
3538 )
3539 {
3540 pAttachFound = pAttach;
3541 foundLevel = level;
3542 }
3543 }
3544 }
3545
3546 if (pAttachFound)
3547 {
3548 base = pAttachFound->i_getMedium();
3549 break;
3550 }
3551
3552 snap = snap->i_getParent();
3553 }
3554
3555 /* re-lock medium tree and the medium, as we need it below */
3556 treeLock.acquire();
3557 mediumLock.acquire();
3558
3559 /* found a suitable diff, use it as a base */
3560 if (!base.isNull())
3561 {
3562 medium = base;
3563 mediumCaller.attach(medium);
3564 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3565 mediumLock.attach(medium);
3566 }
3567 }
3568
3569 Utf8Str strFullSnapshotFolder;
3570 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3571
3572 ComObjPtr<Medium> diff;
3573 diff.createObject();
3574 // store this diff in the same registry as the parent
3575 Guid uuidRegistryParent;
3576 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3577 {
3578 // parent image has no registry: this can happen if we're attaching a new immutable
3579 // image that has not yet been attached (medium then points to the base and we're
3580 // creating the diff image for the immutable, and the parent is not yet registered);
3581 // put the parent in the machine registry then
3582 mediumLock.release();
3583 treeLock.release();
3584 alock.release();
3585 i_addMediumToRegistry(medium);
3586 alock.acquire();
3587 treeLock.acquire();
3588 mediumLock.acquire();
3589 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3590 }
3591 hrc = diff->init(mParent,
3592 medium->i_getPreferredDiffFormat(),
3593 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3594 uuidRegistryParent,
3595 DeviceType_HardDisk);
3596 if (FAILED(hrc)) return hrc;
3597
3598 /* Apply the normal locking logic to the entire chain. */
3599 MediumLockList *pMediumLockList(new MediumLockList());
3600 mediumLock.release();
3601 treeLock.release();
3602 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3603 diff /* pToLockWrite */,
3604 false /* fMediumLockWriteAll */,
3605 medium,
3606 *pMediumLockList);
3607 treeLock.acquire();
3608 mediumLock.acquire();
3609 if (SUCCEEDED(hrc))
3610 {
3611 mediumLock.release();
3612 treeLock.release();
3613 hrc = pMediumLockList->Lock();
3614 treeLock.acquire();
3615 mediumLock.acquire();
3616 if (FAILED(hrc))
3617 setError(hrc,
3618 tr("Could not lock medium when creating diff '%s'"),
3619 diff->i_getLocationFull().c_str());
3620 else
3621 {
3622 /* will release the lock before the potentially lengthy
3623 * operation, so protect with the special state */
3624 MachineState_T oldState = mData->mMachineState;
3625 i_setMachineState(MachineState_SettingUp);
3626
3627 mediumLock.release();
3628 treeLock.release();
3629 alock.release();
3630
3631 hrc = medium->i_createDiffStorage(diff,
3632 medium->i_getPreferredDiffVariant(),
3633 pMediumLockList,
3634 NULL /* aProgress */,
3635 true /* aWait */,
3636 false /* aNotify */);
3637
3638 alock.acquire();
3639 treeLock.acquire();
3640 mediumLock.acquire();
3641
3642 i_setMachineState(oldState);
3643 }
3644 }
3645
3646 /* Unlock the media and free the associated memory. */
3647 delete pMediumLockList;
3648
3649 if (FAILED(hrc)) return hrc;
3650
3651 /* use the created diff for the actual attachment */
3652 medium = diff;
3653 mediumCaller.attach(medium);
3654 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3655 mediumLock.attach(medium);
3656 }
3657 while (0);
3658
3659 ComObjPtr<MediumAttachment> attachment;
3660 attachment.createObject();
3661 hrc = attachment->init(this,
3662 medium,
3663 aName,
3664 aControllerPort,
3665 aDevice,
3666 aType,
3667 fIndirect,
3668 false /* fPassthrough */,
3669 false /* fTempEject */,
3670 false /* fNonRotational */,
3671 false /* fDiscard */,
3672 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3673 Utf8Str::Empty);
3674 if (FAILED(hrc)) return hrc;
3675
3676 if (associate && !medium.isNull())
3677 {
3678 // as the last step, associate the medium to the VM
3679 hrc = medium->i_addBackReference(mData->mUuid);
3680 // here we can fail because of Deleting, or being in process of creating a Diff
3681 if (FAILED(hrc)) return hrc;
3682
3683 mediumLock.release();
3684 treeLock.release();
3685 alock.release();
3686 i_addMediumToRegistry(medium);
3687 alock.acquire();
3688 treeLock.acquire();
3689 mediumLock.acquire();
3690 }
3691
3692 /* success: finally remember the attachment */
3693 i_setModified(IsModified_Storage);
3694 mMediumAttachments.backup();
3695 mMediumAttachments->push_back(attachment);
3696
3697 mediumLock.release();
3698 treeLock.release();
3699 alock.release();
3700
3701 if (fHotplug || fSilent)
3702 {
3703 if (!medium.isNull())
3704 {
3705 MediumLockList *pMediumLockList(new MediumLockList());
3706
3707 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3708 medium /* pToLockWrite */,
3709 false /* fMediumLockWriteAll */,
3710 NULL,
3711 *pMediumLockList);
3712 alock.acquire();
3713 if (FAILED(hrc))
3714 delete pMediumLockList;
3715 else
3716 {
3717 Assert(mData->mSession.mLockedMedia.IsLocked());
3718 mData->mSession.mLockedMedia.Unlock();
3719 alock.release();
3720 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3721 mData->mSession.mLockedMedia.Lock();
3722 alock.acquire();
3723 }
3724 alock.release();
3725 }
3726
3727 if (SUCCEEDED(hrc))
3728 {
3729 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3730 /* Remove lock list in case of error. */
3731 if (FAILED(hrc))
3732 {
3733 mData->mSession.mLockedMedia.Unlock();
3734 mData->mSession.mLockedMedia.Remove(attachment);
3735 mData->mSession.mLockedMedia.Lock();
3736 }
3737 }
3738 }
3739
3740 /* Save modified registries, but skip this machine as it's the caller's
3741 * job to save its settings like all other settings changes. */
3742 mParent->i_unmarkRegistryModified(i_getId());
3743 mParent->i_saveModifiedRegistries();
3744
3745 if (SUCCEEDED(hrc))
3746 {
3747 if (fIndirect && medium != aM)
3748 mParent->i_onMediumConfigChanged(medium);
3749 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3750 }
3751
3752 return hrc;
3753}
3754
3755HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3756 LONG aDevice)
3757{
3758 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3759
3760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3761
3762 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3763 if (FAILED(hrc)) return hrc;
3764
3765 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3766
3767 /* Check for an existing controller. */
3768 ComObjPtr<StorageController> ctl;
3769 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3770 if (FAILED(hrc)) return hrc;
3771
3772 StorageControllerType_T ctrlType;
3773 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3774 if (FAILED(hrc))
3775 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3776
3777 bool fSilent = false;
3778 Utf8Str strReconfig;
3779
3780 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3781 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3782 if ( mData->mMachineState == MachineState_Paused
3783 && strReconfig == "1")
3784 fSilent = true;
3785
3786 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3787 bool fHotplug = false;
3788 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3789 fHotplug = true;
3790
3791 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3792 return setError(VBOX_E_INVALID_VM_STATE,
3793 tr("Controller '%s' does not support hot-plugging"),
3794 aName.c_str());
3795
3796 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3797 aName,
3798 aControllerPort,
3799 aDevice);
3800 if (!pAttach)
3801 return setError(VBOX_E_OBJECT_NOT_FOUND,
3802 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3803 aDevice, aControllerPort, aName.c_str());
3804
3805 if (fHotplug && !pAttach->i_getHotPluggable())
3806 return setError(VBOX_E_NOT_SUPPORTED,
3807 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3808 aDevice, aControllerPort, aName.c_str());
3809
3810 /*
3811 * The VM has to detach the device before we delete any implicit diffs.
3812 * If this fails we can roll back without loosing data.
3813 */
3814 if (fHotplug || fSilent)
3815 {
3816 alock.release();
3817 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3818 alock.acquire();
3819 }
3820 if (FAILED(hrc)) return hrc;
3821
3822 /* If we are here everything went well and we can delete the implicit now. */
3823 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3824
3825 alock.release();
3826
3827 /* Save modified registries, but skip this machine as it's the caller's
3828 * job to save its settings like all other settings changes. */
3829 mParent->i_unmarkRegistryModified(i_getId());
3830 mParent->i_saveModifiedRegistries();
3831
3832 if (SUCCEEDED(hrc))
3833 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3834
3835 return hrc;
3836}
3837
3838HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3839 LONG aDevice, BOOL aPassthrough)
3840{
3841 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3842 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3843
3844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3845
3846 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3847 if (FAILED(hrc)) return hrc;
3848
3849 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3850
3851 /* Check for an existing controller. */
3852 ComObjPtr<StorageController> ctl;
3853 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3854 if (FAILED(hrc)) return hrc;
3855
3856 StorageControllerType_T ctrlType;
3857 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3858 if (FAILED(hrc))
3859 return setError(E_FAIL,
3860 tr("Could not get type of controller '%s'"),
3861 aName.c_str());
3862
3863 bool fSilent = false;
3864 Utf8Str strReconfig;
3865
3866 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3867 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3868 if ( mData->mMachineState == MachineState_Paused
3869 && strReconfig == "1")
3870 fSilent = true;
3871
3872 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3873 bool fHotplug = false;
3874 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3875 fHotplug = true;
3876
3877 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3878 return setError(VBOX_E_INVALID_VM_STATE,
3879 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3880 aName.c_str());
3881
3882 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3883 aName,
3884 aControllerPort,
3885 aDevice);
3886 if (!pAttach)
3887 return setError(VBOX_E_OBJECT_NOT_FOUND,
3888 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3889 aDevice, aControllerPort, aName.c_str());
3890
3891
3892 i_setModified(IsModified_Storage);
3893 mMediumAttachments.backup();
3894
3895 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3896
3897 if (pAttach->i_getType() != DeviceType_DVD)
3898 return setError(E_INVALIDARG,
3899 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3900 aDevice, aControllerPort, aName.c_str());
3901
3902 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3903
3904 pAttach->i_updatePassthrough(!!aPassthrough);
3905
3906 attLock.release();
3907 alock.release();
3908 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3909 if (SUCCEEDED(hrc) && fValueChanged)
3910 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3911
3912 return hrc;
3913}
3914
3915HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3916 LONG aDevice, BOOL aTemporaryEject)
3917{
3918
3919 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3920 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3921
3922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3923
3924 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3925 if (FAILED(hrc)) return hrc;
3926
3927 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3928 aName,
3929 aControllerPort,
3930 aDevice);
3931 if (!pAttach)
3932 return setError(VBOX_E_OBJECT_NOT_FOUND,
3933 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3934 aDevice, aControllerPort, aName.c_str());
3935
3936
3937 i_setModified(IsModified_Storage);
3938 mMediumAttachments.backup();
3939
3940 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3941
3942 if (pAttach->i_getType() != DeviceType_DVD)
3943 return setError(E_INVALIDARG,
3944 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3945 aDevice, aControllerPort, aName.c_str());
3946 pAttach->i_updateTempEject(!!aTemporaryEject);
3947
3948 return S_OK;
3949}
3950
3951HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3952 LONG aDevice, BOOL aNonRotational)
3953{
3954
3955 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3956 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3957
3958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3959
3960 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3961 if (FAILED(hrc)) return hrc;
3962
3963 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3964
3965 if (Global::IsOnlineOrTransient(mData->mMachineState))
3966 return setError(VBOX_E_INVALID_VM_STATE,
3967 tr("Invalid machine state: %s"),
3968 Global::stringifyMachineState(mData->mMachineState));
3969
3970 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3971 aName,
3972 aControllerPort,
3973 aDevice);
3974 if (!pAttach)
3975 return setError(VBOX_E_OBJECT_NOT_FOUND,
3976 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3977 aDevice, aControllerPort, aName.c_str());
3978
3979
3980 i_setModified(IsModified_Storage);
3981 mMediumAttachments.backup();
3982
3983 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3984
3985 if (pAttach->i_getType() != DeviceType_HardDisk)
3986 return setError(E_INVALIDARG,
3987 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"),
3988 aDevice, aControllerPort, aName.c_str());
3989 pAttach->i_updateNonRotational(!!aNonRotational);
3990
3991 return S_OK;
3992}
3993
3994HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
3995 LONG aDevice, BOOL aDiscard)
3996{
3997
3998 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
3999 aName.c_str(), aControllerPort, aDevice, aDiscard));
4000
4001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4002
4003 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4004 if (FAILED(hrc)) return hrc;
4005
4006 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4007
4008 if (Global::IsOnlineOrTransient(mData->mMachineState))
4009 return setError(VBOX_E_INVALID_VM_STATE,
4010 tr("Invalid machine state: %s"),
4011 Global::stringifyMachineState(mData->mMachineState));
4012
4013 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4014 aName,
4015 aControllerPort,
4016 aDevice);
4017 if (!pAttach)
4018 return setError(VBOX_E_OBJECT_NOT_FOUND,
4019 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4020 aDevice, aControllerPort, aName.c_str());
4021
4022
4023 i_setModified(IsModified_Storage);
4024 mMediumAttachments.backup();
4025
4026 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4027
4028 if (pAttach->i_getType() != DeviceType_HardDisk)
4029 return setError(E_INVALIDARG,
4030 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"),
4031 aDevice, aControllerPort, aName.c_str());
4032 pAttach->i_updateDiscard(!!aDiscard);
4033
4034 return S_OK;
4035}
4036
4037HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4038 LONG aDevice, BOOL aHotPluggable)
4039{
4040 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4041 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4042
4043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4044
4045 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4046 if (FAILED(hrc)) return hrc;
4047
4048 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4049
4050 if (Global::IsOnlineOrTransient(mData->mMachineState))
4051 return setError(VBOX_E_INVALID_VM_STATE,
4052 tr("Invalid machine state: %s"),
4053 Global::stringifyMachineState(mData->mMachineState));
4054
4055 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4056 aName,
4057 aControllerPort,
4058 aDevice);
4059 if (!pAttach)
4060 return setError(VBOX_E_OBJECT_NOT_FOUND,
4061 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4062 aDevice, aControllerPort, aName.c_str());
4063
4064 /* Check for an existing controller. */
4065 ComObjPtr<StorageController> ctl;
4066 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4067 if (FAILED(hrc)) return hrc;
4068
4069 StorageControllerType_T ctrlType;
4070 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4071 if (FAILED(hrc))
4072 return setError(E_FAIL,
4073 tr("Could not get type of controller '%s'"),
4074 aName.c_str());
4075
4076 if (!i_isControllerHotplugCapable(ctrlType))
4077 return setError(VBOX_E_NOT_SUPPORTED,
4078 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4079 aName.c_str());
4080
4081 /* silently ignore attempts to modify the hot-plug status of USB devices */
4082 if (ctrlType == StorageControllerType_USB)
4083 return S_OK;
4084
4085 i_setModified(IsModified_Storage);
4086 mMediumAttachments.backup();
4087
4088 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4089
4090 if (pAttach->i_getType() == DeviceType_Floppy)
4091 return setError(E_INVALIDARG,
4092 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"),
4093 aDevice, aControllerPort, aName.c_str());
4094 pAttach->i_updateHotPluggable(!!aHotPluggable);
4095
4096 return S_OK;
4097}
4098
4099HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4100 LONG aDevice)
4101{
4102 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4103 aName.c_str(), aControllerPort, aDevice));
4104
4105 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4106}
4107
4108HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4109 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4110{
4111 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4112 aName.c_str(), aControllerPort, aDevice));
4113
4114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4115
4116 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4117 if (FAILED(hrc)) return hrc;
4118
4119 if (Global::IsOnlineOrTransient(mData->mMachineState))
4120 return setError(VBOX_E_INVALID_VM_STATE,
4121 tr("Invalid machine state: %s"),
4122 Global::stringifyMachineState(mData->mMachineState));
4123
4124 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4125 aName,
4126 aControllerPort,
4127 aDevice);
4128 if (!pAttach)
4129 return setError(VBOX_E_OBJECT_NOT_FOUND,
4130 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4131 aDevice, aControllerPort, aName.c_str());
4132
4133
4134 i_setModified(IsModified_Storage);
4135 mMediumAttachments.backup();
4136
4137 IBandwidthGroup *iB = aBandwidthGroup;
4138 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4139 if (aBandwidthGroup && group.isNull())
4140 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4141
4142 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4143
4144 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4145 if (strBandwidthGroupOld.isNotEmpty())
4146 {
4147 /* Get the bandwidth group object and release it - this must not fail. */
4148 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4149 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4150 Assert(SUCCEEDED(hrc));
4151
4152 pBandwidthGroupOld->i_release();
4153 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4154 }
4155
4156 if (!group.isNull())
4157 {
4158 group->i_reference();
4159 pAttach->i_updateBandwidthGroup(group->i_getName());
4160 }
4161
4162 return S_OK;
4163}
4164
4165HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4166 LONG aControllerPort,
4167 LONG aDevice,
4168 DeviceType_T aType)
4169{
4170 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4171 aName.c_str(), aControllerPort, aDevice, aType));
4172
4173 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4174}
4175
4176
4177HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4178 LONG aControllerPort,
4179 LONG aDevice,
4180 BOOL aForce)
4181{
4182 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4183 aName.c_str(), aControllerPort, aForce));
4184
4185 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4186}
4187
4188HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4189 LONG aControllerPort,
4190 LONG aDevice,
4191 const ComPtr<IMedium> &aMedium,
4192 BOOL aForce)
4193{
4194 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4195 aName.c_str(), aControllerPort, aDevice, aForce));
4196
4197 // request the host lock first, since might be calling Host methods for getting host drives;
4198 // next, protect the media tree all the while we're in here, as well as our member variables
4199 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4200 this->lockHandle(),
4201 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4202
4203 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4204 if (FAILED(hrc)) return hrc;
4205
4206 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4207 aName,
4208 aControllerPort,
4209 aDevice);
4210 if (pAttach.isNull())
4211 return setError(VBOX_E_OBJECT_NOT_FOUND,
4212 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4213 aDevice, aControllerPort, aName.c_str());
4214
4215 /* Remember previously mounted medium. The medium before taking the
4216 * backup is not necessarily the same thing. */
4217 ComObjPtr<Medium> oldmedium;
4218 oldmedium = pAttach->i_getMedium();
4219
4220 IMedium *iM = aMedium;
4221 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4222 if (aMedium && pMedium.isNull())
4223 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4224
4225 /* Check if potential medium is already mounted */
4226 if (pMedium == oldmedium)
4227 return S_OK;
4228
4229 AutoCaller mediumCaller(pMedium);
4230 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4231
4232 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4233 if (pMedium)
4234 {
4235 DeviceType_T mediumType = pAttach->i_getType();
4236 switch (mediumType)
4237 {
4238 case DeviceType_DVD:
4239 case DeviceType_Floppy:
4240 break;
4241
4242 default:
4243 return setError(VBOX_E_INVALID_OBJECT_STATE,
4244 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4245 aControllerPort,
4246 aDevice,
4247 aName.c_str());
4248 }
4249 }
4250
4251 i_setModified(IsModified_Storage);
4252 mMediumAttachments.backup();
4253
4254 {
4255 // The backup operation makes the pAttach reference point to the
4256 // old settings. Re-get the correct reference.
4257 pAttach = i_findAttachment(*mMediumAttachments.data(),
4258 aName,
4259 aControllerPort,
4260 aDevice);
4261 if (!oldmedium.isNull())
4262 oldmedium->i_removeBackReference(mData->mUuid);
4263 if (!pMedium.isNull())
4264 {
4265 pMedium->i_addBackReference(mData->mUuid);
4266
4267 mediumLock.release();
4268 multiLock.release();
4269 i_addMediumToRegistry(pMedium);
4270 multiLock.acquire();
4271 mediumLock.acquire();
4272 }
4273
4274 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4275 pAttach->i_updateMedium(pMedium);
4276 }
4277
4278 i_setModified(IsModified_Storage);
4279
4280 mediumLock.release();
4281 multiLock.release();
4282 hrc = i_onMediumChange(pAttach, aForce);
4283 multiLock.acquire();
4284 mediumLock.acquire();
4285
4286 /* On error roll back this change only. */
4287 if (FAILED(hrc))
4288 {
4289 if (!pMedium.isNull())
4290 pMedium->i_removeBackReference(mData->mUuid);
4291 pAttach = i_findAttachment(*mMediumAttachments.data(),
4292 aName,
4293 aControllerPort,
4294 aDevice);
4295 /* If the attachment is gone in the meantime, bail out. */
4296 if (pAttach.isNull())
4297 return hrc;
4298 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4299 if (!oldmedium.isNull())
4300 oldmedium->i_addBackReference(mData->mUuid);
4301 pAttach->i_updateMedium(oldmedium);
4302 }
4303
4304 mediumLock.release();
4305 multiLock.release();
4306
4307 /* Save modified registries, but skip this machine as it's the caller's
4308 * job to save its settings like all other settings changes. */
4309 mParent->i_unmarkRegistryModified(i_getId());
4310 mParent->i_saveModifiedRegistries();
4311
4312 return hrc;
4313}
4314HRESULT Machine::getMedium(const com::Utf8Str &aName,
4315 LONG aControllerPort,
4316 LONG aDevice,
4317 ComPtr<IMedium> &aMedium)
4318{
4319 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4320 aName.c_str(), aControllerPort, aDevice));
4321
4322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4323
4324 aMedium = NULL;
4325
4326 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4327 aName,
4328 aControllerPort,
4329 aDevice);
4330 if (pAttach.isNull())
4331 return setError(VBOX_E_OBJECT_NOT_FOUND,
4332 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4333 aDevice, aControllerPort, aName.c_str());
4334
4335 aMedium = pAttach->i_getMedium();
4336
4337 return S_OK;
4338}
4339
4340HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4341{
4342 if (aSlot < RT_ELEMENTS(mSerialPorts))
4343 {
4344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4345 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4346 return S_OK;
4347 }
4348 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4349}
4350
4351HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4352{
4353 if (aSlot < RT_ELEMENTS(mParallelPorts))
4354 {
4355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4356 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4357 return S_OK;
4358 }
4359 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4360}
4361
4362
4363HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4364{
4365 /* Do not assert if slot is out of range, just return the advertised
4366 status. testdriver/vbox.py triggers this in logVmInfo. */
4367 if (aSlot >= mNetworkAdapters.size())
4368 return setError(E_INVALIDARG,
4369 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4370 aSlot, mNetworkAdapters.size());
4371
4372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4373
4374 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4375
4376 return S_OK;
4377}
4378
4379HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4380{
4381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4382
4383 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4384 size_t i = 0;
4385 for (settings::StringsMap::const_iterator
4386 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4387 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4388 ++it, ++i)
4389 aKeys[i] = it->first;
4390
4391 return S_OK;
4392}
4393
4394 /**
4395 * @note Locks this object for reading.
4396 */
4397HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4398 com::Utf8Str &aValue)
4399{
4400 /* start with nothing found */
4401 aValue = "";
4402
4403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4404
4405 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4406 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4407 // found:
4408 aValue = it->second; // source is a Utf8Str
4409
4410 /* return the result to caller (may be empty) */
4411 return S_OK;
4412}
4413
4414 /**
4415 * @note Locks mParent for writing + this object for writing.
4416 */
4417HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4418{
4419 /* Because control characters in aKey have caused problems in the settings
4420 * they are rejected unless the key should be deleted. */
4421 if (!aValue.isEmpty())
4422 {
4423 for (size_t i = 0; i < aKey.length(); ++i)
4424 {
4425 char ch = aKey[i];
4426 if (RTLocCIsCntrl(ch))
4427 return E_INVALIDARG;
4428 }
4429 }
4430
4431 Utf8Str strOldValue; // empty
4432
4433 // locking note: we only hold the read lock briefly to look up the old value,
4434 // then release it and call the onExtraCanChange callbacks. There is a small
4435 // chance of a race insofar as the callback might be called twice if two callers
4436 // change the same key at the same time, but that's a much better solution
4437 // than the deadlock we had here before. The actual changing of the extradata
4438 // is then performed under the write lock and race-free.
4439
4440 // look up the old value first; if nothing has changed then we need not do anything
4441 {
4442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4443
4444 // For snapshots don't even think about allowing changes, extradata
4445 // is global for a machine, so there is nothing snapshot specific.
4446 if (i_isSnapshotMachine())
4447 return setError(VBOX_E_INVALID_VM_STATE,
4448 tr("Cannot set extradata for a snapshot"));
4449
4450 // check if the right IMachine instance is used
4451 if (mData->mRegistered && !i_isSessionMachine())
4452 return setError(VBOX_E_INVALID_VM_STATE,
4453 tr("Cannot set extradata for an immutable machine"));
4454
4455 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4456 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4457 strOldValue = it->second;
4458 }
4459
4460 bool fChanged;
4461 if ((fChanged = (strOldValue != aValue)))
4462 {
4463 // ask for permission from all listeners outside the locks;
4464 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4465 // lock to copy the list of callbacks to invoke
4466 Bstr bstrError;
4467 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4468 {
4469 const char *sep = bstrError.isEmpty() ? "" : ": ";
4470 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4471 return setError(E_ACCESSDENIED,
4472 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4473 aKey.c_str(),
4474 aValue.c_str(),
4475 sep,
4476 bstrError.raw());
4477 }
4478
4479 // data is changing and change not vetoed: then write it out under the lock
4480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4481
4482 if (aValue.isEmpty())
4483 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4484 else
4485 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4486 // creates a new key if needed
4487
4488 bool fNeedsGlobalSaveSettings = false;
4489 // This saving of settings is tricky: there is no "old state" for the
4490 // extradata items at all (unlike all other settings), so the old/new
4491 // settings comparison would give a wrong result!
4492 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4493
4494 if (fNeedsGlobalSaveSettings)
4495 {
4496 // save the global settings; for that we should hold only the VirtualBox lock
4497 alock.release();
4498 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4499 mParent->i_saveSettings();
4500 }
4501 }
4502
4503 // fire notification outside the lock
4504 if (fChanged)
4505 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4506
4507 return S_OK;
4508}
4509
4510HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4511{
4512 aProgress = NULL;
4513 NOREF(aSettingsFilePath);
4514 ReturnComNotImplemented();
4515}
4516
4517HRESULT Machine::saveSettings()
4518{
4519 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4520
4521 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4522 if (FAILED(hrc)) return hrc;
4523
4524 /* the settings file path may never be null */
4525 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4526
4527 /* save all VM data excluding snapshots */
4528 bool fNeedsGlobalSaveSettings = false;
4529 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4530 mlock.release();
4531
4532 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4533 {
4534 // save the global settings; for that we should hold only the VirtualBox lock
4535 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4536 hrc = mParent->i_saveSettings();
4537 }
4538
4539 return hrc;
4540}
4541
4542
4543HRESULT Machine::discardSettings()
4544{
4545 /*
4546 * We need to take the machine list lock here as well as the machine one
4547 * or we'll get into trouble should any media stuff require rolling back.
4548 *
4549 * Details:
4550 *
4551 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4552 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4553 * 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]
4554 * 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
4555 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4556 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4557 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4558 * 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
4559 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4560 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4561 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4562 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4563 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4564 * 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]
4565 * 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] (*)
4566 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4567 * 0:005> k
4568 * # Child-SP RetAddr Call Site
4569 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4570 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4571 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4572 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4573 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4574 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4575 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4576 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4577 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4578 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4579 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4580 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4581 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4582 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4583 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4584 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4585 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4586 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4587 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4588 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4589 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4590 *
4591 */
4592 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4594
4595 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4596 if (FAILED(hrc)) return hrc;
4597
4598 /*
4599 * during this rollback, the session will be notified if data has
4600 * been actually changed
4601 */
4602 i_rollback(true /* aNotify */);
4603
4604 return S_OK;
4605}
4606
4607/** @note Locks objects! */
4608HRESULT Machine::unregister(AutoCaller &autoCaller,
4609 CleanupMode_T aCleanupMode,
4610 std::vector<ComPtr<IMedium> > &aMedia)
4611{
4612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4613
4614 Guid id(i_getId());
4615
4616 if (mData->mSession.mState != SessionState_Unlocked)
4617 return setError(VBOX_E_INVALID_OBJECT_STATE,
4618 tr("Cannot unregister the machine '%s' while it is locked"),
4619 mUserData->s.strName.c_str());
4620
4621 // wait for state dependents to drop to zero
4622 i_ensureNoStateDependencies(alock);
4623
4624 if (!mData->mAccessible)
4625 {
4626 // inaccessible machines can only be unregistered; uninitialize ourselves
4627 // here because currently there may be no unregistered that are inaccessible
4628 // (this state combination is not supported). Note releasing the caller and
4629 // leaving the lock before calling uninit()
4630 alock.release();
4631 autoCaller.release();
4632
4633 uninit();
4634
4635 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4636 // calls VirtualBox::i_saveSettings()
4637
4638 return S_OK;
4639 }
4640
4641 HRESULT hrc = S_OK;
4642 mData->llFilesToDelete.clear();
4643
4644 if (!mSSData->strStateFilePath.isEmpty())
4645 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4646
4647 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4648 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4649 mData->llFilesToDelete.push_back(strNVRAMFile);
4650
4651 // This list collects the medium objects from all medium attachments
4652 // which we will detach from the machine and its snapshots, in a specific
4653 // order which allows for closing all media without getting "media in use"
4654 // errors, simply by going through the list from the front to the back:
4655 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4656 // and must be closed before the parent media from the snapshots, or closing the parents
4657 // will fail because they still have children);
4658 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4659 // the root ("first") snapshot of the machine.
4660 MediaList llMedia;
4661
4662 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4663 && mMediumAttachments->size()
4664 )
4665 {
4666 // we have media attachments: detach them all and add the Medium objects to our list
4667 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4668 }
4669
4670 if (mData->mFirstSnapshot)
4671 {
4672 // add the media from the medium attachments of the snapshots to
4673 // llMedia as well, after the "main" machine media;
4674 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4675 // snapshot machine, depth first.
4676
4677 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4678 MachineState_T oldState = mData->mMachineState;
4679 mData->mMachineState = MachineState_DeletingSnapshot;
4680
4681 // make a copy of the first snapshot reference so the refcount does not
4682 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4683 // (would hang due to the AutoCaller voodoo)
4684 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4685
4686 // GO!
4687 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4688
4689 mData->mMachineState = oldState;
4690 }
4691
4692 if (FAILED(hrc))
4693 {
4694 i_rollbackMedia();
4695 return hrc;
4696 }
4697
4698 // commit all the media changes made above
4699 i_commitMedia();
4700
4701 mData->mRegistered = false;
4702
4703 // machine lock no longer needed
4704 alock.release();
4705
4706 /* Make sure that the settings of the current VM are not saved, because
4707 * they are rather crippled at this point to meet the cleanup expectations
4708 * and there's no point destroying the VM config on disk just because. */
4709 mParent->i_unmarkRegistryModified(id);
4710
4711 // return media to caller
4712 aMedia.resize(llMedia.size());
4713 size_t i = 0;
4714 for (MediaList::const_iterator
4715 it = llMedia.begin();
4716 it != llMedia.end();
4717 ++it, ++i)
4718 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4719
4720 mParent->i_unregisterMachine(this, aCleanupMode, id);
4721 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4722
4723 return S_OK;
4724}
4725
4726/**
4727 * Task record for deleting a machine config.
4728 */
4729class Machine::DeleteConfigTask
4730 : public Machine::Task
4731{
4732public:
4733 DeleteConfigTask(Machine *m,
4734 Progress *p,
4735 const Utf8Str &t,
4736 const RTCList<ComPtr<IMedium> > &llMedia,
4737 const StringsList &llFilesToDelete)
4738 : Task(m, p, t),
4739 m_llMedia(llMedia),
4740 m_llFilesToDelete(llFilesToDelete)
4741 {}
4742
4743private:
4744 void handler()
4745 {
4746 try
4747 {
4748 m_pMachine->i_deleteConfigHandler(*this);
4749 }
4750 catch (...)
4751 {
4752 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4753 }
4754 }
4755
4756 RTCList<ComPtr<IMedium> > m_llMedia;
4757 StringsList m_llFilesToDelete;
4758
4759 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4760};
4761
4762/**
4763 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4764 * SessionMachine::taskHandler().
4765 *
4766 * @note Locks this object for writing.
4767 *
4768 * @param task
4769 */
4770void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4771{
4772 LogFlowThisFuncEnter();
4773
4774 AutoCaller autoCaller(this);
4775 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4776 if (FAILED(autoCaller.hrc()))
4777 {
4778 /* we might have been uninitialized because the session was accidentally
4779 * closed by the client, so don't assert */
4780 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4781 task.m_pProgress->i_notifyComplete(hrc);
4782 LogFlowThisFuncLeave();
4783 return;
4784 }
4785
4786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4787
4788 HRESULT hrc;
4789 try
4790 {
4791 ULONG uLogHistoryCount = 3;
4792 ComPtr<ISystemProperties> systemProperties;
4793 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4794 if (FAILED(hrc)) throw hrc;
4795
4796 if (!systemProperties.isNull())
4797 {
4798 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4799 if (FAILED(hrc)) throw hrc;
4800 }
4801
4802 MachineState_T oldState = mData->mMachineState;
4803 i_setMachineState(MachineState_SettingUp);
4804 alock.release();
4805 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4806 {
4807 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4808 {
4809 AutoCaller mac(pMedium);
4810 if (FAILED(mac.hrc())) throw mac.hrc();
4811 Utf8Str strLocation = pMedium->i_getLocationFull();
4812 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4813 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4814 if (FAILED(hrc)) throw hrc;
4815 }
4816 if (pMedium->i_isMediumFormatFile())
4817 {
4818 ComPtr<IProgress> pProgress2;
4819 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4820 if (FAILED(hrc)) throw hrc;
4821 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4822 if (FAILED(hrc)) throw hrc;
4823 }
4824
4825 /* Close the medium, deliberately without checking the return
4826 * code, and without leaving any trace in the error info, as
4827 * a failure here is a very minor issue, which shouldn't happen
4828 * as above we even managed to delete the medium. */
4829 {
4830 ErrorInfoKeeper eik;
4831 pMedium->Close();
4832 }
4833 }
4834 i_setMachineState(oldState);
4835 alock.acquire();
4836
4837 // delete the files pushed on the task list by Machine::Delete()
4838 // (this includes saved states of the machine and snapshots and
4839 // medium storage files from the IMedium list passed in, and the
4840 // machine XML file)
4841 for (StringsList::const_iterator
4842 it = task.m_llFilesToDelete.begin();
4843 it != task.m_llFilesToDelete.end();
4844 ++it)
4845 {
4846 const Utf8Str &strFile = *it;
4847 LogFunc(("Deleting file %s\n", strFile.c_str()));
4848 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4849 if (FAILED(hrc)) throw hrc;
4850 i_deleteFile(strFile);
4851 }
4852
4853 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4854 if (FAILED(hrc)) throw hrc;
4855
4856 /* delete the settings only when the file actually exists */
4857 if (mData->pMachineConfigFile->fileExists())
4858 {
4859 /* Delete any backup or uncommitted XML files. Ignore failures.
4860 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4861 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4862 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4863 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4864 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4865 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4866
4867 /* delete the Logs folder, nothing important should be left
4868 * there (we don't check for errors because the user might have
4869 * some private files there that we don't want to delete) */
4870 Utf8Str logFolder;
4871 getLogFolder(logFolder);
4872 Assert(logFolder.length());
4873 if (RTDirExists(logFolder.c_str()))
4874 {
4875 /* Delete all VBox.log[.N] files from the Logs folder
4876 * (this must be in sync with the rotation logic in
4877 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4878 * files that may have been created by the GUI. */
4879 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4880 i_deleteFile(log, true /* fIgnoreFailures */);
4881 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4882 i_deleteFile(log, true /* fIgnoreFailures */);
4883 for (ULONG i = uLogHistoryCount; i > 0; i--)
4884 {
4885 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4886 i_deleteFile(log, true /* fIgnoreFailures */);
4887 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4888 i_deleteFile(log, true /* fIgnoreFailures */);
4889 }
4890 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4891 i_deleteFile(log, true /* fIgnoreFailures */);
4892#if defined(RT_OS_WINDOWS)
4893 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4894 i_deleteFile(log, true /* fIgnoreFailures */);
4895 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4896 i_deleteFile(log, true /* fIgnoreFailures */);
4897#endif
4898
4899 RTDirRemove(logFolder.c_str());
4900 }
4901
4902 /* delete the Snapshots folder, nothing important should be left
4903 * there (we don't check for errors because the user might have
4904 * some private files there that we don't want to delete) */
4905 Utf8Str strFullSnapshotFolder;
4906 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4907 Assert(!strFullSnapshotFolder.isEmpty());
4908 if (RTDirExists(strFullSnapshotFolder.c_str()))
4909 RTDirRemove(strFullSnapshotFolder.c_str());
4910
4911 // delete the directory that contains the settings file, but only
4912 // if it matches the VM name
4913 Utf8Str settingsDir;
4914 if (i_isInOwnDir(&settingsDir))
4915 RTDirRemove(settingsDir.c_str());
4916 }
4917
4918 alock.release();
4919
4920 mParent->i_saveModifiedRegistries();
4921 }
4922 catch (HRESULT hrcXcpt)
4923 {
4924 hrc = hrcXcpt;
4925 }
4926
4927 task.m_pProgress->i_notifyComplete(hrc);
4928
4929 LogFlowThisFuncLeave();
4930}
4931
4932HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4933{
4934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4935
4936 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4937 if (FAILED(hrc)) return hrc;
4938
4939 if (mData->mRegistered)
4940 return setError(VBOX_E_INVALID_VM_STATE,
4941 tr("Cannot delete settings of a registered machine"));
4942
4943 // collect files to delete
4944 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4945 // machine config file
4946 if (mData->pMachineConfigFile->fileExists())
4947 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4948 // backup of machine config file
4949 Utf8Str strTmp(mData->m_strConfigFileFull);
4950 strTmp.append("-prev");
4951 if (RTFileExists(strTmp.c_str()))
4952 llFilesToDelete.push_back(strTmp);
4953
4954 RTCList<ComPtr<IMedium> > llMedia;
4955 for (size_t i = 0; i < aMedia.size(); ++i)
4956 {
4957 IMedium *pIMedium(aMedia[i]);
4958 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4959 if (pMedium.isNull())
4960 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4961 SafeArray<BSTR> ids;
4962 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4963 if (FAILED(hrc)) return hrc;
4964 /* At this point the medium should not have any back references
4965 * anymore. If it has it is attached to another VM and *must* not
4966 * deleted. */
4967 if (ids.size() < 1)
4968 llMedia.append(pMedium);
4969 }
4970
4971 ComObjPtr<Progress> pProgress;
4972 pProgress.createObject();
4973 hrc = pProgress->init(i_getVirtualBox(),
4974 static_cast<IMachine*>(this) /* aInitiator */,
4975 tr("Deleting files"),
4976 true /* fCancellable */,
4977 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4978 tr("Collecting file inventory"));
4979 if (FAILED(hrc))
4980 return hrc;
4981
4982 /* create and start the task on a separate thread (note that it will not
4983 * start working until we release alock) */
4984 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
4985 hrc = pTask->createThread();
4986 pTask = NULL;
4987 if (FAILED(hrc))
4988 return hrc;
4989
4990 pProgress.queryInterfaceTo(aProgress.asOutParam());
4991
4992 LogFlowFuncLeave();
4993
4994 return S_OK;
4995}
4996
4997HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
4998{
4999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5000
5001 ComObjPtr<Snapshot> pSnapshot;
5002 HRESULT hrc;
5003
5004 if (aNameOrId.isEmpty())
5005 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5006 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5007 else
5008 {
5009 Guid uuid(aNameOrId);
5010 if (uuid.isValid())
5011 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5012 else
5013 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5014 }
5015 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5016
5017 return hrc;
5018}
5019
5020HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5021 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5022{
5023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5024
5025 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5026 if (FAILED(hrc)) return hrc;
5027
5028 ComObjPtr<SharedFolder> sharedFolder;
5029 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5030 if (SUCCEEDED(hrc))
5031 return setError(VBOX_E_OBJECT_IN_USE,
5032 tr("Shared folder named '%s' already exists"),
5033 aName.c_str());
5034
5035 SymlinkPolicy_T enmSymlinkPolicy = SymlinkPolicy_None;
5036 sharedFolder.createObject();
5037 hrc = sharedFolder->init(i_getMachine(),
5038 aName,
5039 aHostPath,
5040 !!aWritable,
5041 !!aAutomount,
5042 aAutoMountPoint,
5043 true /* fFailOnError */,
5044 enmSymlinkPolicy);
5045 if (FAILED(hrc)) return hrc;
5046
5047 i_setModified(IsModified_SharedFolders);
5048 mHWData.backup();
5049 mHWData->mSharedFolders.push_back(sharedFolder);
5050
5051 /* inform the direct session if any */
5052 alock.release();
5053 i_onSharedFolderChange();
5054
5055 return S_OK;
5056}
5057
5058HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5059{
5060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5061
5062 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5063 if (FAILED(hrc)) return hrc;
5064
5065 ComObjPtr<SharedFolder> sharedFolder;
5066 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5067 if (FAILED(hrc)) return hrc;
5068
5069 i_setModified(IsModified_SharedFolders);
5070 mHWData.backup();
5071 mHWData->mSharedFolders.remove(sharedFolder);
5072
5073 /* inform the direct session if any */
5074 alock.release();
5075 i_onSharedFolderChange();
5076
5077 return S_OK;
5078}
5079
5080HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5081{
5082 /* start with No */
5083 *aCanShow = FALSE;
5084
5085 ComPtr<IInternalSessionControl> directControl;
5086 {
5087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5088
5089 if (mData->mSession.mState != SessionState_Locked)
5090 return setError(VBOX_E_INVALID_VM_STATE,
5091 tr("Machine is not locked for session (session state: %s)"),
5092 Global::stringifySessionState(mData->mSession.mState));
5093
5094 if (mData->mSession.mLockType == LockType_VM)
5095 directControl = mData->mSession.mDirectControl;
5096 }
5097
5098 /* ignore calls made after #OnSessionEnd() is called */
5099 if (!directControl)
5100 return S_OK;
5101
5102 LONG64 dummy;
5103 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5104}
5105
5106HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5107{
5108 ComPtr<IInternalSessionControl> directControl;
5109 {
5110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5111
5112 if (mData->mSession.mState != SessionState_Locked)
5113 return setError(E_FAIL,
5114 tr("Machine is not locked for session (session state: %s)"),
5115 Global::stringifySessionState(mData->mSession.mState));
5116
5117 if (mData->mSession.mLockType == LockType_VM)
5118 directControl = mData->mSession.mDirectControl;
5119 }
5120
5121 /* ignore calls made after #OnSessionEnd() is called */
5122 if (!directControl)
5123 return S_OK;
5124
5125 BOOL dummy;
5126 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5127}
5128
5129#ifdef VBOX_WITH_GUEST_PROPS
5130/**
5131 * Look up a guest property in VBoxSVC's internal structures.
5132 */
5133HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5134 com::Utf8Str &aValue,
5135 LONG64 *aTimestamp,
5136 com::Utf8Str &aFlags) const
5137{
5138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5139
5140 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5141 if (it != mHWData->mGuestProperties.end())
5142 {
5143 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5144 aValue = it->second.strValue;
5145 *aTimestamp = it->second.mTimestamp;
5146 GuestPropWriteFlags(it->second.mFlags, szFlags);
5147 aFlags = Utf8Str(szFlags);
5148 }
5149
5150 return S_OK;
5151}
5152
5153/**
5154 * Query the VM that a guest property belongs to for the property.
5155 * @returns E_ACCESSDENIED if the VM process is not available or not
5156 * currently handling queries and the lookup should then be done in
5157 * VBoxSVC.
5158 */
5159HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5160 com::Utf8Str &aValue,
5161 LONG64 *aTimestamp,
5162 com::Utf8Str &aFlags) const
5163{
5164 HRESULT hrc = S_OK;
5165 Bstr bstrValue;
5166 Bstr bstrFlags;
5167
5168 ComPtr<IInternalSessionControl> directControl;
5169 {
5170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5171 if (mData->mSession.mLockType == LockType_VM)
5172 directControl = mData->mSession.mDirectControl;
5173 }
5174
5175 /* ignore calls made after #OnSessionEnd() is called */
5176 if (!directControl)
5177 hrc = E_ACCESSDENIED;
5178 else
5179 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5180 0 /* accessMode */,
5181 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5182
5183 aValue = bstrValue;
5184 aFlags = bstrFlags;
5185
5186 return hrc;
5187}
5188#endif // VBOX_WITH_GUEST_PROPS
5189
5190HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5191 com::Utf8Str &aValue,
5192 LONG64 *aTimestamp,
5193 com::Utf8Str &aFlags)
5194{
5195#ifndef VBOX_WITH_GUEST_PROPS
5196 ReturnComNotImplemented();
5197#else // VBOX_WITH_GUEST_PROPS
5198
5199 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5200
5201 if (hrc == E_ACCESSDENIED)
5202 /* The VM is not running or the service is not (yet) accessible */
5203 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5204 return hrc;
5205#endif // VBOX_WITH_GUEST_PROPS
5206}
5207
5208HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5209{
5210 LONG64 dummyTimestamp;
5211 com::Utf8Str dummyFlags;
5212 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5213
5214}
5215HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5216{
5217 com::Utf8Str dummyFlags;
5218 com::Utf8Str dummyValue;
5219 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5220}
5221
5222#ifdef VBOX_WITH_GUEST_PROPS
5223/**
5224 * Set a guest property in VBoxSVC's internal structures.
5225 */
5226HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5227 const com::Utf8Str &aFlags, bool fDelete)
5228{
5229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5230 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5231 if (FAILED(hrc)) return hrc;
5232
5233 try
5234 {
5235 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5236 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5237 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5238
5239 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5240 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5241
5242 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5243 if (it == mHWData->mGuestProperties.end())
5244 {
5245 if (!fDelete)
5246 {
5247 i_setModified(IsModified_MachineData);
5248 mHWData.backupEx();
5249
5250 RTTIMESPEC time;
5251 HWData::GuestProperty prop;
5252 prop.strValue = aValue;
5253 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5254 prop.mFlags = fFlags;
5255 mHWData->mGuestProperties[aName] = prop;
5256 }
5257 }
5258 else
5259 {
5260 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5261 {
5262 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5263 }
5264 else
5265 {
5266 i_setModified(IsModified_MachineData);
5267 mHWData.backupEx();
5268
5269 /* The backupEx() operation invalidates our iterator,
5270 * so get a new one. */
5271 it = mHWData->mGuestProperties.find(aName);
5272 Assert(it != mHWData->mGuestProperties.end());
5273
5274 if (!fDelete)
5275 {
5276 RTTIMESPEC time;
5277 it->second.strValue = aValue;
5278 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5279 it->second.mFlags = fFlags;
5280 }
5281 else
5282 mHWData->mGuestProperties.erase(it);
5283 }
5284 }
5285
5286 if (SUCCEEDED(hrc))
5287 {
5288 alock.release();
5289
5290 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5291 }
5292 }
5293 catch (std::bad_alloc &)
5294 {
5295 hrc = E_OUTOFMEMORY;
5296 }
5297
5298 return hrc;
5299}
5300
5301/**
5302 * Set a property on the VM that that property belongs to.
5303 * @returns E_ACCESSDENIED if the VM process is not available or not
5304 * currently handling queries and the setting should then be done in
5305 * VBoxSVC.
5306 */
5307HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5308 const com::Utf8Str &aFlags, bool fDelete)
5309{
5310 HRESULT hrc;
5311
5312 try
5313 {
5314 ComPtr<IInternalSessionControl> directControl;
5315 {
5316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5317 if (mData->mSession.mLockType == LockType_VM)
5318 directControl = mData->mSession.mDirectControl;
5319 }
5320
5321 Bstr dummy1; /* will not be changed (setter) */
5322 Bstr dummy2; /* will not be changed (setter) */
5323 LONG64 dummy64;
5324 if (!directControl)
5325 hrc = E_ACCESSDENIED;
5326 else
5327 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5328 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5329 fDelete ? 2 : 1 /* accessMode */,
5330 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5331 }
5332 catch (std::bad_alloc &)
5333 {
5334 hrc = E_OUTOFMEMORY;
5335 }
5336
5337 return hrc;
5338}
5339#endif // VBOX_WITH_GUEST_PROPS
5340
5341HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5342 const com::Utf8Str &aFlags)
5343{
5344#ifndef VBOX_WITH_GUEST_PROPS
5345 ReturnComNotImplemented();
5346#else // VBOX_WITH_GUEST_PROPS
5347
5348 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5349 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5350
5351 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5352 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5353
5354 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5355 if (hrc == E_ACCESSDENIED)
5356 /* The VM is not running or the service is not (yet) accessible */
5357 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5358 return hrc;
5359#endif // VBOX_WITH_GUEST_PROPS
5360}
5361
5362HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5363{
5364 return setGuestProperty(aProperty, aValue, "");
5365}
5366
5367HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5368{
5369#ifndef VBOX_WITH_GUEST_PROPS
5370 ReturnComNotImplemented();
5371#else // VBOX_WITH_GUEST_PROPS
5372 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5373 if (hrc == E_ACCESSDENIED)
5374 /* The VM is not running or the service is not (yet) accessible */
5375 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5376 return hrc;
5377#endif // VBOX_WITH_GUEST_PROPS
5378}
5379
5380#ifdef VBOX_WITH_GUEST_PROPS
5381/**
5382 * Enumerate the guest properties in VBoxSVC's internal structures.
5383 */
5384HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5385 std::vector<com::Utf8Str> &aNames,
5386 std::vector<com::Utf8Str> &aValues,
5387 std::vector<LONG64> &aTimestamps,
5388 std::vector<com::Utf8Str> &aFlags)
5389{
5390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5391 Utf8Str strPatterns(aPatterns);
5392
5393 /*
5394 * Look for matching patterns and build up a list.
5395 */
5396 HWData::GuestPropertyMap propMap;
5397 for (HWData::GuestPropertyMap::const_iterator
5398 it = mHWData->mGuestProperties.begin();
5399 it != mHWData->mGuestProperties.end();
5400 ++it)
5401 {
5402 if ( strPatterns.isEmpty()
5403 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5404 RTSTR_MAX,
5405 it->first.c_str(),
5406 RTSTR_MAX,
5407 NULL)
5408 )
5409 propMap.insert(*it);
5410 }
5411
5412 alock.release();
5413
5414 /*
5415 * And build up the arrays for returning the property information.
5416 */
5417 size_t cEntries = propMap.size();
5418
5419 aNames.resize(cEntries);
5420 aValues.resize(cEntries);
5421 aTimestamps.resize(cEntries);
5422 aFlags.resize(cEntries);
5423
5424 size_t i = 0;
5425 for (HWData::GuestPropertyMap::const_iterator
5426 it = propMap.begin();
5427 it != propMap.end();
5428 ++it, ++i)
5429 {
5430 aNames[i] = it->first;
5431 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5432 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5433
5434 aValues[i] = it->second.strValue;
5435 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5436 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5437
5438 aTimestamps[i] = it->second.mTimestamp;
5439
5440 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5441 GuestPropWriteFlags(it->second.mFlags, szFlags);
5442 aFlags[i] = szFlags;
5443 }
5444
5445 return S_OK;
5446}
5447
5448/**
5449 * Enumerate the properties managed by a VM.
5450 * @returns E_ACCESSDENIED if the VM process is not available or not
5451 * currently handling queries and the setting should then be done in
5452 * VBoxSVC.
5453 */
5454HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5455 std::vector<com::Utf8Str> &aNames,
5456 std::vector<com::Utf8Str> &aValues,
5457 std::vector<LONG64> &aTimestamps,
5458 std::vector<com::Utf8Str> &aFlags)
5459{
5460 HRESULT hrc;
5461 ComPtr<IInternalSessionControl> directControl;
5462 {
5463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5464 if (mData->mSession.mLockType == LockType_VM)
5465 directControl = mData->mSession.mDirectControl;
5466 }
5467
5468 com::SafeArray<BSTR> bNames;
5469 com::SafeArray<BSTR> bValues;
5470 com::SafeArray<LONG64> bTimestamps;
5471 com::SafeArray<BSTR> bFlags;
5472
5473 if (!directControl)
5474 hrc = E_ACCESSDENIED;
5475 else
5476 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5477 ComSafeArrayAsOutParam(bNames),
5478 ComSafeArrayAsOutParam(bValues),
5479 ComSafeArrayAsOutParam(bTimestamps),
5480 ComSafeArrayAsOutParam(bFlags));
5481 size_t i;
5482 aNames.resize(bNames.size());
5483 for (i = 0; i < bNames.size(); ++i)
5484 aNames[i] = Utf8Str(bNames[i]);
5485 aValues.resize(bValues.size());
5486 for (i = 0; i < bValues.size(); ++i)
5487 aValues[i] = Utf8Str(bValues[i]);
5488 aTimestamps.resize(bTimestamps.size());
5489 for (i = 0; i < bTimestamps.size(); ++i)
5490 aTimestamps[i] = bTimestamps[i];
5491 aFlags.resize(bFlags.size());
5492 for (i = 0; i < bFlags.size(); ++i)
5493 aFlags[i] = Utf8Str(bFlags[i]);
5494
5495 return hrc;
5496}
5497#endif // VBOX_WITH_GUEST_PROPS
5498HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5499 std::vector<com::Utf8Str> &aNames,
5500 std::vector<com::Utf8Str> &aValues,
5501 std::vector<LONG64> &aTimestamps,
5502 std::vector<com::Utf8Str> &aFlags)
5503{
5504#ifndef VBOX_WITH_GUEST_PROPS
5505 ReturnComNotImplemented();
5506#else // VBOX_WITH_GUEST_PROPS
5507
5508 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5509
5510 if (hrc == E_ACCESSDENIED)
5511 /* The VM is not running or the service is not (yet) accessible */
5512 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5513 return hrc;
5514#endif // VBOX_WITH_GUEST_PROPS
5515}
5516
5517HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5518 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5519{
5520 MediumAttachmentList atts;
5521
5522 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5523 if (FAILED(hrc)) return hrc;
5524
5525 aMediumAttachments.resize(atts.size());
5526 size_t i = 0;
5527 for (MediumAttachmentList::const_iterator
5528 it = atts.begin();
5529 it != atts.end();
5530 ++it, ++i)
5531 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5532
5533 return S_OK;
5534}
5535
5536HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5537 LONG aControllerPort,
5538 LONG aDevice,
5539 ComPtr<IMediumAttachment> &aAttachment)
5540{
5541 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5542 aName.c_str(), aControllerPort, aDevice));
5543
5544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5545
5546 aAttachment = NULL;
5547
5548 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5549 aName,
5550 aControllerPort,
5551 aDevice);
5552 if (pAttach.isNull())
5553 return setError(VBOX_E_OBJECT_NOT_FOUND,
5554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5555 aDevice, aControllerPort, aName.c_str());
5556
5557 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5558
5559 return S_OK;
5560}
5561
5562
5563HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5564 StorageBus_T aConnectionType,
5565 ComPtr<IStorageController> &aController)
5566{
5567 if ( (aConnectionType <= StorageBus_Null)
5568 || (aConnectionType > StorageBus_VirtioSCSI))
5569 return setError(E_INVALIDARG,
5570 tr("Invalid connection type: %d"),
5571 aConnectionType);
5572
5573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5576 if (FAILED(hrc)) return hrc;
5577
5578 /* try to find one with the name first. */
5579 ComObjPtr<StorageController> ctrl;
5580
5581 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5582 if (SUCCEEDED(hrc))
5583 return setError(VBOX_E_OBJECT_IN_USE,
5584 tr("Storage controller named '%s' already exists"),
5585 aName.c_str());
5586
5587 ctrl.createObject();
5588
5589 /* get a new instance number for the storage controller */
5590 ULONG ulInstance = 0;
5591 bool fBootable = true;
5592 for (StorageControllerList::const_iterator
5593 it = mStorageControllers->begin();
5594 it != mStorageControllers->end();
5595 ++it)
5596 {
5597 if ((*it)->i_getStorageBus() == aConnectionType)
5598 {
5599 ULONG ulCurInst = (*it)->i_getInstance();
5600
5601 if (ulCurInst >= ulInstance)
5602 ulInstance = ulCurInst + 1;
5603
5604 /* Only one controller of each type can be marked as bootable. */
5605 if ((*it)->i_getBootable())
5606 fBootable = false;
5607 }
5608 }
5609
5610 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5611 if (FAILED(hrc)) return hrc;
5612
5613 i_setModified(IsModified_Storage);
5614 mStorageControllers.backup();
5615 mStorageControllers->push_back(ctrl);
5616
5617 ctrl.queryInterfaceTo(aController.asOutParam());
5618
5619 /* inform the direct session if any */
5620 alock.release();
5621 i_onStorageControllerChange(i_getId(), aName);
5622
5623 return S_OK;
5624}
5625
5626HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5627 ComPtr<IStorageController> &aStorageController)
5628{
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630
5631 ComObjPtr<StorageController> ctrl;
5632
5633 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5634 if (SUCCEEDED(hrc))
5635 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5636
5637 return hrc;
5638}
5639
5640HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5641 ULONG aInstance,
5642 ComPtr<IStorageController> &aStorageController)
5643{
5644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5645
5646 for (StorageControllerList::const_iterator
5647 it = mStorageControllers->begin();
5648 it != mStorageControllers->end();
5649 ++it)
5650 {
5651 if ( (*it)->i_getStorageBus() == aConnectionType
5652 && (*it)->i_getInstance() == aInstance)
5653 {
5654 (*it).queryInterfaceTo(aStorageController.asOutParam());
5655 return S_OK;
5656 }
5657 }
5658
5659 return setError(VBOX_E_OBJECT_NOT_FOUND,
5660 tr("Could not find a storage controller with instance number '%lu'"),
5661 aInstance);
5662}
5663
5664HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5665{
5666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5667
5668 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5669 if (FAILED(hrc)) return hrc;
5670
5671 ComObjPtr<StorageController> ctrl;
5672
5673 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5674 if (SUCCEEDED(hrc))
5675 {
5676 /* Ensure that only one controller of each type is marked as bootable. */
5677 if (aBootable == TRUE)
5678 {
5679 for (StorageControllerList::const_iterator
5680 it = mStorageControllers->begin();
5681 it != mStorageControllers->end();
5682 ++it)
5683 {
5684 ComObjPtr<StorageController> aCtrl = (*it);
5685
5686 if ( (aCtrl->i_getName() != aName)
5687 && aCtrl->i_getBootable() == TRUE
5688 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5689 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5690 {
5691 aCtrl->i_setBootable(FALSE);
5692 break;
5693 }
5694 }
5695 }
5696
5697 if (SUCCEEDED(hrc))
5698 {
5699 ctrl->i_setBootable(aBootable);
5700 i_setModified(IsModified_Storage);
5701 }
5702 }
5703
5704 if (SUCCEEDED(hrc))
5705 {
5706 /* inform the direct session if any */
5707 alock.release();
5708 i_onStorageControllerChange(i_getId(), aName);
5709 }
5710
5711 return hrc;
5712}
5713
5714HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5715{
5716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5717
5718 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5719 if (FAILED(hrc)) return hrc;
5720
5721 ComObjPtr<StorageController> ctrl;
5722 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5723 if (FAILED(hrc)) return hrc;
5724
5725 MediumAttachmentList llDetachedAttachments;
5726 {
5727 /* find all attached devices to the appropriate storage controller and detach them all */
5728 // make a temporary list because detachDevice invalidates iterators into
5729 // mMediumAttachments
5730 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5731
5732 for (MediumAttachmentList::const_iterator
5733 it = llAttachments2.begin();
5734 it != llAttachments2.end();
5735 ++it)
5736 {
5737 MediumAttachment *pAttachTemp = *it;
5738
5739 AutoCaller localAutoCaller(pAttachTemp);
5740 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
5741
5742 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5743
5744 if (pAttachTemp->i_getControllerName() == aName)
5745 {
5746 llDetachedAttachments.push_back(pAttachTemp);
5747 hrc = i_detachDevice(pAttachTemp, alock, NULL);
5748 if (FAILED(hrc)) return hrc;
5749 }
5750 }
5751 }
5752
5753 /* send event about detached devices before removing parent controller */
5754 for (MediumAttachmentList::const_iterator
5755 it = llDetachedAttachments.begin();
5756 it != llDetachedAttachments.end();
5757 ++it)
5758 {
5759 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5760 }
5761
5762 /* We can remove it now. */
5763 i_setModified(IsModified_Storage);
5764 mStorageControllers.backup();
5765
5766 ctrl->i_unshare();
5767
5768 mStorageControllers->remove(ctrl);
5769
5770 /* inform the direct session if any */
5771 alock.release();
5772 i_onStorageControllerChange(i_getId(), aName);
5773
5774 return S_OK;
5775}
5776
5777HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5778 ComPtr<IUSBController> &aController)
5779{
5780 if ( (aType <= USBControllerType_Null)
5781 || (aType >= USBControllerType_Last))
5782 return setError(E_INVALIDARG,
5783 tr("Invalid USB controller type: %d"),
5784 aType);
5785
5786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5787
5788 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5789 if (FAILED(hrc)) return hrc;
5790
5791 /* try to find one with the same type first. */
5792 ComObjPtr<USBController> ctrl;
5793
5794 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5795 if (SUCCEEDED(hrc))
5796 return setError(VBOX_E_OBJECT_IN_USE,
5797 tr("USB controller named '%s' already exists"),
5798 aName.c_str());
5799
5800 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5801 ChipsetType_T enmChipsetType;
5802 hrc = mPlatform->getChipsetType(&enmChipsetType);
5803 if (FAILED(hrc))
5804 return hrc;
5805
5806 ULONG maxInstances;
5807 hrc = mPlatformProperties->GetMaxInstancesOfUSBControllerType(enmChipsetType, aType, &maxInstances);
5808 if (FAILED(hrc))
5809 return hrc;
5810
5811 ULONG cInstances = i_getUSBControllerCountByType(aType);
5812 if (cInstances >= maxInstances)
5813 return setError(E_INVALIDARG,
5814 tr("Too many USB controllers of this type"));
5815
5816 ctrl.createObject();
5817
5818 hrc = ctrl->init(this, aName, aType);
5819 if (FAILED(hrc)) return hrc;
5820
5821 i_setModified(IsModified_USB);
5822 mUSBControllers.backup();
5823 mUSBControllers->push_back(ctrl);
5824
5825 ctrl.queryInterfaceTo(aController.asOutParam());
5826
5827 /* inform the direct session if any */
5828 alock.release();
5829 i_onUSBControllerChange();
5830
5831 return S_OK;
5832}
5833
5834HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
5835{
5836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5837
5838 ComObjPtr<USBController> ctrl;
5839
5840 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5841 if (SUCCEEDED(hrc))
5842 ctrl.queryInterfaceTo(aController.asOutParam());
5843
5844 return hrc;
5845}
5846
5847HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
5848 ULONG *aControllers)
5849{
5850 if ( (aType <= USBControllerType_Null)
5851 || (aType >= USBControllerType_Last))
5852 return setError(E_INVALIDARG,
5853 tr("Invalid USB controller type: %d"),
5854 aType);
5855
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857
5858 ComObjPtr<USBController> ctrl;
5859
5860 *aControllers = i_getUSBControllerCountByType(aType);
5861
5862 return S_OK;
5863}
5864
5865HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
5866{
5867
5868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5869
5870 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5871 if (FAILED(hrc)) return hrc;
5872
5873 ComObjPtr<USBController> ctrl;
5874 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5875 if (FAILED(hrc)) return hrc;
5876
5877 i_setModified(IsModified_USB);
5878 mUSBControllers.backup();
5879
5880 ctrl->i_unshare();
5881
5882 mUSBControllers->remove(ctrl);
5883
5884 /* inform the direct session if any */
5885 alock.release();
5886 i_onUSBControllerChange();
5887
5888 return S_OK;
5889}
5890
5891HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
5892 ULONG *aOriginX,
5893 ULONG *aOriginY,
5894 ULONG *aWidth,
5895 ULONG *aHeight,
5896 BOOL *aEnabled)
5897{
5898 uint32_t u32OriginX= 0;
5899 uint32_t u32OriginY= 0;
5900 uint32_t u32Width = 0;
5901 uint32_t u32Height = 0;
5902 uint16_t u16Flags = 0;
5903
5904#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5905 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5906#else
5907 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5908#endif
5909 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
5910 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5911 if (RT_FAILURE(vrc))
5912 {
5913#ifdef RT_OS_WINDOWS
5914 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5915 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5916 * So just assign fEnable to TRUE again.
5917 * The right fix would be to change GUI API wrappers to make sure that parameters
5918 * are changed only if API succeeds.
5919 */
5920 *aEnabled = TRUE;
5921#endif
5922 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5923 tr("Saved guest size is not available (%Rrc)"),
5924 vrc);
5925 }
5926
5927 *aOriginX = u32OriginX;
5928 *aOriginY = u32OriginY;
5929 *aWidth = u32Width;
5930 *aHeight = u32Height;
5931 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5932
5933 return S_OK;
5934}
5935
5936HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
5937 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
5938{
5939 if (aScreenId != 0)
5940 return E_NOTIMPL;
5941
5942 if ( aBitmapFormat != BitmapFormat_BGR0
5943 && aBitmapFormat != BitmapFormat_BGRA
5944 && aBitmapFormat != BitmapFormat_RGBA
5945 && aBitmapFormat != BitmapFormat_PNG)
5946 return setError(E_NOTIMPL,
5947 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
5948
5949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5950
5951 uint8_t *pu8Data = NULL;
5952 uint32_t cbData = 0;
5953 uint32_t u32Width = 0;
5954 uint32_t u32Height = 0;
5955
5956#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5957 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5958#else
5959 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5960#endif
5961 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
5962 &pu8Data, &cbData, &u32Width, &u32Height);
5963 if (RT_FAILURE(vrc))
5964 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5965 tr("Saved thumbnail data is not available (%Rrc)"),
5966 vrc);
5967
5968 HRESULT hrc = S_OK;
5969
5970 *aWidth = u32Width;
5971 *aHeight = u32Height;
5972
5973 if (cbData > 0)
5974 {
5975 /* Convert pixels to the format expected by the API caller. */
5976 if (aBitmapFormat == BitmapFormat_BGR0)
5977 {
5978 /* [0] B, [1] G, [2] R, [3] 0. */
5979 aData.resize(cbData);
5980 memcpy(&aData.front(), pu8Data, cbData);
5981 }
5982 else if (aBitmapFormat == BitmapFormat_BGRA)
5983 {
5984 /* [0] B, [1] G, [2] R, [3] A. */
5985 aData.resize(cbData);
5986 for (uint32_t i = 0; i < cbData; i += 4)
5987 {
5988 aData[i] = pu8Data[i];
5989 aData[i + 1] = pu8Data[i + 1];
5990 aData[i + 2] = pu8Data[i + 2];
5991 aData[i + 3] = 0xff;
5992 }
5993 }
5994 else if (aBitmapFormat == BitmapFormat_RGBA)
5995 {
5996 /* [0] R, [1] G, [2] B, [3] A. */
5997 aData.resize(cbData);
5998 for (uint32_t i = 0; i < cbData; i += 4)
5999 {
6000 aData[i] = pu8Data[i + 2];
6001 aData[i + 1] = pu8Data[i + 1];
6002 aData[i + 2] = pu8Data[i];
6003 aData[i + 3] = 0xff;
6004 }
6005 }
6006 else if (aBitmapFormat == BitmapFormat_PNG)
6007 {
6008 uint8_t *pu8PNG = NULL;
6009 uint32_t cbPNG = 0;
6010 uint32_t cxPNG = 0;
6011 uint32_t cyPNG = 0;
6012
6013 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6014
6015 if (RT_SUCCESS(vrc))
6016 {
6017 aData.resize(cbPNG);
6018 if (cbPNG)
6019 memcpy(&aData.front(), pu8PNG, cbPNG);
6020 }
6021 else
6022 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6023
6024 RTMemFree(pu8PNG);
6025 }
6026 }
6027
6028 freeSavedDisplayScreenshot(pu8Data);
6029
6030 return hrc;
6031}
6032
6033HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6034 ULONG *aWidth,
6035 ULONG *aHeight,
6036 std::vector<BitmapFormat_T> &aBitmapFormats)
6037{
6038 if (aScreenId != 0)
6039 return E_NOTIMPL;
6040
6041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6042
6043 uint8_t *pu8Data = NULL;
6044 uint32_t cbData = 0;
6045 uint32_t u32Width = 0;
6046 uint32_t u32Height = 0;
6047
6048#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6049 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6050#else
6051 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6052#endif
6053 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6054 &pu8Data, &cbData, &u32Width, &u32Height);
6055
6056 if (RT_FAILURE(vrc))
6057 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6058 tr("Saved screenshot data is not available (%Rrc)"),
6059 vrc);
6060
6061 *aWidth = u32Width;
6062 *aHeight = u32Height;
6063 aBitmapFormats.resize(1);
6064 aBitmapFormats[0] = BitmapFormat_PNG;
6065
6066 freeSavedDisplayScreenshot(pu8Data);
6067
6068 return S_OK;
6069}
6070
6071HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6072 BitmapFormat_T aBitmapFormat,
6073 ULONG *aWidth,
6074 ULONG *aHeight,
6075 std::vector<BYTE> &aData)
6076{
6077 if (aScreenId != 0)
6078 return E_NOTIMPL;
6079
6080 if (aBitmapFormat != BitmapFormat_PNG)
6081 return E_NOTIMPL;
6082
6083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6084
6085 uint8_t *pu8Data = NULL;
6086 uint32_t cbData = 0;
6087 uint32_t u32Width = 0;
6088 uint32_t u32Height = 0;
6089
6090#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6091 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6092#else
6093 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6094#endif
6095 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6096 &pu8Data, &cbData, &u32Width, &u32Height);
6097
6098 if (RT_FAILURE(vrc))
6099 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6100 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6101 vrc);
6102
6103 *aWidth = u32Width;
6104 *aHeight = u32Height;
6105
6106 aData.resize(cbData);
6107 if (cbData)
6108 memcpy(&aData.front(), pu8Data, cbData);
6109
6110 freeSavedDisplayScreenshot(pu8Data);
6111
6112 return S_OK;
6113}
6114
6115HRESULT Machine::hotPlugCPU(ULONG aCpu)
6116{
6117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6118
6119 if (!mHWData->mCPUHotPlugEnabled)
6120 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6121
6122 if (aCpu >= mHWData->mCPUCount)
6123 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6124
6125 if (mHWData->mCPUAttached[aCpu])
6126 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6127
6128 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6129 if (FAILED(hrc)) return hrc;
6130
6131 alock.release();
6132 hrc = i_onCPUChange(aCpu, false);
6133 alock.acquire();
6134 if (FAILED(hrc)) return hrc;
6135
6136 i_setModified(IsModified_MachineData);
6137 mHWData.backup();
6138 mHWData->mCPUAttached[aCpu] = true;
6139
6140 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6141 if (Global::IsOnline(mData->mMachineState))
6142 i_saveSettings(NULL, alock);
6143
6144 return S_OK;
6145}
6146
6147HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6148{
6149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6150
6151 if (!mHWData->mCPUHotPlugEnabled)
6152 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6153
6154 if (aCpu >= SchemaDefs::MaxCPUCount)
6155 return setError(E_INVALIDARG,
6156 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6157 SchemaDefs::MaxCPUCount);
6158
6159 if (!mHWData->mCPUAttached[aCpu])
6160 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6161
6162 /* CPU 0 can't be detached */
6163 if (aCpu == 0)
6164 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6165
6166 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6167 if (FAILED(hrc)) return hrc;
6168
6169 alock.release();
6170 hrc = i_onCPUChange(aCpu, true);
6171 alock.acquire();
6172 if (FAILED(hrc)) return hrc;
6173
6174 i_setModified(IsModified_MachineData);
6175 mHWData.backup();
6176 mHWData->mCPUAttached[aCpu] = false;
6177
6178 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6179 if (Global::IsOnline(mData->mMachineState))
6180 i_saveSettings(NULL, alock);
6181
6182 return S_OK;
6183}
6184
6185HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6186{
6187 *aAttached = false;
6188
6189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 /* If hotplug is enabled the CPU is always enabled. */
6192 if (!mHWData->mCPUHotPlugEnabled)
6193 {
6194 if (aCpu < mHWData->mCPUCount)
6195 *aAttached = true;
6196 }
6197 else
6198 {
6199 if (aCpu < SchemaDefs::MaxCPUCount)
6200 *aAttached = mHWData->mCPUAttached[aCpu];
6201 }
6202
6203 return S_OK;
6204}
6205
6206HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6207{
6208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6209
6210 Utf8Str log = i_getLogFilename(aIdx);
6211 if (!RTFileExists(log.c_str()))
6212 log.setNull();
6213 aFilename = log;
6214
6215 return S_OK;
6216}
6217
6218HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6219{
6220 if (aSize < 0)
6221 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6222
6223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 HRESULT hrc = S_OK;
6226 Utf8Str log = i_getLogFilename(aIdx);
6227
6228 /* do not unnecessarily hold the lock while doing something which does
6229 * not need the lock and potentially takes a long time. */
6230 alock.release();
6231
6232 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6233 * keeps the SOAP reply size under 1M for the webservice (we're using
6234 * base64 encoded strings for binary data for years now, avoiding the
6235 * expansion of each byte array element to approx. 25 bytes of XML. */
6236 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6237 aData.resize(cbData);
6238
6239 int vrc = VINF_SUCCESS;
6240 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6241
6242#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6243 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6244 {
6245 PCVBOXCRYPTOIF pCryptoIf = NULL;
6246 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6247 if (SUCCEEDED(hrc))
6248 {
6249 alock.acquire();
6250
6251 SecretKey *pKey = NULL;
6252 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6253 alock.release();
6254
6255 if (RT_SUCCESS(vrc))
6256 {
6257 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6258 if (RT_SUCCESS(vrc))
6259 {
6260 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6261 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6262 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6263 if (RT_SUCCESS(vrc))
6264 {
6265 RTVfsIoStrmRelease(hVfsIosLog);
6266 hVfsIosLog = hVfsIosLogDec;
6267 }
6268 }
6269
6270 pKey->release();
6271 }
6272
6273 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6274 }
6275 }
6276 else
6277 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6278#else
6279 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6280#endif
6281 if (RT_SUCCESS(vrc))
6282 {
6283 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6284 cbData ? &aData.front() : NULL, cbData,
6285 true /*fBlocking*/, &cbData);
6286 if (RT_SUCCESS(vrc))
6287 aData.resize(cbData);
6288 else
6289 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6290
6291 RTVfsIoStrmRelease(hVfsIosLog);
6292 }
6293 else
6294 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6295
6296 if (FAILED(hrc))
6297 aData.resize(0);
6298
6299 return hrc;
6300}
6301
6302
6303/**
6304 * Currently this method doesn't attach device to the running VM,
6305 * just makes sure it's plugged on next VM start.
6306 */
6307HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6308{
6309 // lock scope
6310 {
6311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6312
6313 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6314 if (FAILED(hrc)) return hrc;
6315
6316 ChipsetType_T aChipset = ChipsetType_PIIX3;
6317 hrc = mPlatform->COMGETTER(ChipsetType)(&aChipset);
6318 if (FAILED(hrc)) return hrc;
6319
6320 if (aChipset != ChipsetType_ICH9) /** @todo BUGBUG ASSUMES x86! */
6321 {
6322 return setError(E_INVALIDARG,
6323 tr("Host PCI attachment only supported with ICH9 chipset"));
6324 }
6325
6326 // check if device with this host PCI address already attached
6327 for (HWData::PCIDeviceAssignmentList::const_iterator
6328 it = mHWData->mPCIDeviceAssignments.begin();
6329 it != mHWData->mPCIDeviceAssignments.end();
6330 ++it)
6331 {
6332 LONG iHostAddress = -1;
6333 ComPtr<PCIDeviceAttachment> pAttach;
6334 pAttach = *it;
6335 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6336 if (iHostAddress == aHostAddress)
6337 return setError(E_INVALIDARG,
6338 tr("Device with host PCI address already attached to this VM"));
6339 }
6340
6341 ComObjPtr<PCIDeviceAttachment> pda;
6342 char name[32];
6343
6344 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6345 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6346 pda.createObject();
6347 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6348 i_setModified(IsModified_MachineData);
6349 mHWData.backup();
6350 mHWData->mPCIDeviceAssignments.push_back(pda);
6351 }
6352
6353 return S_OK;
6354}
6355
6356/**
6357 * Currently this method doesn't detach device from the running VM,
6358 * just makes sure it's not plugged on next VM start.
6359 */
6360HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6361{
6362 ComObjPtr<PCIDeviceAttachment> pAttach;
6363 bool fRemoved = false;
6364 HRESULT hrc;
6365
6366 // lock scope
6367 {
6368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6369
6370 hrc = i_checkStateDependency(MutableStateDep);
6371 if (FAILED(hrc)) return hrc;
6372
6373 for (HWData::PCIDeviceAssignmentList::const_iterator
6374 it = mHWData->mPCIDeviceAssignments.begin();
6375 it != mHWData->mPCIDeviceAssignments.end();
6376 ++it)
6377 {
6378 LONG iHostAddress = -1;
6379 pAttach = *it;
6380 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6381 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6382 {
6383 i_setModified(IsModified_MachineData);
6384 mHWData.backup();
6385 mHWData->mPCIDeviceAssignments.remove(pAttach);
6386 fRemoved = true;
6387 break;
6388 }
6389 }
6390 }
6391
6392
6393 /* Fire event outside of the lock */
6394 if (fRemoved)
6395 {
6396 Assert(!pAttach.isNull());
6397 ComPtr<IEventSource> es;
6398 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
6399 Assert(SUCCEEDED(hrc));
6400 Bstr mid;
6401 hrc = this->COMGETTER(Id)(mid.asOutParam());
6402 Assert(SUCCEEDED(hrc));
6403 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6404 }
6405
6406 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6407 tr("No host PCI device %08x attached"),
6408 aHostAddress
6409 );
6410}
6411
6412HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6413{
6414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6417 size_t i = 0;
6418 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6419 it = mHWData->mPCIDeviceAssignments.begin();
6420 it != mHWData->mPCIDeviceAssignments.end();
6421 ++it, ++i)
6422 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6423
6424 return S_OK;
6425}
6426
6427HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6428{
6429 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6430
6431 return S_OK;
6432}
6433
6434HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6435{
6436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6439
6440 return S_OK;
6441}
6442
6443HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6444{
6445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6446 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6447 if (SUCCEEDED(hrc))
6448 {
6449 hrc = mHWData.backupEx();
6450 if (SUCCEEDED(hrc))
6451 {
6452 i_setModified(IsModified_MachineData);
6453 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6454 }
6455 }
6456 return hrc;
6457}
6458
6459HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6460{
6461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6462 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6463 return S_OK;
6464}
6465
6466HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6467{
6468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6469 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6470 if (SUCCEEDED(hrc))
6471 {
6472 hrc = mHWData.backupEx();
6473 if (SUCCEEDED(hrc))
6474 {
6475 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6476 if (SUCCEEDED(hrc))
6477 i_setModified(IsModified_MachineData);
6478 }
6479 }
6480 return hrc;
6481}
6482
6483HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6484{
6485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6486
6487 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6488
6489 return S_OK;
6490}
6491
6492HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6493{
6494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6495 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6496 if (SUCCEEDED(hrc))
6497 {
6498 hrc = mHWData.backupEx();
6499 if (SUCCEEDED(hrc))
6500 {
6501 i_setModified(IsModified_MachineData);
6502 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6503 }
6504 }
6505 return hrc;
6506}
6507
6508HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6509{
6510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6513
6514 return S_OK;
6515}
6516
6517HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6518{
6519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6520
6521 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6522 if ( SUCCEEDED(hrc)
6523 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6524 {
6525 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6526 int vrc;
6527
6528 if (aAutostartEnabled)
6529 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6530 else
6531 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6532
6533 if (RT_SUCCESS(vrc))
6534 {
6535 hrc = mHWData.backupEx();
6536 if (SUCCEEDED(hrc))
6537 {
6538 i_setModified(IsModified_MachineData);
6539 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6540 }
6541 }
6542 else if (vrc == VERR_NOT_SUPPORTED)
6543 hrc = setError(VBOX_E_NOT_SUPPORTED,
6544 tr("The VM autostart feature is not supported on this platform"));
6545 else if (vrc == VERR_PATH_NOT_FOUND)
6546 hrc = setError(E_FAIL,
6547 tr("The path to the autostart database is not set"));
6548 else
6549 hrc = setError(E_UNEXPECTED,
6550 aAutostartEnabled ?
6551 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6552 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6553 mUserData->s.strName.c_str(), vrc);
6554 }
6555 return hrc;
6556}
6557
6558HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6559{
6560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6561
6562 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6563
6564 return S_OK;
6565}
6566
6567HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6568{
6569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6570 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6571 if (SUCCEEDED(hrc))
6572 {
6573 hrc = mHWData.backupEx();
6574 if (SUCCEEDED(hrc))
6575 {
6576 i_setModified(IsModified_MachineData);
6577 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6578 }
6579 }
6580 return hrc;
6581}
6582
6583HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6584{
6585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6588
6589 return S_OK;
6590}
6591
6592HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6593{
6594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6595 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6596 if ( SUCCEEDED(hrc)
6597 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6598 {
6599 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6600 int vrc;
6601
6602 if (aAutostopType != AutostopType_Disabled)
6603 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6604 else
6605 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6606
6607 if (RT_SUCCESS(vrc))
6608 {
6609 hrc = mHWData.backupEx();
6610 if (SUCCEEDED(hrc))
6611 {
6612 i_setModified(IsModified_MachineData);
6613 mHWData->mAutostart.enmAutostopType = aAutostopType;
6614 }
6615 }
6616 else if (vrc == VERR_NOT_SUPPORTED)
6617 hrc = setError(VBOX_E_NOT_SUPPORTED,
6618 tr("The VM autostop feature is not supported on this platform"));
6619 else if (vrc == VERR_PATH_NOT_FOUND)
6620 hrc = setError(E_FAIL,
6621 tr("The path to the autostart database is not set"));
6622 else
6623 hrc = setError(E_UNEXPECTED,
6624 aAutostopType != AutostopType_Disabled ?
6625 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6626 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6627 mUserData->s.strName.c_str(), vrc);
6628 }
6629 return hrc;
6630}
6631
6632HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6633{
6634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6635
6636 aDefaultFrontend = mHWData->mDefaultFrontend;
6637
6638 return S_OK;
6639}
6640
6641HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6642{
6643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6644 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6645 if (SUCCEEDED(hrc))
6646 {
6647 hrc = mHWData.backupEx();
6648 if (SUCCEEDED(hrc))
6649 {
6650 i_setModified(IsModified_MachineData);
6651 mHWData->mDefaultFrontend = aDefaultFrontend;
6652 }
6653 }
6654 return hrc;
6655}
6656
6657HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6658{
6659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6660 size_t cbIcon = mUserData->s.ovIcon.size();
6661 aIcon.resize(cbIcon);
6662 if (cbIcon)
6663 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6664 return S_OK;
6665}
6666
6667HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6668{
6669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6670 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6671 if (SUCCEEDED(hrc))
6672 {
6673 i_setModified(IsModified_MachineData);
6674 mUserData.backup();
6675 size_t cbIcon = aIcon.size();
6676 mUserData->s.ovIcon.resize(cbIcon);
6677 if (cbIcon)
6678 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6679 }
6680 return hrc;
6681}
6682
6683HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6684{
6685#ifdef VBOX_WITH_USB
6686 *aUSBProxyAvailable = true;
6687#else
6688 *aUSBProxyAvailable = false;
6689#endif
6690 return S_OK;
6691}
6692
6693HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6694{
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 *aVMProcessPriority = mUserData->s.enmVMPriority;
6698
6699 return S_OK;
6700}
6701
6702HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6703{
6704 RT_NOREF(aVMProcessPriority);
6705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6706 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6707 if (SUCCEEDED(hrc))
6708 {
6709 hrc = mUserData.backupEx();
6710 if (SUCCEEDED(hrc))
6711 {
6712 i_setModified(IsModified_MachineData);
6713 mUserData->s.enmVMPriority = aVMProcessPriority;
6714 }
6715 }
6716 alock.release();
6717 if (SUCCEEDED(hrc))
6718 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6719 return hrc;
6720}
6721
6722HRESULT Machine::getVMExecutionEngine(VMExecutionEngine_T *aVMExecutionEngine)
6723{
6724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6725
6726 *aVMExecutionEngine = mUserData->s.enmExecEngine;
6727
6728 return S_OK;
6729}
6730
6731HRESULT Machine::setVMExecutionEngine(VMExecutionEngine_T aVMExecutionEngine)
6732{
6733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6734 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6735 if (SUCCEEDED(hrc))
6736 {
6737 hrc = mUserData.backupEx();
6738 if (SUCCEEDED(hrc))
6739 {
6740 i_setModified(IsModified_MachineData);
6741 mUserData->s.enmExecEngine = aVMExecutionEngine;
6742 }
6743 }
6744 return hrc;
6745}
6746
6747HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6748 ComPtr<IProgress> &aProgress)
6749{
6750 ComObjPtr<Progress> pP;
6751 Progress *ppP = pP;
6752 IProgress *iP = static_cast<IProgress *>(ppP);
6753 IProgress **pProgress = &iP;
6754
6755 IMachine *pTarget = aTarget;
6756
6757 /* Convert the options. */
6758 RTCList<CloneOptions_T> optList;
6759 if (aOptions.size())
6760 for (size_t i = 0; i < aOptions.size(); ++i)
6761 optList.append(aOptions[i]);
6762
6763 if (optList.contains(CloneOptions_Link))
6764 {
6765 if (!i_isSnapshotMachine())
6766 return setError(E_INVALIDARG,
6767 tr("Linked clone can only be created from a snapshot"));
6768 if (aMode != CloneMode_MachineState)
6769 return setError(E_INVALIDARG,
6770 tr("Linked clone can only be created for a single machine state"));
6771 }
6772 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6773
6774 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6775
6776 HRESULT hrc = pWorker->start(pProgress);
6777
6778 pP = static_cast<Progress *>(*pProgress);
6779 pP.queryInterfaceTo(aProgress.asOutParam());
6780
6781 return hrc;
6782
6783}
6784
6785HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6786 const com::Utf8Str &aType,
6787 ComPtr<IProgress> &aProgress)
6788{
6789 LogFlowThisFuncEnter();
6790
6791 ComObjPtr<Progress> ptrProgress;
6792 HRESULT hrc = ptrProgress.createObject();
6793 if (SUCCEEDED(hrc))
6794 {
6795 com::Utf8Str strDefaultPath;
6796 if (aTargetPath.isEmpty())
6797 i_calculateFullPath(".", strDefaultPath);
6798
6799 /* Initialize our worker task */
6800 MachineMoveVM *pTask = NULL;
6801 try
6802 {
6803 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6804 }
6805 catch (std::bad_alloc &)
6806 {
6807 return E_OUTOFMEMORY;
6808 }
6809
6810 hrc = pTask->init();//no exceptions are thrown
6811
6812 if (SUCCEEDED(hrc))
6813 {
6814 hrc = pTask->createThread();
6815 pTask = NULL; /* Consumed by createThread(). */
6816 if (SUCCEEDED(hrc))
6817 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6818 else
6819 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6820 }
6821 else
6822 delete pTask;
6823 }
6824
6825 LogFlowThisFuncLeave();
6826 return hrc;
6827
6828}
6829
6830HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6831{
6832 NOREF(aProgress);
6833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 // This check should always fail.
6836 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6837 if (FAILED(hrc)) return hrc;
6838
6839 AssertFailedReturn(E_NOTIMPL);
6840}
6841
6842HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6843{
6844 NOREF(aSavedStateFile);
6845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6846
6847 // This check should always fail.
6848 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6849 if (FAILED(hrc)) return hrc;
6850
6851 AssertFailedReturn(E_NOTIMPL);
6852}
6853
6854HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6855{
6856 NOREF(aFRemoveFile);
6857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 // This check should always fail.
6860 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6861 if (FAILED(hrc)) return hrc;
6862
6863 AssertFailedReturn(E_NOTIMPL);
6864}
6865
6866// public methods for internal purposes
6867/////////////////////////////////////////////////////////////////////////////
6868
6869/**
6870 * Adds the given IsModified_* flag to the dirty flags of the machine.
6871 * This must be called either during i_loadSettings or under the machine write lock.
6872 * @param fl Flag
6873 * @param fAllowStateModification If state modifications are allowed.
6874 */
6875void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6876{
6877 mData->flModifications |= fl;
6878 if (fAllowStateModification && i_isStateModificationAllowed())
6879 mData->mCurrentStateModified = true;
6880}
6881
6882/**
6883 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6884 * care of the write locking.
6885 *
6886 * @param fModification The flag to add.
6887 * @param fAllowStateModification If state modifications are allowed.
6888 */
6889void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6890{
6891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6892 i_setModified(fModification, fAllowStateModification);
6893}
6894
6895/**
6896 * Saves the registry entry of this machine to the given configuration node.
6897 *
6898 * @param data Machine registry data.
6899 *
6900 * @note locks this object for reading.
6901 */
6902HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6903{
6904 AutoLimitedCaller autoCaller(this);
6905 AssertComRCReturnRC(autoCaller.hrc());
6906
6907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6908
6909 data.uuid = mData->mUuid;
6910 data.strSettingsFile = mData->m_strConfigFile;
6911
6912 return S_OK;
6913}
6914
6915/**
6916 * Calculates the absolute path of the given path taking the directory of the
6917 * machine settings file as the current directory.
6918 *
6919 * @param strPath Path to calculate the absolute path for.
6920 * @param aResult Where to put the result (used only on success, can be the
6921 * same Utf8Str instance as passed in @a aPath).
6922 * @return IPRT result.
6923 *
6924 * @note Locks this object for reading.
6925 */
6926int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6927{
6928 AutoCaller autoCaller(this);
6929 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
6930
6931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6932
6933 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6934
6935 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6936
6937 strSettingsDir.stripFilename();
6938 char szFolder[RTPATH_MAX];
6939 size_t cbFolder = sizeof(szFolder);
6940 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
6941 if (RT_SUCCESS(vrc))
6942 aResult = szFolder;
6943
6944 return vrc;
6945}
6946
6947/**
6948 * Copies strSource to strTarget, making it relative to the machine folder
6949 * if it is a subdirectory thereof, or simply copying it otherwise.
6950 *
6951 * @param strSource Path to evaluate and copy.
6952 * @param strTarget Buffer to receive target path.
6953 *
6954 * @note Locks this object for reading.
6955 */
6956void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6957 Utf8Str &strTarget)
6958{
6959 AutoCaller autoCaller(this);
6960 AssertComRCReturn(autoCaller.hrc(), (void)0);
6961
6962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6963
6964 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6965 // use strTarget as a temporary buffer to hold the machine settings dir
6966 strTarget = mData->m_strConfigFileFull;
6967 strTarget.stripFilename();
6968 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6969 {
6970 // is relative: then append what's left
6971 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6972 // for empty paths (only possible for subdirs) use "." to avoid
6973 // triggering default settings for not present config attributes.
6974 if (strTarget.isEmpty())
6975 strTarget = ".";
6976 }
6977 else
6978 // is not relative: then overwrite
6979 strTarget = strSource;
6980}
6981
6982/**
6983 * Returns the full path to the machine's log folder in the
6984 * \a aLogFolder argument.
6985 */
6986void Machine::i_getLogFolder(Utf8Str &aLogFolder)
6987{
6988 AutoCaller autoCaller(this);
6989 AssertComRCReturnVoid(autoCaller.hrc());
6990
6991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6992
6993 char szTmp[RTPATH_MAX];
6994 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6995 if (RT_SUCCESS(vrc))
6996 {
6997 if (szTmp[0] && !mUserData.isNull())
6998 {
6999 char szTmp2[RTPATH_MAX];
7000 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7001 if (RT_SUCCESS(vrc))
7002 aLogFolder.printf("%s%c%s",
7003 szTmp2,
7004 RTPATH_DELIMITER,
7005 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7006 }
7007 else
7008 vrc = VERR_PATH_IS_RELATIVE;
7009 }
7010
7011 if (RT_FAILURE(vrc))
7012 {
7013 // fallback if VBOX_USER_LOGHOME is not set or invalid
7014 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7015 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7016 aLogFolder.append(RTPATH_DELIMITER);
7017 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7018 }
7019}
7020
7021/**
7022 * Returns the full path to the machine's log file for an given index.
7023 */
7024Utf8Str Machine::i_getLogFilename(ULONG idx)
7025{
7026 Utf8Str logFolder;
7027 getLogFolder(logFolder);
7028 Assert(logFolder.length());
7029
7030 Utf8Str log;
7031 if (idx == 0)
7032 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7033#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7034 else if (idx == 1)
7035 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7036 else
7037 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7038#else
7039 else
7040 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7041#endif
7042 return log;
7043}
7044
7045/**
7046 * Returns the full path to the machine's hardened log file.
7047 */
7048Utf8Str Machine::i_getHardeningLogFilename(void)
7049{
7050 Utf8Str strFilename;
7051 getLogFolder(strFilename);
7052 Assert(strFilename.length());
7053 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7054 return strFilename;
7055}
7056
7057/**
7058 * Returns the default NVRAM filename based on the location of the VM config.
7059 * Note that this is a relative path.
7060 */
7061Utf8Str Machine::i_getDefaultNVRAMFilename()
7062{
7063 AutoCaller autoCaller(this);
7064 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7065
7066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7067
7068 if (i_isSnapshotMachine())
7069 return Utf8Str::Empty;
7070
7071 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7072 strNVRAMFilePath.stripPath();
7073 strNVRAMFilePath.stripSuffix();
7074 strNVRAMFilePath += ".nvram";
7075
7076 return strNVRAMFilePath;
7077}
7078
7079/**
7080 * Returns the NVRAM filename for a new snapshot. This intentionally works
7081 * similarly to the saved state file naming. Note that this is usually
7082 * a relative path, unless the snapshot folder is absolute.
7083 */
7084Utf8Str Machine::i_getSnapshotNVRAMFilename()
7085{
7086 AutoCaller autoCaller(this);
7087 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7088
7089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 RTTIMESPEC ts;
7092 RTTimeNow(&ts);
7093 RTTIME time;
7094 RTTimeExplode(&time, &ts);
7095
7096 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7097 strNVRAMFilePath += RTPATH_DELIMITER;
7098 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7099 time.i32Year, time.u8Month, time.u8MonthDay,
7100 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7101
7102 return strNVRAMFilePath;
7103}
7104
7105/**
7106 * Returns the version of the settings file.
7107 */
7108SettingsVersion_T Machine::i_getSettingsVersion(void)
7109{
7110 AutoCaller autoCaller(this);
7111 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7112
7113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7114
7115 return mData->pMachineConfigFile->getSettingsVersion();
7116}
7117
7118/**
7119 * Composes a unique saved state filename based on the current system time. The filename is
7120 * granular to the second so this will work so long as no more than one snapshot is taken on
7121 * a machine per second.
7122 *
7123 * Before version 4.1, we used this formula for saved state files:
7124 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7125 * which no longer works because saved state files can now be shared between the saved state of the
7126 * "saved" machine and an online snapshot, and the following would cause problems:
7127 * 1) save machine
7128 * 2) create online snapshot from that machine state --> reusing saved state file
7129 * 3) save machine again --> filename would be reused, breaking the online snapshot
7130 *
7131 * So instead we now use a timestamp.
7132 *
7133 * @param strStateFilePath
7134 */
7135
7136void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7137{
7138 AutoCaller autoCaller(this);
7139 AssertComRCReturnVoid(autoCaller.hrc());
7140
7141 {
7142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7143 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7144 }
7145
7146 RTTIMESPEC ts;
7147 RTTimeNow(&ts);
7148 RTTIME time;
7149 RTTimeExplode(&time, &ts);
7150
7151 strStateFilePath += RTPATH_DELIMITER;
7152 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7153 time.i32Year, time.u8Month, time.u8MonthDay,
7154 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7155}
7156
7157/**
7158 * Returns whether at least one USB controller is present for the VM.
7159 */
7160bool Machine::i_isUSBControllerPresent()
7161{
7162 AutoCaller autoCaller(this);
7163 AssertComRCReturn(autoCaller.hrc(), false);
7164
7165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7166
7167 return (mUSBControllers->size() > 0);
7168}
7169
7170
7171/**
7172 * @note Locks this object for writing, calls the client process
7173 * (inside the lock).
7174 */
7175HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7176 const Utf8Str &strFrontend,
7177 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7178 ProgressProxy *aProgress)
7179{
7180 LogFlowThisFuncEnter();
7181
7182 AssertReturn(aControl, E_FAIL);
7183 AssertReturn(aProgress, E_FAIL);
7184 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7185
7186 AutoCaller autoCaller(this);
7187 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7188
7189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 if (!mData->mRegistered)
7192 return setError(E_UNEXPECTED,
7193 tr("The machine '%s' is not registered"),
7194 mUserData->s.strName.c_str());
7195
7196 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7197
7198 /* The process started when launching a VM with separate UI/VM processes is always
7199 * the UI process, i.e. needs special handling as it won't claim the session. */
7200 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7201
7202 if (fSeparate)
7203 {
7204 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7205 return setError(VBOX_E_INVALID_OBJECT_STATE,
7206 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7207 mUserData->s.strName.c_str());
7208 }
7209 else
7210 {
7211 if ( mData->mSession.mState == SessionState_Locked
7212 || mData->mSession.mState == SessionState_Spawning
7213 || mData->mSession.mState == SessionState_Unlocking)
7214 return setError(VBOX_E_INVALID_OBJECT_STATE,
7215 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7216 mUserData->s.strName.c_str());
7217
7218 /* may not be busy */
7219 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7220 }
7221
7222 /* Hardening logging */
7223#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7224 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7225 {
7226 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7227 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7228 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7229 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7230 {
7231 Utf8Str strStartupLogDir = strHardeningLogFile;
7232 strStartupLogDir.stripFilename();
7233 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7234 file without stripping the file. */
7235 }
7236 strSupHardeningLogArg.append(strHardeningLogFile);
7237
7238 /* Remove legacy log filename to avoid confusion. */
7239 Utf8Str strOldStartupLogFile;
7240 getLogFolder(strOldStartupLogFile);
7241 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7242 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7243 }
7244#else
7245 Utf8Str strSupHardeningLogArg;
7246#endif
7247
7248 Utf8Str strAppOverride;
7249#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7250 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7251#endif
7252
7253 bool fUseVBoxSDS = false;
7254 Utf8Str strCanonicalName;
7255 if (false)
7256 { }
7257#ifdef VBOX_WITH_QTGUI
7258 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7259 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7260 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7261 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7262 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7263 {
7264 strCanonicalName = "GUI/Qt";
7265 fUseVBoxSDS = true;
7266 }
7267#endif
7268#ifdef VBOX_WITH_VBOXSDL
7269 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7270 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7271 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7272 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7273 {
7274 strCanonicalName = "GUI/SDL";
7275 fUseVBoxSDS = true;
7276 }
7277#endif
7278#ifdef VBOX_WITH_HEADLESS
7279 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7280 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7281 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7282 {
7283 strCanonicalName = "headless";
7284 }
7285#endif
7286 else
7287 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7288
7289 Utf8Str idStr = mData->mUuid.toString();
7290 Utf8Str const &strMachineName = mUserData->s.strName;
7291 RTPROCESS pid = NIL_RTPROCESS;
7292
7293#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7294 RT_NOREF(fUseVBoxSDS);
7295#else
7296 DWORD idCallerSession = ~(DWORD)0;
7297 if (fUseVBoxSDS)
7298 {
7299 /*
7300 * The VBoxSDS should be used for process launching the VM with
7301 * GUI only if the caller and the VBoxSDS are in different Windows
7302 * sessions and the caller in the interactive one.
7303 */
7304 fUseVBoxSDS = false;
7305
7306 /* Get windows session of the current process. The process token used
7307 due to several reasons:
7308 1. The token is absent for the current thread except someone set it
7309 for us.
7310 2. Needs to get the id of the session where the process is started.
7311 We only need to do this once, though. */
7312 static DWORD s_idCurrentSession = ~(DWORD)0;
7313 DWORD idCurrentSession = s_idCurrentSession;
7314 if (idCurrentSession == ~(DWORD)0)
7315 {
7316 HANDLE hCurrentProcessToken = NULL;
7317 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7318 {
7319 DWORD cbIgn = 0;
7320 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7321 s_idCurrentSession = idCurrentSession;
7322 else
7323 {
7324 idCurrentSession = ~(DWORD)0;
7325 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7326 }
7327 CloseHandle(hCurrentProcessToken);
7328 }
7329 else
7330 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7331 }
7332
7333 /* get the caller's session */
7334 HRESULT hrc = CoImpersonateClient();
7335 if (SUCCEEDED(hrc))
7336 {
7337 HANDLE hCallerThreadToken;
7338 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7339 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7340 &hCallerThreadToken))
7341 {
7342 SetLastError(NO_ERROR);
7343 DWORD cbIgn = 0;
7344 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7345 {
7346 /* Only need to use SDS if the session ID differs: */
7347 if (idCurrentSession != idCallerSession)
7348 {
7349 fUseVBoxSDS = false;
7350
7351 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7352 DWORD cbTokenGroups = 0;
7353 PTOKEN_GROUPS pTokenGroups = NULL;
7354 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7355 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7356 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7357 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7358 {
7359 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7360 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7361 PSID pInteractiveSid = NULL;
7362 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7363 {
7364 /* Iterate over the groups looking for the interactive SID: */
7365 fUseVBoxSDS = false;
7366 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7367 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7368 {
7369 fUseVBoxSDS = true;
7370 break;
7371 }
7372 FreeSid(pInteractiveSid);
7373 }
7374 }
7375 else
7376 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7377 RTMemTmpFree(pTokenGroups);
7378 }
7379 }
7380 else
7381 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7382 CloseHandle(hCallerThreadToken);
7383 }
7384 else
7385 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7386 CoRevertToSelf();
7387 }
7388 else
7389 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7390 }
7391 if (fUseVBoxSDS)
7392 {
7393 /* connect to VBoxSDS */
7394 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7395 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7396 if (FAILED(hrc))
7397 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7398 strMachineName.c_str());
7399
7400 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7401 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7402 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7403 service to access the files. */
7404 hrc = CoSetProxyBlanket(pVBoxSDS,
7405 RPC_C_AUTHN_DEFAULT,
7406 RPC_C_AUTHZ_DEFAULT,
7407 COLE_DEFAULT_PRINCIPAL,
7408 RPC_C_AUTHN_LEVEL_DEFAULT,
7409 RPC_C_IMP_LEVEL_IMPERSONATE,
7410 NULL,
7411 EOAC_DEFAULT);
7412 if (FAILED(hrc))
7413 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7414
7415 size_t const cEnvVars = aEnvironmentChanges.size();
7416 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7417 for (size_t i = 0; i < cEnvVars; i++)
7418 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7419
7420 ULONG uPid = 0;
7421 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7422 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7423 idCallerSession, &uPid);
7424 if (FAILED(hrc))
7425 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7426 pid = (RTPROCESS)uPid;
7427 }
7428 else
7429#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7430 {
7431 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7432 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7433 if (RT_FAILURE(vrc))
7434 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7435 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7436 }
7437
7438 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7439 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7440
7441 if (!fSeparate)
7442 {
7443 /*
7444 * Note that we don't release the lock here before calling the client,
7445 * because it doesn't need to call us back if called with a NULL argument.
7446 * Releasing the lock here is dangerous because we didn't prepare the
7447 * launch data yet, but the client we've just started may happen to be
7448 * too fast and call LockMachine() that will fail (because of PID, etc.),
7449 * so that the Machine will never get out of the Spawning session state.
7450 */
7451
7452 /* inform the session that it will be a remote one */
7453 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7454#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7455 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7457 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7458#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7459 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
7460
7461 if (FAILED(hrc))
7462 {
7463 /* restore the session state */
7464 mData->mSession.mState = SessionState_Unlocked;
7465 alock.release();
7466 mParent->i_addProcessToReap(pid);
7467 /* The failure may occur w/o any error info (from RPC), so provide one */
7468 return setError(VBOX_E_VM_ERROR,
7469 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
7470 }
7471
7472 /* attach launch data to the machine */
7473 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7474 mData->mSession.mRemoteControls.push_back(aControl);
7475 mData->mSession.mProgress = aProgress;
7476 mData->mSession.mPID = pid;
7477 mData->mSession.mState = SessionState_Spawning;
7478 Assert(strCanonicalName.isNotEmpty());
7479 mData->mSession.mName = strCanonicalName;
7480 }
7481 else
7482 {
7483 /* For separate UI process we declare the launch as completed instantly, as the
7484 * actual headless VM start may or may not come. No point in remembering anything
7485 * yet, as what matters for us is when the headless VM gets started. */
7486 aProgress->i_notifyComplete(S_OK);
7487 }
7488
7489 alock.release();
7490 mParent->i_addProcessToReap(pid);
7491
7492 LogFlowThisFuncLeave();
7493 return S_OK;
7494}
7495
7496/**
7497 * Returns @c true if the given session machine instance has an open direct
7498 * session (and optionally also for direct sessions which are closing) and
7499 * returns the session control machine instance if so.
7500 *
7501 * Note that when the method returns @c false, the arguments remain unchanged.
7502 *
7503 * @param aMachine Session machine object.
7504 * @param aControl Direct session control object (optional).
7505 * @param aRequireVM If true then only allow VM sessions.
7506 * @param aAllowClosing If true then additionally a session which is currently
7507 * being closed will also be allowed.
7508 *
7509 * @note locks this object for reading.
7510 */
7511bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7512 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7513 bool aRequireVM /*= false*/,
7514 bool aAllowClosing /*= false*/)
7515{
7516 AutoLimitedCaller autoCaller(this);
7517 AssertComRCReturn(autoCaller.hrc(), false);
7518
7519 /* just return false for inaccessible machines */
7520 if (getObjectState().getState() != ObjectState::Ready)
7521 return false;
7522
7523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7524
7525 if ( ( mData->mSession.mState == SessionState_Locked
7526 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7527 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7528 )
7529 {
7530 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7531
7532 aMachine = mData->mSession.mMachine;
7533
7534 if (aControl != NULL)
7535 *aControl = mData->mSession.mDirectControl;
7536
7537 return true;
7538 }
7539
7540 return false;
7541}
7542
7543/**
7544 * Returns @c true if the given machine has an spawning direct session.
7545 *
7546 * @note locks this object for reading.
7547 */
7548bool Machine::i_isSessionSpawning()
7549{
7550 AutoLimitedCaller autoCaller(this);
7551 AssertComRCReturn(autoCaller.hrc(), false);
7552
7553 /* just return false for inaccessible machines */
7554 if (getObjectState().getState() != ObjectState::Ready)
7555 return false;
7556
7557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7558
7559 if (mData->mSession.mState == SessionState_Spawning)
7560 return true;
7561
7562 return false;
7563}
7564
7565/**
7566 * Called from the client watcher thread to check for unexpected client process
7567 * death during Session_Spawning state (e.g. before it successfully opened a
7568 * direct session).
7569 *
7570 * On Win32 and on OS/2, this method is called only when we've got the
7571 * direct client's process termination notification, so it always returns @c
7572 * true.
7573 *
7574 * On other platforms, this method returns @c true if the client process is
7575 * terminated and @c false if it's still alive.
7576 *
7577 * @note Locks this object for writing.
7578 */
7579bool Machine::i_checkForSpawnFailure()
7580{
7581 AutoCaller autoCaller(this);
7582 if (!autoCaller.isOk())
7583 {
7584 /* nothing to do */
7585 LogFlowThisFunc(("Already uninitialized!\n"));
7586 return true;
7587 }
7588
7589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7590
7591 if (mData->mSession.mState != SessionState_Spawning)
7592 {
7593 /* nothing to do */
7594 LogFlowThisFunc(("Not spawning any more!\n"));
7595 return true;
7596 }
7597
7598 /* PID not yet initialized, skip check. */
7599 if (mData->mSession.mPID == NIL_RTPROCESS)
7600 return false;
7601
7602 HRESULT hrc = S_OK;
7603 RTPROCSTATUS status;
7604 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7605 if (vrc != VERR_PROCESS_RUNNING)
7606 {
7607 Utf8Str strExtraInfo;
7608
7609#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7610 /* If the startup logfile exists and is of non-zero length, tell the
7611 user to look there for more details to encourage them to attach it
7612 when reporting startup issues. */
7613 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7614 uint64_t cbStartupLogFile = 0;
7615 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7616 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7617 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7618#endif
7619
7620 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7621 hrc = setError(E_FAIL,
7622 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7623 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7624 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7625 hrc = setError(E_FAIL,
7626 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7627 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7628 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7629 hrc = setError(E_FAIL,
7630 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7631 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7632 else
7633 hrc = setErrorBoth(E_FAIL, vrc,
7634 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7635 i_getName().c_str(), vrc, strExtraInfo.c_str());
7636 }
7637
7638 if (FAILED(hrc))
7639 {
7640 /* Close the remote session, remove the remote control from the list
7641 * and reset session state to Closed (@note keep the code in sync with
7642 * the relevant part in LockMachine()). */
7643
7644 Assert(mData->mSession.mRemoteControls.size() == 1);
7645 if (mData->mSession.mRemoteControls.size() == 1)
7646 {
7647 ErrorInfoKeeper eik;
7648 mData->mSession.mRemoteControls.front()->Uninitialize();
7649 }
7650
7651 mData->mSession.mRemoteControls.clear();
7652 mData->mSession.mState = SessionState_Unlocked;
7653
7654 /* finalize the progress after setting the state */
7655 if (!mData->mSession.mProgress.isNull())
7656 {
7657 mData->mSession.mProgress->notifyComplete(hrc);
7658 mData->mSession.mProgress.setNull();
7659 }
7660
7661 mData->mSession.mPID = NIL_RTPROCESS;
7662
7663 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7664 return true;
7665 }
7666
7667 return false;
7668}
7669
7670/**
7671 * Checks whether the machine can be registered. If so, commits and saves
7672 * all settings.
7673 *
7674 * @note Must be called from mParent's write lock. Locks this object and
7675 * children for writing.
7676 */
7677HRESULT Machine::i_prepareRegister()
7678{
7679 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7680
7681 AutoLimitedCaller autoCaller(this);
7682 AssertComRCReturnRC(autoCaller.hrc());
7683
7684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7685
7686 /* wait for state dependents to drop to zero */
7687 i_ensureNoStateDependencies(alock);
7688
7689 if (!mData->mAccessible)
7690 return setError(VBOX_E_INVALID_OBJECT_STATE,
7691 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7692 mUserData->s.strName.c_str(),
7693 mData->mUuid.toString().c_str());
7694
7695 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7696
7697 if (mData->mRegistered)
7698 return setError(VBOX_E_INVALID_OBJECT_STATE,
7699 tr("The machine '%s' with UUID {%s} is already registered"),
7700 mUserData->s.strName.c_str(),
7701 mData->mUuid.toString().c_str());
7702
7703 HRESULT hrc = S_OK;
7704
7705 // Ensure the settings are saved. If we are going to be registered and
7706 // no config file exists yet, create it by calling i_saveSettings() too.
7707 if ( (mData->flModifications)
7708 || (!mData->pMachineConfigFile->fileExists())
7709 )
7710 {
7711 hrc = i_saveSettings(NULL, alock);
7712 // no need to check whether VirtualBox.xml needs saving too since
7713 // we can't have a machine XML file rename pending
7714 if (FAILED(hrc)) return hrc;
7715 }
7716
7717 /* more config checking goes here */
7718
7719 if (SUCCEEDED(hrc))
7720 {
7721 /* we may have had implicit modifications we want to fix on success */
7722 i_commit();
7723
7724 mData->mRegistered = true;
7725 }
7726 else
7727 {
7728 /* we may have had implicit modifications we want to cancel on failure*/
7729 i_rollback(false /* aNotify */);
7730 }
7731
7732 return hrc;
7733}
7734
7735/**
7736 * Increases the number of objects dependent on the machine state or on the
7737 * registered state. Guarantees that these two states will not change at least
7738 * until #i_releaseStateDependency() is called.
7739 *
7740 * Depending on the @a aDepType value, additional state checks may be made.
7741 * These checks will set extended error info on failure. See
7742 * #i_checkStateDependency() for more info.
7743 *
7744 * If this method returns a failure, the dependency is not added and the caller
7745 * is not allowed to rely on any particular machine state or registration state
7746 * value and may return the failed result code to the upper level.
7747 *
7748 * @param aDepType Dependency type to add.
7749 * @param aState Current machine state (NULL if not interested).
7750 * @param aRegistered Current registered state (NULL if not interested).
7751 *
7752 * @note Locks this object for writing.
7753 */
7754HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7755 MachineState_T *aState /* = NULL */,
7756 BOOL *aRegistered /* = NULL */)
7757{
7758 AutoCaller autoCaller(this);
7759 AssertComRCReturnRC(autoCaller.hrc());
7760
7761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7762
7763 HRESULT hrc = i_checkStateDependency(aDepType);
7764 if (FAILED(hrc)) return hrc;
7765
7766 {
7767 if (mData->mMachineStateChangePending != 0)
7768 {
7769 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7770 * drop to zero so don't add more. It may make sense to wait a bit
7771 * and retry before reporting an error (since the pending state
7772 * transition should be really quick) but let's just assert for
7773 * now to see if it ever happens on practice. */
7774
7775 AssertFailed();
7776
7777 return setError(E_ACCESSDENIED,
7778 tr("Machine state change is in progress. Please retry the operation later."));
7779 }
7780
7781 ++mData->mMachineStateDeps;
7782 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7783 }
7784
7785 if (aState)
7786 *aState = mData->mMachineState;
7787 if (aRegistered)
7788 *aRegistered = mData->mRegistered;
7789
7790 return S_OK;
7791}
7792
7793/**
7794 * Decreases the number of objects dependent on the machine state.
7795 * Must always complete the #i_addStateDependency() call after the state
7796 * dependency is no more necessary.
7797 */
7798void Machine::i_releaseStateDependency()
7799{
7800 AutoCaller autoCaller(this);
7801 AssertComRCReturnVoid(autoCaller.hrc());
7802
7803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7804
7805 /* releaseStateDependency() w/o addStateDependency()? */
7806 AssertReturnVoid(mData->mMachineStateDeps != 0);
7807 -- mData->mMachineStateDeps;
7808
7809 if (mData->mMachineStateDeps == 0)
7810 {
7811 /* inform i_ensureNoStateDependencies() that there are no more deps */
7812 if (mData->mMachineStateChangePending != 0)
7813 {
7814 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7815 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7816 }
7817 }
7818}
7819
7820Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7821{
7822 /* start with nothing found */
7823 Utf8Str strResult("");
7824
7825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7826
7827 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7828 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7829 // found:
7830 strResult = it->second; // source is a Utf8Str
7831
7832 return strResult;
7833}
7834
7835FirmwareType_T Machine::i_getFirmwareType() const
7836{
7837 return mFirmwareSettings->i_getFirmwareType();
7838}
7839
7840// protected methods
7841/////////////////////////////////////////////////////////////////////////////
7842
7843/**
7844 * Performs machine state checks based on the @a aDepType value. If a check
7845 * fails, this method will set extended error info, otherwise it will return
7846 * S_OK. It is supposed, that on failure, the caller will immediately return
7847 * the return value of this method to the upper level.
7848 *
7849 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7850 *
7851 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7852 * current state of this machine object allows to change settings of the
7853 * machine (i.e. the machine is not registered, or registered but not running
7854 * and not saved). It is useful to call this method from Machine setters
7855 * before performing any change.
7856 *
7857 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7858 * as for MutableStateDep except that if the machine is saved, S_OK is also
7859 * returned. This is useful in setters which allow changing machine
7860 * properties when it is in the saved state.
7861 *
7862 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7863 * if the current state of this machine object allows to change runtime
7864 * changeable settings of the machine (i.e. the machine is not registered, or
7865 * registered but either running or not running and not saved). It is useful
7866 * to call this method from Machine setters before performing any changes to
7867 * runtime changeable settings.
7868 *
7869 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7870 * the same as for MutableOrRunningStateDep except that if the machine is
7871 * saved, S_OK is also returned. This is useful in setters which allow
7872 * changing runtime and saved state changeable machine properties.
7873 *
7874 * @param aDepType Dependency type to check.
7875 *
7876 * @note Non Machine based classes should use #i_addStateDependency() and
7877 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7878 * template.
7879 *
7880 * @note This method must be called from under this object's read or write
7881 * lock.
7882 */
7883HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7884{
7885 switch (aDepType)
7886 {
7887 case AnyStateDep:
7888 {
7889 break;
7890 }
7891 case MutableStateDep:
7892 {
7893 if ( mData->mRegistered
7894 && ( !i_isSessionMachine()
7895 || ( mData->mMachineState != MachineState_Aborted
7896 && mData->mMachineState != MachineState_Teleported
7897 && mData->mMachineState != MachineState_PoweredOff
7898 )
7899 )
7900 )
7901 return setError(VBOX_E_INVALID_VM_STATE,
7902 tr("The machine is not mutable (state is %s)"),
7903 Global::stringifyMachineState(mData->mMachineState));
7904 break;
7905 }
7906 case MutableOrSavedStateDep:
7907 {
7908 if ( mData->mRegistered
7909 && ( !i_isSessionMachine()
7910 || ( mData->mMachineState != MachineState_Aborted
7911 && mData->mMachineState != MachineState_Teleported
7912 && mData->mMachineState != MachineState_Saved
7913 && mData->mMachineState != MachineState_AbortedSaved
7914 && mData->mMachineState != MachineState_PoweredOff
7915 )
7916 )
7917 )
7918 return setError(VBOX_E_INVALID_VM_STATE,
7919 tr("The machine is not mutable or saved (state is %s)"),
7920 Global::stringifyMachineState(mData->mMachineState));
7921 break;
7922 }
7923 case MutableOrRunningStateDep:
7924 {
7925 if ( mData->mRegistered
7926 && ( !i_isSessionMachine()
7927 || ( mData->mMachineState != MachineState_Aborted
7928 && mData->mMachineState != MachineState_Teleported
7929 && mData->mMachineState != MachineState_PoweredOff
7930 && !Global::IsOnline(mData->mMachineState)
7931 )
7932 )
7933 )
7934 return setError(VBOX_E_INVALID_VM_STATE,
7935 tr("The machine is not mutable or running (state is %s)"),
7936 Global::stringifyMachineState(mData->mMachineState));
7937 break;
7938 }
7939 case MutableOrSavedOrRunningStateDep:
7940 {
7941 if ( mData->mRegistered
7942 && ( !i_isSessionMachine()
7943 || ( mData->mMachineState != MachineState_Aborted
7944 && mData->mMachineState != MachineState_Teleported
7945 && mData->mMachineState != MachineState_Saved
7946 && mData->mMachineState != MachineState_AbortedSaved
7947 && mData->mMachineState != MachineState_PoweredOff
7948 && !Global::IsOnline(mData->mMachineState)
7949 )
7950 )
7951 )
7952 return setError(VBOX_E_INVALID_VM_STATE,
7953 tr("The machine is not mutable, saved or running (state is %s)"),
7954 Global::stringifyMachineState(mData->mMachineState));
7955 break;
7956 }
7957 }
7958
7959 return S_OK;
7960}
7961
7962/**
7963 * Helper to initialize all associated child objects and allocate data
7964 * structures.
7965 *
7966 * This method must be called as a part of the object's initialization procedure
7967 * (usually done in the #init() method).
7968 *
7969 * @note Must be called only from #init() or from #i_registeredInit().
7970 */
7971HRESULT Machine::initDataAndChildObjects()
7972{
7973 AutoCaller autoCaller(this);
7974 AssertComRCReturnRC(autoCaller.hrc());
7975 AssertReturn( getObjectState().getState() == ObjectState::InInit
7976 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7977
7978 AssertReturn(!mData->mAccessible, E_FAIL);
7979
7980 /* allocate data structures */
7981 mSSData.allocate();
7982 mUserData.allocate();
7983 mHWData.allocate();
7984 mMediumAttachments.allocate();
7985 mStorageControllers.allocate();
7986 mUSBControllers.allocate();
7987
7988 /* create the platform + platform properties objects for this machine */
7989 HRESULT hrc = unconst(mPlatform).createObject();
7990 ComAssertComRCRetRC(hrc);
7991 hrc = mPlatform->init(this);
7992 ComAssertComRCRetRC(hrc);
7993 hrc = unconst(mPlatformProperties).createObject();
7994 ComAssertComRCRetRC(hrc);
7995 hrc = mPlatformProperties->init(mParent);
7996 ComAssertComRCRetRC(hrc);
7997
7998 /* initialize mOSTypeId */
7999 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8000
8001 /* create associated firmware settings object */
8002 unconst(mFirmwareSettings).createObject();
8003 mFirmwareSettings->init(this);
8004
8005 /* create associated recording settings object */
8006 unconst(mRecordingSettings).createObject();
8007 mRecordingSettings->init(this);
8008
8009 /* create associated trusted platform module object */
8010 unconst(mTrustedPlatformModule).createObject();
8011 mTrustedPlatformModule->init(this);
8012
8013 /* create associated NVRAM store object */
8014 unconst(mNvramStore).createObject();
8015 mNvramStore->init(this);
8016
8017 /* create the graphics adapter object (always present) */
8018 unconst(mGraphicsAdapter).createObject();
8019 mGraphicsAdapter->init(this);
8020
8021 /* create an associated VRDE object (default is disabled) */
8022 unconst(mVRDEServer).createObject();
8023 mVRDEServer->init(this);
8024
8025 /* create associated serial port objects */
8026 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8027 {
8028 unconst(mSerialPorts[slot]).createObject();
8029 mSerialPorts[slot]->init(this, slot);
8030 }
8031
8032 /* create associated parallel port objects */
8033 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8034 {
8035 unconst(mParallelPorts[slot]).createObject();
8036 mParallelPorts[slot]->init(this, slot);
8037 }
8038
8039 /* create the audio settings object */
8040 unconst(mAudioSettings).createObject();
8041 mAudioSettings->init(this);
8042
8043 /* create the USB device filters object (always present) */
8044 unconst(mUSBDeviceFilters).createObject();
8045 mUSBDeviceFilters->init(this);
8046
8047 /* create associated network adapter objects */
8048 ChipsetType_T enmChipsetType;
8049 hrc = mPlatform->getChipsetType(&enmChipsetType);
8050 ComAssertComRC(hrc);
8051
8052 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType));
8053 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8054 {
8055 unconst(mNetworkAdapters[slot]).createObject();
8056 mNetworkAdapters[slot]->init(this, slot);
8057 }
8058
8059 /* create the bandwidth control */
8060 unconst(mBandwidthControl).createObject();
8061 mBandwidthControl->init(this);
8062
8063 /* create the guest debug control object */
8064 unconst(mGuestDebugControl).createObject();
8065 mGuestDebugControl->init(this);
8066
8067 return hrc;
8068}
8069
8070/**
8071 * Helper to uninitialize all associated child objects and to free all data
8072 * structures.
8073 *
8074 * This method must be called as a part of the object's uninitialization
8075 * procedure (usually done in the #uninit() method).
8076 *
8077 * @note Must be called only from #uninit() or from #i_registeredInit().
8078 */
8079void Machine::uninitDataAndChildObjects()
8080{
8081 AutoCaller autoCaller(this);
8082 AssertComRCReturnVoid(autoCaller.hrc());
8083 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8084 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8085 || getObjectState().getState() == ObjectState::InUninit
8086 || getObjectState().getState() == ObjectState::Limited);
8087
8088 /* tell all our other child objects we've been uninitialized */
8089 if (mGuestDebugControl)
8090 {
8091 mGuestDebugControl->uninit();
8092 unconst(mGuestDebugControl).setNull();
8093 }
8094
8095 if (mBandwidthControl)
8096 {
8097 mBandwidthControl->uninit();
8098 unconst(mBandwidthControl).setNull();
8099 }
8100
8101 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8102 {
8103 if (mNetworkAdapters[slot])
8104 {
8105 mNetworkAdapters[slot]->uninit();
8106 unconst(mNetworkAdapters[slot]).setNull();
8107 }
8108 }
8109
8110 if (mUSBDeviceFilters)
8111 {
8112 mUSBDeviceFilters->uninit();
8113 unconst(mUSBDeviceFilters).setNull();
8114 }
8115
8116 if (mAudioSettings)
8117 {
8118 mAudioSettings->uninit();
8119 unconst(mAudioSettings).setNull();
8120 }
8121
8122 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8123 {
8124 if (mParallelPorts[slot])
8125 {
8126 mParallelPorts[slot]->uninit();
8127 unconst(mParallelPorts[slot]).setNull();
8128 }
8129 }
8130
8131 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8132 {
8133 if (mSerialPorts[slot])
8134 {
8135 mSerialPorts[slot]->uninit();
8136 unconst(mSerialPorts[slot]).setNull();
8137 }
8138 }
8139
8140 if (mVRDEServer)
8141 {
8142 mVRDEServer->uninit();
8143 unconst(mVRDEServer).setNull();
8144 }
8145
8146 if (mGraphicsAdapter)
8147 {
8148 mGraphicsAdapter->uninit();
8149 unconst(mGraphicsAdapter).setNull();
8150 }
8151
8152 if (mPlatform)
8153 {
8154 mPlatform->uninit();
8155 unconst(mPlatform).setNull();
8156 }
8157
8158 if (mPlatformProperties)
8159 {
8160 mPlatformProperties->uninit();
8161 unconst(mPlatformProperties).setNull();
8162 }
8163
8164 if (mFirmwareSettings)
8165 {
8166 mFirmwareSettings->uninit();
8167 unconst(mFirmwareSettings).setNull();
8168 }
8169
8170 if (mRecordingSettings)
8171 {
8172 mRecordingSettings->uninit();
8173 unconst(mRecordingSettings).setNull();
8174 }
8175
8176 if (mTrustedPlatformModule)
8177 {
8178 mTrustedPlatformModule->uninit();
8179 unconst(mTrustedPlatformModule).setNull();
8180 }
8181
8182 if (mNvramStore)
8183 {
8184 mNvramStore->uninit();
8185 unconst(mNvramStore).setNull();
8186 }
8187
8188 /* Deassociate media (only when a real Machine or a SnapshotMachine
8189 * instance is uninitialized; SessionMachine instances refer to real
8190 * Machine media). This is necessary for a clean re-initialization of
8191 * the VM after successfully re-checking the accessibility state. Note
8192 * that in case of normal Machine or SnapshotMachine uninitialization (as
8193 * a result of unregistering or deleting the snapshot), outdated media
8194 * attachments will already be uninitialized and deleted, so this
8195 * code will not affect them. */
8196 if ( !mMediumAttachments.isNull()
8197 && !i_isSessionMachine()
8198 )
8199 {
8200 for (MediumAttachmentList::const_iterator
8201 it = mMediumAttachments->begin();
8202 it != mMediumAttachments->end();
8203 ++it)
8204 {
8205 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8206 if (pMedium.isNull())
8207 continue;
8208 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8209 AssertComRC(hrc);
8210 }
8211 }
8212
8213 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8214 {
8215 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8216 if (mData->mFirstSnapshot)
8217 {
8218 // Snapshots tree is protected by machine write lock.
8219 // Otherwise we assert in Snapshot::uninit()
8220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8221 mData->mFirstSnapshot->uninit();
8222 mData->mFirstSnapshot.setNull();
8223 }
8224
8225 mData->mCurrentSnapshot.setNull();
8226 }
8227
8228 /* free data structures (the essential mData structure is not freed here
8229 * since it may be still in use) */
8230 mMediumAttachments.free();
8231 mStorageControllers.free();
8232 mUSBControllers.free();
8233 mHWData.free();
8234 mUserData.free();
8235 mSSData.free();
8236}
8237
8238/**
8239 * Returns a pointer to the Machine object for this machine that acts like a
8240 * parent for complex machine data objects such as shared folders, etc.
8241 *
8242 * For primary Machine objects and for SnapshotMachine objects, returns this
8243 * object's pointer itself. For SessionMachine objects, returns the peer
8244 * (primary) machine pointer.
8245 */
8246Machine *Machine::i_getMachine()
8247{
8248 if (i_isSessionMachine())
8249 return (Machine*)mPeer;
8250 return this;
8251}
8252
8253/**
8254 * Makes sure that there are no machine state dependents. If necessary, waits
8255 * for the number of dependents to drop to zero.
8256 *
8257 * Make sure this method is called from under this object's write lock to
8258 * guarantee that no new dependents may be added when this method returns
8259 * control to the caller.
8260 *
8261 * @note Receives a lock to this object for writing. The lock will be released
8262 * while waiting (if necessary).
8263 *
8264 * @warning To be used only in methods that change the machine state!
8265 */
8266void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8267{
8268 AssertReturnVoid(isWriteLockOnCurrentThread());
8269
8270 /* Wait for all state dependents if necessary */
8271 if (mData->mMachineStateDeps != 0)
8272 {
8273 /* lazy semaphore creation */
8274 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8275 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8276
8277 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8278 mData->mMachineStateDeps));
8279
8280 ++mData->mMachineStateChangePending;
8281
8282 /* reset the semaphore before waiting, the last dependent will signal
8283 * it */
8284 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8285
8286 alock.release();
8287
8288 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8289
8290 alock.acquire();
8291
8292 -- mData->mMachineStateChangePending;
8293 }
8294}
8295
8296/**
8297 * Changes the machine state and informs callbacks.
8298 *
8299 * This method is not intended to fail so it either returns S_OK or asserts (and
8300 * returns a failure).
8301 *
8302 * @note Locks this object for writing.
8303 */
8304HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8305{
8306 LogFlowThisFuncEnter();
8307 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8308 Assert(aMachineState != MachineState_Null);
8309
8310 AutoCaller autoCaller(this);
8311 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8312
8313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8314
8315 /* wait for state dependents to drop to zero */
8316 i_ensureNoStateDependencies(alock);
8317
8318 MachineState_T const enmOldState = mData->mMachineState;
8319 if (enmOldState != aMachineState)
8320 {
8321 mData->mMachineState = aMachineState;
8322 RTTimeNow(&mData->mLastStateChange);
8323
8324#ifdef VBOX_WITH_DTRACE_R3_MAIN
8325 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8326#endif
8327 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8328 }
8329
8330 LogFlowThisFuncLeave();
8331 return S_OK;
8332}
8333
8334/**
8335 * Searches for a shared folder with the given logical name
8336 * in the collection of shared folders.
8337 *
8338 * @param aName logical name of the shared folder
8339 * @param aSharedFolder where to return the found object
8340 * @param aSetError whether to set the error info if the folder is
8341 * not found
8342 * @return
8343 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8344 *
8345 * @note
8346 * must be called from under the object's lock!
8347 */
8348HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8349 ComObjPtr<SharedFolder> &aSharedFolder,
8350 bool aSetError /* = false */)
8351{
8352 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8353 for (HWData::SharedFolderList::const_iterator
8354 it = mHWData->mSharedFolders.begin();
8355 it != mHWData->mSharedFolders.end();
8356 ++it)
8357 {
8358 SharedFolder *pSF = *it;
8359 AutoCaller autoCaller(pSF);
8360 if (pSF->i_getName() == aName)
8361 {
8362 aSharedFolder = pSF;
8363 hrc = S_OK;
8364 break;
8365 }
8366 }
8367
8368 if (aSetError && FAILED(hrc))
8369 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8370
8371 return hrc;
8372}
8373
8374/**
8375 * Initializes all machine instance data from the given settings structures
8376 * from XML. The exception is the machine UUID which needs special handling
8377 * depending on the caller's use case, so the caller needs to set that herself.
8378 *
8379 * This gets called in several contexts during machine initialization:
8380 *
8381 * -- When machine XML exists on disk already and needs to be loaded into memory,
8382 * for example, from #i_registeredInit() to load all registered machines on
8383 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8384 * attached to the machine should be part of some media registry already.
8385 *
8386 * -- During OVF import, when a machine config has been constructed from an
8387 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8388 * ensure that the media listed as attachments in the config (which have
8389 * been imported from the OVF) receive the correct registry ID.
8390 *
8391 * -- During VM cloning.
8392 *
8393 * @param config Machine settings from XML.
8394 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8395 * for each attached medium in the config.
8396 * @return
8397 */
8398HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8399 const Guid *puuidRegistry)
8400{
8401 // copy name, description, OS type, teleporter, UTC etc.
8402 mUserData->s = config.machineUserData;
8403
8404 // look up the object by Id to check it is valid
8405 ComObjPtr<GuestOSType> pGuestOSType;
8406 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8407 if (!pGuestOSType.isNull())
8408 mUserData->s.strOsType = pGuestOSType->i_id();
8409
8410#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8411 // stateFile encryption (optional)
8412 mSSData->strStateKeyId = config.strStateKeyId;
8413 mSSData->strStateKeyStore = config.strStateKeyStore;
8414 mData->mstrLogKeyId = config.strLogKeyId;
8415 mData->mstrLogKeyStore = config.strLogKeyStore;
8416#endif
8417
8418 // stateFile (optional)
8419 if (config.strStateFile.isEmpty())
8420 mSSData->strStateFilePath.setNull();
8421 else
8422 {
8423 Utf8Str stateFilePathFull(config.strStateFile);
8424 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8425 if (RT_FAILURE(vrc))
8426 return setErrorBoth(E_FAIL, vrc,
8427 tr("Invalid saved state file path '%s' (%Rrc)"),
8428 config.strStateFile.c_str(),
8429 vrc);
8430 mSSData->strStateFilePath = stateFilePathFull;
8431 }
8432
8433 // snapshot folder needs special processing so set it again
8434 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8435 if (FAILED(hrc)) return hrc;
8436
8437 /* Copy the extra data items (config may or may not be the same as
8438 * mData->pMachineConfigFile) if necessary. When loading the XML files
8439 * from disk they are the same, but not for OVF import. */
8440 if (mData->pMachineConfigFile != &config)
8441 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8442
8443 /* currentStateModified (optional, default is true) */
8444 mData->mCurrentStateModified = config.fCurrentStateModified;
8445
8446 mData->mLastStateChange = config.timeLastStateChange;
8447
8448 /*
8449 * note: all mUserData members must be assigned prior this point because
8450 * we need to commit changes in order to let mUserData be shared by all
8451 * snapshot machine instances.
8452 */
8453 mUserData.commitCopy();
8454
8455 // machine registry, if present (must be loaded before snapshots)
8456 if (config.canHaveOwnMediaRegistry())
8457 {
8458 // determine machine folder
8459 Utf8Str strMachineFolder = i_getSettingsFileFull();
8460 strMachineFolder.stripFilename();
8461 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8462 config.mediaRegistry,
8463 strMachineFolder);
8464 if (FAILED(hrc)) return hrc;
8465 }
8466
8467 /* Snapshot node (optional) */
8468 size_t cRootSnapshots;
8469 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8470 {
8471 // there must be only one root snapshot
8472 Assert(cRootSnapshots == 1);
8473 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8474
8475 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
8476 if (FAILED(hrc)) return hrc;
8477 }
8478
8479 // hardware data
8480 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
8481 config.recordingSettings);
8482 if (FAILED(hrc)) return hrc;
8483
8484 /*
8485 * NOTE: the assignment below must be the last thing to do,
8486 * otherwise it will be not possible to change the settings
8487 * somewhere in the code above because all setters will be
8488 * blocked by i_checkStateDependency(MutableStateDep).
8489 */
8490
8491 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8492 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8493 {
8494 /* no need to use i_setMachineState() during init() */
8495 mData->mMachineState = MachineState_AbortedSaved;
8496 }
8497 else if (config.fAborted)
8498 {
8499 mSSData->strStateFilePath.setNull();
8500
8501 /* no need to use i_setMachineState() during init() */
8502 mData->mMachineState = MachineState_Aborted;
8503 }
8504 else if (!mSSData->strStateFilePath.isEmpty())
8505 {
8506 /* no need to use i_setMachineState() during init() */
8507 mData->mMachineState = MachineState_Saved;
8508 }
8509
8510 // after loading settings, we are no longer different from the XML on disk
8511 mData->flModifications = 0;
8512
8513 return S_OK;
8514}
8515
8516/**
8517 * Loads all snapshots starting from the given settings.
8518 *
8519 * @param data snapshot settings.
8520 * @param aCurSnapshotId Current snapshot ID from the settings file.
8521 */
8522HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8523 const Guid &aCurSnapshotId)
8524{
8525 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8526 AssertReturn(!i_isSessionMachine(), E_FAIL);
8527
8528 HRESULT hrc = S_OK;
8529
8530 std::list<const settings::Snapshot *> llSettingsTodo;
8531 llSettingsTodo.push_back(&data);
8532 std::list<Snapshot *> llParentsTodo;
8533 llParentsTodo.push_back(NULL);
8534
8535 while (llSettingsTodo.size() > 0)
8536 {
8537 const settings::Snapshot *current = llSettingsTodo.front();
8538 llSettingsTodo.pop_front();
8539 Snapshot *pParent = llParentsTodo.front();
8540 llParentsTodo.pop_front();
8541
8542 Utf8Str strStateFile;
8543 if (!current->strStateFile.isEmpty())
8544 {
8545 /* optional */
8546 strStateFile = current->strStateFile;
8547 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8548 if (RT_FAILURE(vrc))
8549 {
8550 setErrorBoth(E_FAIL, vrc,
8551 tr("Invalid saved state file path '%s' (%Rrc)"),
8552 strStateFile.c_str(), vrc);
8553 }
8554 }
8555
8556 /* create a snapshot machine object */
8557 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8558 pSnapshotMachine.createObject();
8559 hrc = pSnapshotMachine->initFromSettings(this,
8560 current->hardware,
8561 &current->debugging,
8562 &current->autostart,
8563 current->recordingSettings,
8564 current->uuid.ref(),
8565 strStateFile);
8566 if (FAILED(hrc)) break;
8567
8568 /* create a snapshot object */
8569 ComObjPtr<Snapshot> pSnapshot;
8570 pSnapshot.createObject();
8571 /* initialize the snapshot */
8572 hrc = pSnapshot->init(mParent, // VirtualBox object
8573 current->uuid,
8574 current->strName,
8575 current->strDescription,
8576 current->timestamp,
8577 pSnapshotMachine,
8578 pParent);
8579 if (FAILED(hrc)) break;
8580
8581 /* memorize the first snapshot if necessary */
8582 if (!mData->mFirstSnapshot)
8583 {
8584 Assert(pParent == NULL);
8585 mData->mFirstSnapshot = pSnapshot;
8586 }
8587
8588 /* memorize the current snapshot when appropriate */
8589 if ( !mData->mCurrentSnapshot
8590 && pSnapshot->i_getId() == aCurSnapshotId
8591 )
8592 mData->mCurrentSnapshot = pSnapshot;
8593
8594 /* create all children */
8595 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8596 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8597 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8598 {
8599 llSettingsTodo.push_back(&*it);
8600 llParentsTodo.push_back(pSnapshot);
8601 }
8602 }
8603
8604 return hrc;
8605}
8606
8607/**
8608 * Loads settings into mHWData.
8609 *
8610 * @param puuidRegistry Registry ID.
8611 * @param puuidSnapshot Snapshot ID
8612 * @param data Reference to the hardware settings.
8613 * @param pDbg Pointer to the debugging settings.
8614 * @param pAutostart Pointer to the autostart settings
8615 * @param recording Reference to recording settings.
8616 */
8617HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8618 const Guid *puuidSnapshot,
8619 const settings::Hardware &data,
8620 const settings::Debugging *pDbg,
8621 const settings::Autostart *pAutostart,
8622 const settings::Recording &recording)
8623{
8624 AssertReturn(!i_isSessionMachine(), E_FAIL);
8625
8626 HRESULT hrc = S_OK;
8627
8628 try
8629 {
8630 ComObjPtr<GuestOSType> pGuestOSType;
8631 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8632
8633 /* The hardware version attribute (optional). */
8634 mHWData->mHWVersion = data.strVersion;
8635 mHWData->mHardwareUUID = data.uuid;
8636
8637 mHWData->mCPUCount = data.cCPUs;
8638 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8639 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8640 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8641 mHWData->mCpuProfile = data.strCpuProfile;
8642
8643 // cpu
8644 if (mHWData->mCPUHotPlugEnabled)
8645 {
8646 for (settings::CpuList::const_iterator
8647 it = data.llCpus.begin();
8648 it != data.llCpus.end();
8649 ++it)
8650 {
8651 const settings::Cpu &cpu = *it;
8652
8653 mHWData->mCPUAttached[cpu.ulId] = true;
8654 }
8655 }
8656
8657 mHWData->mMemorySize = data.ulMemorySizeMB;
8658 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8659
8660 // boot order
8661 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8662 {
8663 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8664 if (it == data.mapBootOrder.end())
8665 mHWData->mBootOrder[i] = DeviceType_Null;
8666 else
8667 mHWData->mBootOrder[i] = it->second;
8668 }
8669
8670 mHWData->mPointingHIDType = data.pointingHIDType;
8671 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8672 mHWData->mParavirtProvider = data.paravirtProvider;
8673 mHWData->mParavirtDebug = data.strParavirtDebug;
8674 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8675
8676 /* GraphicsAdapter */
8677 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8678 if (FAILED(hrc)) return hrc;
8679
8680 /* VRDEServer */
8681 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8682 if (FAILED(hrc)) return hrc;
8683
8684 /* Platform */
8685 hrc = mPlatform->i_loadSettings(data.platformSettings);
8686 if (FAILED(hrc)) return hrc;
8687
8688 i_platformPropertiesUpdate();
8689
8690 /* Firmware */
8691 hrc = mFirmwareSettings->i_loadSettings(data.firmwareSettings);
8692 if (FAILED(hrc)) return hrc;
8693
8694 /* Recording */
8695 hrc = mRecordingSettings->i_loadSettings(recording);
8696 if (FAILED(hrc)) return hrc;
8697
8698 /* Trusted Platform Module */
8699 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8700 if (FAILED(hrc)) return hrc;
8701
8702 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
8703 if (FAILED(hrc)) return hrc;
8704
8705 // Bandwidth control (must come before network adapters)
8706 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
8707 if (FAILED(hrc)) return hrc;
8708
8709 /* USB controllers */
8710 for (settings::USBControllerList::const_iterator
8711 it = data.usbSettings.llUSBControllers.begin();
8712 it != data.usbSettings.llUSBControllers.end();
8713 ++it)
8714 {
8715 const settings::USBController &settingsCtrl = *it;
8716 ComObjPtr<USBController> newCtrl;
8717
8718 newCtrl.createObject();
8719 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8720 mUSBControllers->push_back(newCtrl);
8721 }
8722
8723 /* USB device filters */
8724 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8725 if (FAILED(hrc)) return hrc;
8726
8727 // network adapters (establish array size first and apply defaults, to
8728 // ensure reading the same settings as we saved, since the list skips
8729 // adapters having defaults)
8730 size_t const newCount = PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType);
8731 size_t const oldCount = mNetworkAdapters.size();
8732 if (newCount > oldCount)
8733 {
8734 mNetworkAdapters.resize(newCount);
8735 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8736 {
8737 unconst(mNetworkAdapters[slot]).createObject();
8738 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8739 }
8740 }
8741 else if (newCount < oldCount)
8742 mNetworkAdapters.resize(newCount);
8743 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8744 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8745 for (settings::NetworkAdaptersList::const_iterator
8746 it = data.llNetworkAdapters.begin();
8747 it != data.llNetworkAdapters.end();
8748 ++it)
8749 {
8750 const settings::NetworkAdapter &nic = *it;
8751
8752 /* slot uniqueness is guaranteed by XML Schema */
8753 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8754 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8755 if (FAILED(hrc)) return hrc;
8756 }
8757
8758 // serial ports (establish defaults first, to ensure reading the same
8759 // settings as we saved, since the list skips ports having defaults)
8760 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8761 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8762 for (settings::SerialPortsList::const_iterator
8763 it = data.llSerialPorts.begin();
8764 it != data.llSerialPorts.end();
8765 ++it)
8766 {
8767 const settings::SerialPort &s = *it;
8768
8769 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8770 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8771 if (FAILED(hrc)) return hrc;
8772 }
8773
8774 // parallel ports (establish defaults first, to ensure reading the same
8775 // settings as we saved, since the list skips ports having defaults)
8776 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8777 mParallelPorts[i]->i_applyDefaults();
8778 for (settings::ParallelPortsList::const_iterator
8779 it = data.llParallelPorts.begin();
8780 it != data.llParallelPorts.end();
8781 ++it)
8782 {
8783 const settings::ParallelPort &p = *it;
8784
8785 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8786 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8787 if (FAILED(hrc)) return hrc;
8788 }
8789
8790 /* Audio settings */
8791 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
8792 if (FAILED(hrc)) return hrc;
8793
8794 /* storage controllers */
8795 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
8796 if (FAILED(hrc)) return hrc;
8797
8798 /* Shared folders */
8799 for (settings::SharedFoldersList::const_iterator
8800 it = data.llSharedFolders.begin();
8801 it != data.llSharedFolders.end();
8802 ++it)
8803 {
8804 const settings::SharedFolder &sf = *it;
8805
8806 ComObjPtr<SharedFolder> sharedFolder;
8807 /* Check for double entries. Not allowed! */
8808 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8809 if (SUCCEEDED(hrc))
8810 return setError(VBOX_E_OBJECT_IN_USE,
8811 tr("Shared folder named '%s' already exists"),
8812 sf.strName.c_str());
8813
8814 /* Create the new shared folder. Don't break on error. This will be
8815 * reported when the machine starts. */
8816 sharedFolder.createObject();
8817 hrc = sharedFolder->init(i_getMachine(),
8818 sf.strName,
8819 sf.strHostPath,
8820 RT_BOOL(sf.fWritable),
8821 RT_BOOL(sf.fAutoMount),
8822 sf.strAutoMountPoint,
8823 false /* fFailOnError */,
8824 sf.enmSymlinkPolicy);
8825 if (FAILED(hrc)) return hrc;
8826 mHWData->mSharedFolders.push_back(sharedFolder);
8827 }
8828
8829 // Clipboard
8830 mHWData->mClipboardMode = data.clipboardMode;
8831 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8832
8833 // drag'n'drop
8834 mHWData->mDnDMode = data.dndMode;
8835
8836 // guest settings
8837 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8838
8839 // IO settings
8840 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8841 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8842
8843 // Host PCI devices
8844 for (settings::HostPCIDeviceAttachmentList::const_iterator
8845 it = data.pciAttachments.begin();
8846 it != data.pciAttachments.end();
8847 ++it)
8848 {
8849 const settings::HostPCIDeviceAttachment &hpda = *it;
8850 ComObjPtr<PCIDeviceAttachment> pda;
8851
8852 pda.createObject();
8853 pda->i_loadSettings(this, hpda);
8854 mHWData->mPCIDeviceAssignments.push_back(pda);
8855 }
8856
8857 /*
8858 * (The following isn't really real hardware, but it lives in HWData
8859 * for reasons of convenience.)
8860 */
8861
8862#ifdef VBOX_WITH_GUEST_PROPS
8863 /* Guest properties (optional) */
8864
8865 /* Only load transient guest properties for configs which have saved
8866 * state, because there shouldn't be any for powered off VMs. The same
8867 * logic applies for snapshots, as offline snapshots shouldn't have
8868 * any such properties. They confuse the code in various places.
8869 * Note: can't rely on the machine state, as it isn't set yet. */
8870 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8871 /* apologies for the hacky unconst() usage, but this needs hacking
8872 * actually inconsistent settings into consistency, otherwise there
8873 * will be some corner cases where the inconsistency survives
8874 * surprisingly long without getting fixed, especially for snapshots
8875 * as there are no config changes. */
8876 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8877 for (settings::GuestPropertiesList::iterator
8878 it = llGuestProperties.begin();
8879 it != llGuestProperties.end();
8880 /*nothing*/)
8881 {
8882 const settings::GuestProperty &prop = *it;
8883 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8884 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8885 if ( fSkipTransientGuestProperties
8886 && ( fFlags & GUEST_PROP_F_TRANSIENT
8887 || fFlags & GUEST_PROP_F_TRANSRESET))
8888 {
8889 it = llGuestProperties.erase(it);
8890 continue;
8891 }
8892 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8893 mHWData->mGuestProperties[prop.strName] = property;
8894 ++it;
8895 }
8896#endif /* VBOX_WITH_GUEST_PROPS defined */
8897
8898 hrc = i_loadDebugging(pDbg);
8899 if (FAILED(hrc))
8900 return hrc;
8901
8902 mHWData->mAutostart = *pAutostart;
8903
8904 /* default frontend */
8905 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8906 }
8907 catch (std::bad_alloc &)
8908 {
8909 return E_OUTOFMEMORY;
8910 }
8911
8912 AssertComRC(hrc);
8913 return hrc;
8914}
8915
8916/**
8917 * Called from i_loadHardware() to load the debugging settings of the
8918 * machine.
8919 *
8920 * @param pDbg Pointer to the settings.
8921 */
8922HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8923{
8924 mHWData->mDebugging = *pDbg;
8925 /* no more processing currently required, this will probably change. */
8926
8927 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
8928 if (FAILED(hrc)) return hrc;
8929
8930 return S_OK;
8931}
8932
8933/**
8934 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8935 *
8936 * @param data storage settings.
8937 * @param puuidRegistry media registry ID to set media to or NULL;
8938 * see Machine::i_loadMachineDataFromSettings()
8939 * @param puuidSnapshot snapshot ID
8940 * @return
8941 */
8942HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8943 const Guid *puuidRegistry,
8944 const Guid *puuidSnapshot)
8945{
8946 AssertReturn(!i_isSessionMachine(), E_FAIL);
8947
8948 HRESULT hrc = S_OK;
8949
8950 for (settings::StorageControllersList::const_iterator
8951 it = data.llStorageControllers.begin();
8952 it != data.llStorageControllers.end();
8953 ++it)
8954 {
8955 const settings::StorageController &ctlData = *it;
8956
8957 ComObjPtr<StorageController> pCtl;
8958 /* Try to find one with the name first. */
8959 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8960 if (SUCCEEDED(hrc))
8961 return setError(VBOX_E_OBJECT_IN_USE,
8962 tr("Storage controller named '%s' already exists"),
8963 ctlData.strName.c_str());
8964
8965 pCtl.createObject();
8966 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
8967 if (FAILED(hrc)) return hrc;
8968
8969 mStorageControllers->push_back(pCtl);
8970
8971 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8972 if (FAILED(hrc)) return hrc;
8973
8974 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8975 if (FAILED(hrc)) return hrc;
8976
8977 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8978 if (FAILED(hrc)) return hrc;
8979
8980 /* Load the attached devices now. */
8981 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
8982 if (FAILED(hrc)) return hrc;
8983 }
8984
8985 return S_OK;
8986}
8987
8988/**
8989 * Called from i_loadStorageControllers for a controller's devices.
8990 *
8991 * @param aStorageController
8992 * @param data
8993 * @param puuidRegistry media registry ID to set media to or NULL; see
8994 * Machine::i_loadMachineDataFromSettings()
8995 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
8996 * @return
8997 */
8998HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8999 const settings::StorageController &data,
9000 const Guid *puuidRegistry,
9001 const Guid *puuidSnapshot)
9002{
9003 HRESULT hrc = S_OK;
9004
9005 /* paranoia: detect duplicate attachments */
9006 for (settings::AttachedDevicesList::const_iterator
9007 it = data.llAttachedDevices.begin();
9008 it != data.llAttachedDevices.end();
9009 ++it)
9010 {
9011 const settings::AttachedDevice &ad = *it;
9012
9013 for (settings::AttachedDevicesList::const_iterator it2 = it;
9014 it2 != data.llAttachedDevices.end();
9015 ++it2)
9016 {
9017 if (it == it2)
9018 continue;
9019
9020 const settings::AttachedDevice &ad2 = *it2;
9021
9022 if ( ad.lPort == ad2.lPort
9023 && ad.lDevice == ad2.lDevice)
9024 {
9025 return setError(E_FAIL,
9026 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9027 aStorageController->i_getName().c_str(),
9028 ad.lPort,
9029 ad.lDevice,
9030 mUserData->s.strName.c_str());
9031 }
9032 }
9033 }
9034
9035 for (settings::AttachedDevicesList::const_iterator
9036 it = data.llAttachedDevices.begin();
9037 it != data.llAttachedDevices.end();
9038 ++it)
9039 {
9040 const settings::AttachedDevice &dev = *it;
9041 ComObjPtr<Medium> medium;
9042
9043 switch (dev.deviceType)
9044 {
9045 case DeviceType_Floppy:
9046 case DeviceType_DVD:
9047 if (dev.strHostDriveSrc.isNotEmpty())
9048 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9049 false /* fRefresh */, medium);
9050 else
9051 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9052 dev.uuid,
9053 false /* fRefresh */,
9054 false /* aSetError */,
9055 medium);
9056 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9057 // This is not an error. The host drive or UUID might have vanished, so just go
9058 // ahead without this removeable medium attachment
9059 hrc = S_OK;
9060 break;
9061
9062 case DeviceType_HardDisk:
9063 {
9064 /* find a hard disk by UUID */
9065 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9066 if (FAILED(hrc))
9067 {
9068 if (i_isSnapshotMachine())
9069 {
9070 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9071 // so the user knows that the bad disk is in a snapshot somewhere
9072 com::ErrorInfo info;
9073 return setError(E_FAIL,
9074 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9075 puuidSnapshot->raw(),
9076 info.getText().raw());
9077 }
9078 return hrc;
9079 }
9080
9081 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9082
9083 if (medium->i_getType() == MediumType_Immutable)
9084 {
9085 if (i_isSnapshotMachine())
9086 return setError(E_FAIL,
9087 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9088 "of the virtual machine '%s' ('%s')"),
9089 medium->i_getLocationFull().c_str(),
9090 dev.uuid.raw(),
9091 puuidSnapshot->raw(),
9092 mUserData->s.strName.c_str(),
9093 mData->m_strConfigFileFull.c_str());
9094
9095 return setError(E_FAIL,
9096 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9097 medium->i_getLocationFull().c_str(),
9098 dev.uuid.raw(),
9099 mUserData->s.strName.c_str(),
9100 mData->m_strConfigFileFull.c_str());
9101 }
9102
9103 if (medium->i_getType() == MediumType_MultiAttach)
9104 {
9105 if (i_isSnapshotMachine())
9106 return setError(E_FAIL,
9107 tr("Multi-attach 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("Multi-attach 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 ( !i_isSnapshotMachine()
9124 && medium->i_getChildren().size() != 0
9125 )
9126 return setError(E_FAIL,
9127 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9128 "because it has %d differencing child hard disks"),
9129 medium->i_getLocationFull().c_str(),
9130 dev.uuid.raw(),
9131 mUserData->s.strName.c_str(),
9132 mData->m_strConfigFileFull.c_str(),
9133 medium->i_getChildren().size());
9134
9135 if (i_findAttachment(*mMediumAttachments.data(),
9136 medium))
9137 return setError(E_FAIL,
9138 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9139 medium->i_getLocationFull().c_str(),
9140 dev.uuid.raw(),
9141 mUserData->s.strName.c_str(),
9142 mData->m_strConfigFileFull.c_str());
9143
9144 break;
9145 }
9146
9147 default:
9148 return setError(E_FAIL,
9149 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9150 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9151 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9152 }
9153
9154 if (FAILED(hrc))
9155 break;
9156
9157 /* Bandwidth groups are loaded at this point. */
9158 ComObjPtr<BandwidthGroup> pBwGroup;
9159
9160 if (!dev.strBwGroup.isEmpty())
9161 {
9162 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9163 if (FAILED(hrc))
9164 return setError(E_FAIL,
9165 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9166 medium->i_getLocationFull().c_str(),
9167 dev.strBwGroup.c_str(),
9168 mUserData->s.strName.c_str(),
9169 mData->m_strConfigFileFull.c_str());
9170 pBwGroup->i_reference();
9171 }
9172
9173 const Utf8Str controllerName = aStorageController->i_getName();
9174 ComObjPtr<MediumAttachment> pAttachment;
9175 pAttachment.createObject();
9176 hrc = pAttachment->init(this,
9177 medium,
9178 controllerName,
9179 dev.lPort,
9180 dev.lDevice,
9181 dev.deviceType,
9182 false,
9183 dev.fPassThrough,
9184 dev.fTempEject,
9185 dev.fNonRotational,
9186 dev.fDiscard,
9187 dev.fHotPluggable,
9188 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9189 if (FAILED(hrc)) break;
9190
9191 /* associate the medium with this machine and snapshot */
9192 if (!medium.isNull())
9193 {
9194 AutoCaller medCaller(medium);
9195 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9196 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9197
9198 if (i_isSnapshotMachine())
9199 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9200 else
9201 hrc = medium->i_addBackReference(mData->mUuid);
9202 /* If the medium->addBackReference fails it sets an appropriate
9203 * error message, so no need to do any guesswork here. */
9204
9205 if (puuidRegistry)
9206 // caller wants registry ID to be set on all attached media (OVF import case)
9207 medium->i_addRegistry(*puuidRegistry);
9208 }
9209
9210 if (FAILED(hrc))
9211 break;
9212
9213 /* back up mMediumAttachments to let registeredInit() properly rollback
9214 * on failure (= limited accessibility) */
9215 i_setModified(IsModified_Storage);
9216 mMediumAttachments.backup();
9217 mMediumAttachments->push_back(pAttachment);
9218 }
9219
9220 return hrc;
9221}
9222
9223/**
9224 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9225 *
9226 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9227 * @param aSnapshot where to return the found snapshot
9228 * @param aSetError true to set extended error info on failure
9229 */
9230HRESULT Machine::i_findSnapshotById(const Guid &aId,
9231 ComObjPtr<Snapshot> &aSnapshot,
9232 bool aSetError /* = false */)
9233{
9234 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9235
9236 if (!mData->mFirstSnapshot)
9237 {
9238 if (aSetError)
9239 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9240 return E_FAIL;
9241 }
9242
9243 if (aId.isZero())
9244 aSnapshot = mData->mFirstSnapshot;
9245 else
9246 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9247
9248 if (!aSnapshot)
9249 {
9250 if (aSetError)
9251 return setError(E_FAIL,
9252 tr("Could not find a snapshot with UUID {%s}"),
9253 aId.toString().c_str());
9254 return E_FAIL;
9255 }
9256
9257 return S_OK;
9258}
9259
9260/**
9261 * Returns the snapshot with the given name or fails of no such snapshot.
9262 *
9263 * @param strName snapshot name to find
9264 * @param aSnapshot where to return the found snapshot
9265 * @param aSetError true to set extended error info on failure
9266 */
9267HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9268 ComObjPtr<Snapshot> &aSnapshot,
9269 bool aSetError /* = false */)
9270{
9271 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9272
9273 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9274
9275 if (!mData->mFirstSnapshot)
9276 {
9277 if (aSetError)
9278 return setError(VBOX_E_OBJECT_NOT_FOUND,
9279 tr("This machine does not have any snapshots"));
9280 return VBOX_E_OBJECT_NOT_FOUND;
9281 }
9282
9283 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9284
9285 if (!aSnapshot)
9286 {
9287 if (aSetError)
9288 return setError(VBOX_E_OBJECT_NOT_FOUND,
9289 tr("Could not find a snapshot named '%s'"), strName.c_str());
9290 return VBOX_E_OBJECT_NOT_FOUND;
9291 }
9292
9293 return S_OK;
9294}
9295
9296/**
9297 * Returns a storage controller object with the given name.
9298 *
9299 * @param aName storage controller name to find
9300 * @param aStorageController where to return the found storage controller
9301 * @param aSetError true to set extended error info on failure
9302 */
9303HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9304 ComObjPtr<StorageController> &aStorageController,
9305 bool aSetError /* = false */)
9306{
9307 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9308
9309 for (StorageControllerList::const_iterator
9310 it = mStorageControllers->begin();
9311 it != mStorageControllers->end();
9312 ++it)
9313 {
9314 if ((*it)->i_getName() == aName)
9315 {
9316 aStorageController = (*it);
9317 return S_OK;
9318 }
9319 }
9320
9321 if (aSetError)
9322 return setError(VBOX_E_OBJECT_NOT_FOUND,
9323 tr("Could not find a storage controller named '%s'"),
9324 aName.c_str());
9325 return VBOX_E_OBJECT_NOT_FOUND;
9326}
9327
9328/**
9329 * Returns a USB controller object with the given name.
9330 *
9331 * @param aName USB controller name to find
9332 * @param aUSBController where to return the found USB controller
9333 * @param aSetError true to set extended error info on failure
9334 */
9335HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9336 ComObjPtr<USBController> &aUSBController,
9337 bool aSetError /* = false */)
9338{
9339 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9340
9341 for (USBControllerList::const_iterator
9342 it = mUSBControllers->begin();
9343 it != mUSBControllers->end();
9344 ++it)
9345 {
9346 if ((*it)->i_getName() == aName)
9347 {
9348 aUSBController = (*it);
9349 return S_OK;
9350 }
9351 }
9352
9353 if (aSetError)
9354 return setError(VBOX_E_OBJECT_NOT_FOUND,
9355 tr("Could not find a storage controller named '%s'"),
9356 aName.c_str());
9357 return VBOX_E_OBJECT_NOT_FOUND;
9358}
9359
9360/**
9361 * Returns the number of USB controller instance of the given type.
9362 *
9363 * @param enmType USB controller type.
9364 */
9365ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9366{
9367 ULONG cCtrls = 0;
9368
9369 for (USBControllerList::const_iterator
9370 it = mUSBControllers->begin();
9371 it != mUSBControllers->end();
9372 ++it)
9373 {
9374 if ((*it)->i_getControllerType() == enmType)
9375 cCtrls++;
9376 }
9377
9378 return cCtrls;
9379}
9380
9381HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9382 MediumAttachmentList &atts)
9383{
9384 AutoCaller autoCaller(this);
9385 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
9386
9387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9388
9389 for (MediumAttachmentList::const_iterator
9390 it = mMediumAttachments->begin();
9391 it != mMediumAttachments->end();
9392 ++it)
9393 {
9394 const ComObjPtr<MediumAttachment> &pAtt = *it;
9395 // should never happen, but deal with NULL pointers in the list.
9396 AssertContinue(!pAtt.isNull());
9397
9398 // getControllerName() needs caller+read lock
9399 AutoCaller autoAttCaller(pAtt);
9400 if (FAILED(autoAttCaller.hrc()))
9401 {
9402 atts.clear();
9403 return autoAttCaller.hrc();
9404 }
9405 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9406
9407 if (pAtt->i_getControllerName() == aName)
9408 atts.push_back(pAtt);
9409 }
9410
9411 return S_OK;
9412}
9413
9414
9415/**
9416 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9417 * file if the machine name was changed and about creating a new settings file
9418 * if this is a new machine.
9419 *
9420 * @note Must be never called directly but only from #saveSettings().
9421 */
9422HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9423 bool *pfSettingsFileIsNew)
9424{
9425 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9426
9427 HRESULT hrc = S_OK;
9428
9429 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9430 /// @todo need to handle primary group change, too
9431
9432 /* attempt to rename the settings file if machine name is changed */
9433 if ( mUserData->s.fNameSync
9434 && mUserData.isBackedUp()
9435 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9436 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9437 )
9438 {
9439 bool dirRenamed = false;
9440 bool fileRenamed = false;
9441
9442 Utf8Str configFile, newConfigFile;
9443 Utf8Str configFilePrev, newConfigFilePrev;
9444 Utf8Str NVRAMFile, newNVRAMFile;
9445 Utf8Str configDir, newConfigDir;
9446
9447 do
9448 {
9449 int vrc = VINF_SUCCESS;
9450
9451 Utf8Str name = mUserData.backedUpData()->s.strName;
9452 Utf8Str newName = mUserData->s.strName;
9453 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9454 if (group == "/")
9455 group.setNull();
9456 Utf8Str newGroup = mUserData->s.llGroups.front();
9457 if (newGroup == "/")
9458 newGroup.setNull();
9459
9460 configFile = mData->m_strConfigFileFull;
9461
9462 /* first, rename the directory if it matches the group and machine name */
9463 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9464 /** @todo hack, make somehow use of ComposeMachineFilename */
9465 if (mUserData->s.fDirectoryIncludesUUID)
9466 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9467 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9468 /** @todo hack, make somehow use of ComposeMachineFilename */
9469 if (mUserData->s.fDirectoryIncludesUUID)
9470 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9471 configDir = configFile;
9472 configDir.stripFilename();
9473 newConfigDir = configDir;
9474 if ( configDir.length() >= groupPlusName.length()
9475 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9476 groupPlusName.c_str()))
9477 {
9478 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9479 Utf8Str newConfigBaseDir(newConfigDir);
9480 newConfigDir.append(newGroupPlusName);
9481 /* consistency: use \ if appropriate on the platform */
9482 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9483 /* new dir and old dir cannot be equal here because of 'if'
9484 * above and because name != newName */
9485 Assert(configDir != newConfigDir);
9486 if (!fSettingsFileIsNew)
9487 {
9488 /* perform real rename only if the machine is not new */
9489 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9490 if ( vrc == VERR_FILE_NOT_FOUND
9491 || vrc == VERR_PATH_NOT_FOUND)
9492 {
9493 /* create the parent directory, then retry renaming */
9494 Utf8Str parent(newConfigDir);
9495 parent.stripFilename();
9496 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9497 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9498 }
9499 if (RT_FAILURE(vrc))
9500 {
9501 hrc = setErrorBoth(E_FAIL, vrc,
9502 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9503 configDir.c_str(),
9504 newConfigDir.c_str(),
9505 vrc);
9506 break;
9507 }
9508 /* delete subdirectories which are no longer needed */
9509 Utf8Str dir(configDir);
9510 dir.stripFilename();
9511 while (dir != newConfigBaseDir && dir != ".")
9512 {
9513 vrc = RTDirRemove(dir.c_str());
9514 if (RT_FAILURE(vrc))
9515 break;
9516 dir.stripFilename();
9517 }
9518 dirRenamed = true;
9519 }
9520 }
9521
9522 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9523
9524 /* then try to rename the settings file itself */
9525 if (newConfigFile != configFile)
9526 {
9527 /* get the path to old settings file in renamed directory */
9528 Assert(mData->m_strConfigFileFull == configFile);
9529 configFile.printf("%s%c%s",
9530 newConfigDir.c_str(),
9531 RTPATH_DELIMITER,
9532 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9533 if (!fSettingsFileIsNew)
9534 {
9535 /* perform real rename only if the machine is not new */
9536 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9537 if (RT_FAILURE(vrc))
9538 {
9539 hrc = setErrorBoth(E_FAIL, vrc,
9540 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9541 configFile.c_str(),
9542 newConfigFile.c_str(),
9543 vrc);
9544 break;
9545 }
9546 fileRenamed = true;
9547 configFilePrev = configFile;
9548 configFilePrev += "-prev";
9549 newConfigFilePrev = newConfigFile;
9550 newConfigFilePrev += "-prev";
9551 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9552 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9553 if (NVRAMFile.isNotEmpty())
9554 {
9555 // in the NVRAM file path, replace the old directory with the new directory
9556 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9557 {
9558 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9559 NVRAMFile = newConfigDir + strNVRAMFile;
9560 }
9561 newNVRAMFile = newConfigFile;
9562 newNVRAMFile.stripSuffix();
9563 newNVRAMFile += ".nvram";
9564 RTPathRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9565 }
9566 }
9567 }
9568
9569 // update m_strConfigFileFull amd mConfigFile
9570 mData->m_strConfigFileFull = newConfigFile;
9571 // compute the relative path too
9572 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9573
9574 // store the old and new so that VirtualBox::i_saveSettings() can update
9575 // the media registry
9576 if ( mData->mRegistered
9577 && (configDir != newConfigDir || configFile != newConfigFile))
9578 {
9579 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9580
9581 if (pfNeedsGlobalSaveSettings)
9582 *pfNeedsGlobalSaveSettings = true;
9583 }
9584
9585 // in the saved state file path, replace the old directory with the new directory
9586 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9587 {
9588 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9589 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9590 }
9591 if (newNVRAMFile.isNotEmpty())
9592 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9593
9594 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9595 if (mData->mFirstSnapshot)
9596 {
9597 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9598 newConfigDir.c_str());
9599 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9600 newConfigDir.c_str());
9601 }
9602 }
9603 while (0);
9604
9605 if (FAILED(hrc))
9606 {
9607 /* silently try to rename everything back */
9608 if (fileRenamed)
9609 {
9610 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9611 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9612 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9613 RTPathRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9614 }
9615 if (dirRenamed)
9616 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9617 }
9618
9619 if (FAILED(hrc)) return hrc;
9620 }
9621
9622 if (fSettingsFileIsNew)
9623 {
9624 /* create a virgin config file */
9625 int vrc = VINF_SUCCESS;
9626
9627 /* ensure the settings directory exists */
9628 Utf8Str path(mData->m_strConfigFileFull);
9629 path.stripFilename();
9630 if (!RTDirExists(path.c_str()))
9631 {
9632 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9633 if (RT_FAILURE(vrc))
9634 {
9635 return setErrorBoth(E_FAIL, vrc,
9636 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9637 path.c_str(),
9638 vrc);
9639 }
9640 }
9641
9642 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9643 path = mData->m_strConfigFileFull;
9644 RTFILE f = NIL_RTFILE;
9645 vrc = RTFileOpen(&f, path.c_str(),
9646 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9647 if (RT_FAILURE(vrc))
9648 return setErrorBoth(E_FAIL, vrc,
9649 tr("Could not create the settings file '%s' (%Rrc)"),
9650 path.c_str(),
9651 vrc);
9652 RTFileClose(f);
9653 }
9654 if (pfSettingsFileIsNew)
9655 *pfSettingsFileIsNew = fSettingsFileIsNew;
9656
9657 return hrc;
9658}
9659
9660/**
9661 * Saves and commits machine data, user data and hardware data.
9662 *
9663 * Note that on failure, the data remains uncommitted.
9664 *
9665 * @a aFlags may combine the following flags:
9666 *
9667 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9668 * Used when saving settings after an operation that makes them 100%
9669 * correspond to the settings from the current snapshot.
9670 * - SaveS_Force: settings will be saved without doing a deep compare of the
9671 * settings structures. This is used when this is called because snapshots
9672 * have changed to avoid the overhead of the deep compare.
9673 *
9674 * @note Must be called from under this object's write lock. Locks children for
9675 * writing.
9676 *
9677 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9678 * initialized to false and that will be set to true by this function if
9679 * the caller must invoke VirtualBox::i_saveSettings() because the global
9680 * settings have changed. This will happen if a machine rename has been
9681 * saved and the global machine and media registries will therefore need
9682 * updating.
9683 * @param alock Reference to the lock for this machine object.
9684 * @param aFlags Flags.
9685 */
9686HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9687 AutoWriteLock &alock,
9688 int aFlags /*= 0*/)
9689{
9690 LogFlowThisFuncEnter();
9691
9692 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9693
9694 /* make sure child objects are unable to modify the settings while we are
9695 * saving them */
9696 i_ensureNoStateDependencies(alock);
9697
9698 AssertReturn(!i_isSnapshotMachine(),
9699 E_FAIL);
9700
9701 if (!mData->mAccessible)
9702 return setError(VBOX_E_INVALID_VM_STATE,
9703 tr("The machine is not accessible, so cannot save settings"));
9704
9705 HRESULT hrc = S_OK;
9706 PCVBOXCRYPTOIF pCryptoIf = NULL;
9707 const char *pszPassword = NULL;
9708 SecretKey *pKey = NULL;
9709
9710#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9711 if (mData->mstrKeyId.isNotEmpty())
9712 {
9713 /* VM is going to be encrypted. */
9714 alock.release(); /** @todo Revise the locking. */
9715 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
9716 alock.acquire();
9717 if (FAILED(hrc)) return hrc; /* Error is set. */
9718
9719 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
9720 if (RT_SUCCESS(vrc))
9721 pszPassword = (const char *)pKey->getKeyBuffer();
9722 else
9723 {
9724 mParent->i_releaseCryptoIf(pCryptoIf);
9725 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
9726 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
9727 mData->mstrKeyId.c_str(), vrc);
9728 }
9729 }
9730#else
9731 RT_NOREF(pKey);
9732#endif
9733
9734 bool fNeedsWrite = false;
9735 bool fSettingsFileIsNew = false;
9736
9737 /* First, prepare to save settings. It will care about renaming the
9738 * settings directory and file if the machine name was changed and about
9739 * creating a new settings file if this is a new machine. */
9740 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
9741 if (FAILED(hrc))
9742 {
9743#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9744 if (pCryptoIf)
9745 {
9746 alock.release(); /** @todo Revise the locking. */
9747 mParent->i_releaseCryptoIf(pCryptoIf);
9748 alock.acquire();
9749 }
9750 if (pKey)
9751 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9752#endif
9753 return hrc;
9754 }
9755
9756 // keep a pointer to the current settings structures
9757 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9758 settings::MachineConfigFile *pNewConfig = NULL;
9759
9760 try
9761 {
9762 // make a fresh one to have everyone write stuff into
9763 pNewConfig = new settings::MachineConfigFile(NULL);
9764 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9765#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9766 pNewConfig->strKeyId = mData->mstrKeyId;
9767 pNewConfig->strKeyStore = mData->mstrKeyStore;
9768#endif
9769
9770 // now go and copy all the settings data from COM to the settings structures
9771 // (this calls i_saveSettings() on all the COM objects in the machine)
9772 i_copyMachineDataToSettings(*pNewConfig);
9773
9774 if (aFlags & SaveS_ResetCurStateModified)
9775 {
9776 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9777 mData->mCurrentStateModified = FALSE;
9778 fNeedsWrite = true; // always, no need to compare
9779 }
9780 else if (aFlags & SaveS_Force)
9781 {
9782 fNeedsWrite = true; // always, no need to compare
9783 }
9784 else
9785 {
9786 if (!mData->mCurrentStateModified)
9787 {
9788 // do a deep compare of the settings that we just saved with the settings
9789 // previously stored in the config file; this invokes MachineConfigFile::operator==
9790 // which does a deep compare of all the settings, which is expensive but less expensive
9791 // than writing out XML in vain
9792 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9793
9794 // could still be modified if any settings changed
9795 mData->mCurrentStateModified = fAnySettingsChanged;
9796
9797 fNeedsWrite = fAnySettingsChanged;
9798 }
9799 else
9800 fNeedsWrite = true;
9801 }
9802
9803 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9804
9805 if (fNeedsWrite)
9806 {
9807 // now spit it all out!
9808 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
9809 if (aFlags & SaveS_RemoveBackup)
9810 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
9811 }
9812
9813 mData->pMachineConfigFile = pNewConfig;
9814 delete pOldConfig;
9815 i_commit();
9816
9817 // after saving settings, we are no longer different from the XML on disk
9818 mData->flModifications = 0;
9819 }
9820 catch (HRESULT err)
9821 {
9822 // we assume that error info is set by the thrower
9823 hrc = err;
9824
9825 // delete any newly created settings file
9826 if (fSettingsFileIsNew)
9827 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
9828
9829 // restore old config
9830 delete pNewConfig;
9831 mData->pMachineConfigFile = pOldConfig;
9832 }
9833 catch (...)
9834 {
9835 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9836 }
9837
9838#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9839 if (pCryptoIf)
9840 {
9841 alock.release(); /** @todo Revise the locking. */
9842 mParent->i_releaseCryptoIf(pCryptoIf);
9843 alock.acquire();
9844 }
9845 if (pKey)
9846 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9847#endif
9848
9849 if (fNeedsWrite)
9850 {
9851 /* Fire the data change event, even on failure (since we've already
9852 * committed all data). This is done only for SessionMachines because
9853 * mutable Machine instances are always not registered (i.e. private
9854 * to the client process that creates them) and thus don't need to
9855 * inform callbacks. */
9856 if (i_isSessionMachine())
9857 mParent->i_onMachineDataChanged(mData->mUuid);
9858 }
9859
9860 LogFlowThisFunc(("hrc=%08X\n", hrc));
9861 LogFlowThisFuncLeave();
9862 return hrc;
9863}
9864
9865/**
9866 * Implementation for saving the machine settings into the given
9867 * settings::MachineConfigFile instance. This copies machine extradata
9868 * from the previous machine config file in the instance data, if any.
9869 *
9870 * This gets called from two locations:
9871 *
9872 * -- Machine::i_saveSettings(), during the regular XML writing;
9873 *
9874 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9875 * exported to OVF and we write the VirtualBox proprietary XML
9876 * into a <vbox:Machine> tag.
9877 *
9878 * This routine fills all the fields in there, including snapshots, *except*
9879 * for the following:
9880 *
9881 * -- fCurrentStateModified. There is some special logic associated with that.
9882 *
9883 * The caller can then call MachineConfigFile::write() or do something else
9884 * with it.
9885 *
9886 * Caller must hold the machine lock!
9887 *
9888 * This throws XML errors and HRESULT, so the caller must have a catch block!
9889 */
9890void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9891{
9892 // deep copy extradata, being extra careful with self assignment (the STL
9893 // map assignment on Mac OS X clang based Xcode isn't checking)
9894 if (&config != mData->pMachineConfigFile)
9895 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9896
9897 config.uuid = mData->mUuid;
9898
9899 // copy name, description, OS type, teleport, UTC etc.
9900 config.machineUserData = mUserData->s;
9901
9902#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9903 config.strStateKeyId = mSSData->strStateKeyId;
9904 config.strStateKeyStore = mSSData->strStateKeyStore;
9905 config.strLogKeyId = mData->mstrLogKeyId;
9906 config.strLogKeyStore = mData->mstrLogKeyStore;
9907#endif
9908
9909 if ( mData->mMachineState == MachineState_Saved
9910 || mData->mMachineState == MachineState_AbortedSaved
9911 || mData->mMachineState == MachineState_Restoring
9912 // when doing certain snapshot operations we may or may not have
9913 // a saved state in the current state, so keep everything as is
9914 || ( ( mData->mMachineState == MachineState_Snapshotting
9915 || mData->mMachineState == MachineState_DeletingSnapshot
9916 || mData->mMachineState == MachineState_RestoringSnapshot)
9917 && (!mSSData->strStateFilePath.isEmpty())
9918 )
9919 )
9920 {
9921 Assert(!mSSData->strStateFilePath.isEmpty());
9922 /* try to make the file name relative to the settings file dir */
9923 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9924 }
9925 else
9926 {
9927 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9928 config.strStateFile.setNull();
9929 }
9930
9931 if (mData->mCurrentSnapshot)
9932 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9933 else
9934 config.uuidCurrentSnapshot.clear();
9935
9936 config.timeLastStateChange = mData->mLastStateChange;
9937 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
9938 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9939
9940 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
9941 if (FAILED(hrc)) throw hrc;
9942
9943 // save machine's media registry if this is VirtualBox 4.0 or later
9944 if (config.canHaveOwnMediaRegistry())
9945 {
9946 // determine machine folder
9947 Utf8Str strMachineFolder = i_getSettingsFileFull();
9948 strMachineFolder.stripFilename();
9949 mParent->i_saveMediaRegistry(config.mediaRegistry,
9950 i_getId(), // only media with registry ID == machine UUID
9951 strMachineFolder);
9952 // this throws HRESULT
9953 }
9954
9955 // save snapshots
9956 hrc = i_saveAllSnapshots(config);
9957 if (FAILED(hrc)) throw hrc;
9958}
9959
9960/**
9961 * Saves all snapshots of the machine into the given machine config file. Called
9962 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9963 * @param config
9964 * @return
9965 */
9966HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9967{
9968 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9969
9970 HRESULT hrc = S_OK;
9971
9972 try
9973 {
9974 config.llFirstSnapshot.clear();
9975
9976 if (mData->mFirstSnapshot)
9977 {
9978 // the settings use a list for "the first snapshot"
9979 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9980
9981 // get reference to the snapshot on the list and work on that
9982 // element straight in the list to avoid excessive copying later
9983 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9984 if (FAILED(hrc)) throw hrc;
9985 }
9986
9987// if (mType == IsSessionMachine)
9988// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9989
9990 }
9991 catch (HRESULT err)
9992 {
9993 /* we assume that error info is set by the thrower */
9994 hrc = err;
9995 }
9996 catch (...)
9997 {
9998 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9999 }
10000
10001 return hrc;
10002}
10003
10004/**
10005 * Saves the VM hardware configuration. It is assumed that the
10006 * given node is empty.
10007 *
10008 * @param data Reference to the settings object for the hardware config.
10009 * @param pDbg Pointer to the settings object for the debugging config
10010 * which happens to live in mHWData.
10011 * @param pAutostart Pointer to the settings object for the autostart config
10012 * which happens to live in mHWData.
10013 * @param recording Reference to reecording settings.
10014 */
10015HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10016 settings::Autostart *pAutostart, settings::Recording &recording)
10017{
10018 HRESULT hrc = S_OK;
10019
10020 try
10021 {
10022 /* The hardware version attribute (optional).
10023 Automatically upgrade from 1 to current default hardware version
10024 when there is no saved state. (ugly!) */
10025 if ( mHWData->mHWVersion == "1"
10026 && mSSData->strStateFilePath.isEmpty()
10027 )
10028 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10029
10030 data.strVersion = mHWData->mHWVersion;
10031 data.uuid = mHWData->mHardwareUUID;
10032
10033 // CPU
10034 data.cCPUs = mHWData->mCPUCount;
10035 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10036 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10037 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10038 data.strCpuProfile = mHWData->mCpuProfile;
10039
10040 data.llCpus.clear();
10041 if (data.fCpuHotPlug)
10042 {
10043 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10044 {
10045 if (mHWData->mCPUAttached[idx])
10046 {
10047 settings::Cpu cpu;
10048 cpu.ulId = idx;
10049 data.llCpus.push_back(cpu);
10050 }
10051 }
10052 }
10053
10054 // memory
10055 data.ulMemorySizeMB = mHWData->mMemorySize;
10056 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10057
10058 // HID
10059 data.pointingHIDType = mHWData->mPointingHIDType;
10060 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10061
10062 // paravirt
10063 data.paravirtProvider = mHWData->mParavirtProvider;
10064 data.strParavirtDebug = mHWData->mParavirtDebug;
10065
10066 // emulated USB card reader
10067 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10068
10069 // boot order
10070 data.mapBootOrder.clear();
10071 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10072 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10073
10074 /* VRDEServer settings (optional) */
10075 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10076 if (FAILED(hrc)) throw hrc;
10077
10078 /* Platform (required) */
10079 hrc = mPlatform->i_saveSettings(data.platformSettings);
10080 if (FAILED(hrc)) return hrc;
10081
10082 /* Firmware settings (required) */
10083 hrc = mFirmwareSettings->i_saveSettings(data.firmwareSettings);
10084 if (FAILED(hrc)) throw hrc;
10085
10086 /* Recording settings. */
10087 hrc = mRecordingSettings->i_saveSettings(recording);
10088 if (FAILED(hrc)) throw hrc;
10089
10090 /* Trusted Platform Module settings (required) */
10091 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10092 if (FAILED(hrc)) throw hrc;
10093
10094 /* NVRAM settings (required) */
10095 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10096 if (FAILED(hrc)) throw hrc;
10097
10098 /* GraphicsAdapter settings (required) */
10099 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10100 if (FAILED(hrc)) throw hrc;
10101
10102 /* USB Controller (required) */
10103 data.usbSettings.llUSBControllers.clear();
10104 for (USBControllerList::const_iterator
10105 it = mUSBControllers->begin();
10106 it != mUSBControllers->end();
10107 ++it)
10108 {
10109 ComObjPtr<USBController> ctrl = *it;
10110 settings::USBController settingsCtrl;
10111
10112 settingsCtrl.strName = ctrl->i_getName();
10113 settingsCtrl.enmType = ctrl->i_getControllerType();
10114
10115 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10116 }
10117
10118 /* USB device filters (required) */
10119 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10120 if (FAILED(hrc)) throw hrc;
10121
10122 /* Network adapters (required) */
10123 size_t const uMaxNICs =
10124 RT_MIN(PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType), mNetworkAdapters.size());
10125 data.llNetworkAdapters.clear();
10126 /* Write out only the nominal number of network adapters for this
10127 * chipset type. Since Machine::commit() hasn't been called there
10128 * may be extra NIC settings in the vector. */
10129 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10130 {
10131 settings::NetworkAdapter nic;
10132 nic.ulSlot = (uint32_t)slot;
10133 /* paranoia check... must not be NULL, but must not crash either. */
10134 if (mNetworkAdapters[slot])
10135 {
10136 if (mNetworkAdapters[slot]->i_hasDefaults())
10137 continue;
10138
10139 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10140 if (FAILED(hrc)) throw hrc;
10141
10142 data.llNetworkAdapters.push_back(nic);
10143 }
10144 }
10145
10146 /* Serial ports */
10147 data.llSerialPorts.clear();
10148 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10149 {
10150 if (mSerialPorts[slot]->i_hasDefaults())
10151 continue;
10152
10153 settings::SerialPort s;
10154 s.ulSlot = slot;
10155 hrc = mSerialPorts[slot]->i_saveSettings(s);
10156 if (FAILED(hrc)) return hrc;
10157
10158 data.llSerialPorts.push_back(s);
10159 }
10160
10161 /* Parallel ports */
10162 data.llParallelPorts.clear();
10163 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10164 {
10165 if (mParallelPorts[slot]->i_hasDefaults())
10166 continue;
10167
10168 settings::ParallelPort p;
10169 p.ulSlot = slot;
10170 hrc = mParallelPorts[slot]->i_saveSettings(p);
10171 if (FAILED(hrc)) return hrc;
10172
10173 data.llParallelPorts.push_back(p);
10174 }
10175
10176 /* Audio settings */
10177 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10178 if (FAILED(hrc)) return hrc;
10179
10180 hrc = i_saveStorageControllers(data.storage);
10181 if (FAILED(hrc)) return hrc;
10182
10183 /* Shared folders */
10184 data.llSharedFolders.clear();
10185 for (HWData::SharedFolderList::const_iterator
10186 it = mHWData->mSharedFolders.begin();
10187 it != mHWData->mSharedFolders.end();
10188 ++it)
10189 {
10190 SharedFolder *pSF = *it;
10191 AutoCaller sfCaller(pSF);
10192 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10193 settings::SharedFolder sf;
10194 sf.strName = pSF->i_getName();
10195 sf.strHostPath = pSF->i_getHostPath();
10196 sf.fWritable = !!pSF->i_isWritable();
10197 sf.fAutoMount = !!pSF->i_isAutoMounted();
10198 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10199 sf.enmSymlinkPolicy = pSF->i_getSymlinkPolicy();
10200
10201 data.llSharedFolders.push_back(sf);
10202 }
10203
10204 // clipboard
10205 data.clipboardMode = mHWData->mClipboardMode;
10206 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10207
10208 // drag'n'drop
10209 data.dndMode = mHWData->mDnDMode;
10210
10211 /* Guest */
10212 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10213
10214 // IO settings
10215 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10216 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10217
10218 /* BandwidthControl (required) */
10219 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10220 if (FAILED(hrc)) throw hrc;
10221
10222 /* Host PCI devices */
10223 data.pciAttachments.clear();
10224 for (HWData::PCIDeviceAssignmentList::const_iterator
10225 it = mHWData->mPCIDeviceAssignments.begin();
10226 it != mHWData->mPCIDeviceAssignments.end();
10227 ++it)
10228 {
10229 ComObjPtr<PCIDeviceAttachment> pda = *it;
10230 settings::HostPCIDeviceAttachment hpda;
10231
10232 hrc = pda->i_saveSettings(hpda);
10233 if (FAILED(hrc)) throw hrc;
10234
10235 data.pciAttachments.push_back(hpda);
10236 }
10237
10238 // guest properties
10239 data.llGuestProperties.clear();
10240#ifdef VBOX_WITH_GUEST_PROPS
10241 for (HWData::GuestPropertyMap::const_iterator
10242 it = mHWData->mGuestProperties.begin();
10243 it != mHWData->mGuestProperties.end();
10244 ++it)
10245 {
10246 HWData::GuestProperty property = it->second;
10247
10248 /* Remove transient guest properties at shutdown unless we
10249 * are saving state. Note that restoring snapshot intentionally
10250 * keeps them, they will be removed if appropriate once the final
10251 * machine state is set (as crashes etc. need to work). */
10252 if ( ( mData->mMachineState == MachineState_PoweredOff
10253 || mData->mMachineState == MachineState_Aborted
10254 || mData->mMachineState == MachineState_Teleported)
10255 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10256 continue;
10257 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10258 prop.strName = it->first;
10259 prop.strValue = property.strValue;
10260 prop.timestamp = (uint64_t)property.mTimestamp;
10261 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10262 GuestPropWriteFlags(property.mFlags, szFlags);
10263 prop.strFlags = szFlags;
10264
10265 data.llGuestProperties.push_back(prop);
10266 }
10267
10268 /* I presume this doesn't require a backup(). */
10269 mData->mGuestPropertiesModified = FALSE;
10270#endif /* VBOX_WITH_GUEST_PROPS defined */
10271
10272 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10273 if (FAILED(hrc)) throw hrc;
10274
10275 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10276 *pAutostart = mHWData->mAutostart;
10277
10278 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10279 }
10280 catch (std::bad_alloc &)
10281 {
10282 return E_OUTOFMEMORY;
10283 }
10284
10285 AssertComRC(hrc);
10286 return hrc;
10287}
10288
10289/**
10290 * Saves the storage controller configuration.
10291 *
10292 * @param data storage settings.
10293 */
10294HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10295{
10296 data.llStorageControllers.clear();
10297
10298 for (StorageControllerList::const_iterator
10299 it = mStorageControllers->begin();
10300 it != mStorageControllers->end();
10301 ++it)
10302 {
10303 ComObjPtr<StorageController> pCtl = *it;
10304
10305 settings::StorageController ctl;
10306 ctl.strName = pCtl->i_getName();
10307 ctl.controllerType = pCtl->i_getControllerType();
10308 ctl.storageBus = pCtl->i_getStorageBus();
10309 ctl.ulInstance = pCtl->i_getInstance();
10310 ctl.fBootable = pCtl->i_getBootable();
10311
10312 /* Save the port count. */
10313 ULONG portCount;
10314 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10315 ComAssertComRCRet(hrc, hrc);
10316 ctl.ulPortCount = portCount;
10317
10318 /* Save fUseHostIOCache */
10319 BOOL fUseHostIOCache;
10320 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10321 ComAssertComRCRet(hrc, hrc);
10322 ctl.fUseHostIOCache = !!fUseHostIOCache;
10323
10324 /* save the devices now. */
10325 hrc = i_saveStorageDevices(pCtl, ctl);
10326 ComAssertComRCRet(hrc, hrc);
10327
10328 data.llStorageControllers.push_back(ctl);
10329 }
10330
10331 return S_OK;
10332}
10333
10334/**
10335 * Saves the hard disk configuration.
10336 */
10337HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10338 settings::StorageController &data)
10339{
10340 MediumAttachmentList atts;
10341
10342 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10343 if (FAILED(hrc)) return hrc;
10344
10345 data.llAttachedDevices.clear();
10346 for (MediumAttachmentList::const_iterator
10347 it = atts.begin();
10348 it != atts.end();
10349 ++it)
10350 {
10351 settings::AttachedDevice dev;
10352 IMediumAttachment *iA = *it;
10353 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10354 Medium *pMedium = pAttach->i_getMedium();
10355
10356 dev.deviceType = pAttach->i_getType();
10357 dev.lPort = pAttach->i_getPort();
10358 dev.lDevice = pAttach->i_getDevice();
10359 dev.fPassThrough = pAttach->i_getPassthrough();
10360 dev.fHotPluggable = pAttach->i_getHotPluggable();
10361 if (pMedium)
10362 {
10363 if (pMedium->i_isHostDrive())
10364 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10365 else
10366 dev.uuid = pMedium->i_getId();
10367 dev.fTempEject = pAttach->i_getTempEject();
10368 dev.fNonRotational = pAttach->i_getNonRotational();
10369 dev.fDiscard = pAttach->i_getDiscard();
10370 }
10371
10372 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10373
10374 data.llAttachedDevices.push_back(dev);
10375 }
10376
10377 return S_OK;
10378}
10379
10380/**
10381 * Saves machine state settings as defined by aFlags
10382 * (SaveSTS_* values).
10383 *
10384 * @param aFlags Combination of SaveSTS_* flags.
10385 *
10386 * @note Locks objects for writing.
10387 */
10388HRESULT Machine::i_saveStateSettings(int aFlags)
10389{
10390 if (aFlags == 0)
10391 return S_OK;
10392
10393 AutoCaller autoCaller(this);
10394 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10395
10396 /* This object's write lock is also necessary to serialize file access
10397 * (prevent concurrent reads and writes) */
10398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10399
10400 HRESULT hrc = S_OK;
10401
10402 Assert(mData->pMachineConfigFile);
10403
10404 try
10405 {
10406 if (aFlags & SaveSTS_CurStateModified)
10407 mData->pMachineConfigFile->fCurrentStateModified = true;
10408
10409 if (aFlags & SaveSTS_StateFilePath)
10410 {
10411 if (!mSSData->strStateFilePath.isEmpty())
10412 /* try to make the file name relative to the settings file dir */
10413 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10414 else
10415 mData->pMachineConfigFile->strStateFile.setNull();
10416 }
10417
10418 if (aFlags & SaveSTS_StateTimeStamp)
10419 {
10420 Assert( mData->mMachineState != MachineState_Aborted
10421 || mSSData->strStateFilePath.isEmpty());
10422
10423 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10424
10425 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10426 || mData->mMachineState == MachineState_AbortedSaved);
10427/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10428 }
10429
10430 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10431 }
10432 catch (...)
10433 {
10434 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10435 }
10436
10437 return hrc;
10438}
10439
10440/**
10441 * Ensures that the given medium is added to a media registry. If this machine
10442 * was created with 4.0 or later, then the machine registry is used. Otherwise
10443 * the global VirtualBox media registry is used.
10444 *
10445 * Caller must NOT hold machine lock, media tree or any medium locks!
10446 *
10447 * @param pMedium
10448 */
10449void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10450{
10451 /* Paranoia checks: do not hold machine or media tree locks. */
10452 AssertReturnVoid(!isWriteLockOnCurrentThread());
10453 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10454
10455 ComObjPtr<Medium> pBase;
10456 {
10457 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10458 pBase = pMedium->i_getBase();
10459 }
10460
10461 /* Paranoia checks: do not hold medium locks. */
10462 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10463 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10464
10465 // decide which medium registry to use now that the medium is attached:
10466 Guid uuid;
10467 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10468 if (fCanHaveOwnMediaRegistry)
10469 // machine XML is VirtualBox 4.0 or higher:
10470 uuid = i_getId(); // machine UUID
10471 else
10472 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10473
10474 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10475 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10476 if (pMedium->i_addRegistry(uuid))
10477 mParent->i_markRegistryModified(uuid);
10478
10479 /* For more complex hard disk structures it can happen that the base
10480 * medium isn't yet associated with any medium registry. Do that now. */
10481 if (pMedium != pBase)
10482 {
10483 /* Tree lock needed by Medium::addRegistryAll. */
10484 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10485 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10486 {
10487 treeLock.release();
10488 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10489 treeLock.acquire();
10490 }
10491 if (pBase->i_addRegistryAll(uuid))
10492 {
10493 treeLock.release();
10494 mParent->i_markRegistryModified(uuid);
10495 }
10496 }
10497}
10498
10499/**
10500 * Physically deletes a file belonging to a machine.
10501 *
10502 * @returns HRESULT
10503 * @retval VBOX_E_FILE_ERROR on failure.
10504 * @param strFile File to delete.
10505 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
10506 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
10507 * @param strWhat File hint which will be used when setting an error. Optional.
10508 * @param prc Where to return IPRT's status code on failure.
10509 * Optional and can be NULL.
10510 */
10511HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
10512 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
10513{
10514 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
10515
10516 HRESULT hrc = S_OK;
10517
10518 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
10519
10520 int vrc = RTFileDelete(strFile.c_str());
10521 if (RT_FAILURE(vrc))
10522 {
10523 if ( !fIgnoreFailures
10524 /* Don't (externally) bitch about stuff which doesn't exist. */
10525 && ( vrc != VERR_FILE_NOT_FOUND
10526 && vrc != VERR_PATH_NOT_FOUND
10527 )
10528 )
10529 {
10530 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
10531
10532 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
10533 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
10534 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
10535 }
10536 }
10537
10538 if (prc)
10539 *prc = vrc;
10540 return hrc;
10541}
10542
10543/**
10544 * Creates differencing hard disks for all normal hard disks attached to this
10545 * machine and a new set of attachments to refer to created disks.
10546 *
10547 * Used when taking a snapshot or when deleting the current state. Gets called
10548 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10549 *
10550 * This method assumes that mMediumAttachments contains the original hard disk
10551 * attachments it needs to create diffs for. On success, these attachments will
10552 * be replaced with the created diffs.
10553 *
10554 * Attachments with non-normal hard disks are left as is.
10555 *
10556 * If @a aOnline is @c false then the original hard disks that require implicit
10557 * diffs will be locked for reading. Otherwise it is assumed that they are
10558 * already locked for writing (when the VM was started). Note that in the latter
10559 * case it is responsibility of the caller to lock the newly created diffs for
10560 * writing if this method succeeds.
10561 *
10562 * @param aProgress Progress object to run (must contain at least as
10563 * many operations left as the number of hard disks
10564 * attached).
10565 * @param aWeight Weight of this operation.
10566 * @param aOnline Whether the VM was online prior to this operation.
10567 *
10568 * @note The progress object is not marked as completed, neither on success nor
10569 * on failure. This is a responsibility of the caller.
10570 *
10571 * @note Locks this object and the media tree for writing.
10572 */
10573HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10574 ULONG aWeight,
10575 bool aOnline)
10576{
10577 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10578
10579 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10580 AssertReturn(!!pProgressControl, E_INVALIDARG);
10581
10582 AutoCaller autoCaller(this);
10583 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10584
10585 /* We can't use AutoMultiWriteLock2 here as some error code paths acquire
10586 * the media tree lock which means we need to be able to drop the media
10587 * tree lock individually in those cases. */
10588 AutoWriteLock aMachineLock(this->lockHandle() COMMA_LOCKVAL_SRC_POS);
10589 AutoWriteLock aTreeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10590
10591 /* must be in a protective state because we release the lock below */
10592 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10593 || mData->mMachineState == MachineState_OnlineSnapshotting
10594 || mData->mMachineState == MachineState_LiveSnapshotting
10595 || mData->mMachineState == MachineState_RestoringSnapshot
10596 || mData->mMachineState == MachineState_DeletingSnapshot
10597 , E_FAIL);
10598
10599 HRESULT hrc = S_OK;
10600
10601 // use appropriate locked media map (online or offline)
10602 MediumLockListMap lockedMediaOffline;
10603 MediumLockListMap *lockedMediaMap;
10604 if (aOnline)
10605 lockedMediaMap = &mData->mSession.mLockedMedia;
10606 else
10607 lockedMediaMap = &lockedMediaOffline;
10608
10609 try
10610 {
10611 if (!aOnline)
10612 {
10613 /* lock all attached hard disks early to detect "in use"
10614 * situations before creating actual diffs */
10615 for (MediumAttachmentList::const_iterator
10616 it = mMediumAttachments->begin();
10617 it != mMediumAttachments->end();
10618 ++it)
10619 {
10620 MediumAttachment *pAtt = *it;
10621 if (pAtt->i_getType() == DeviceType_HardDisk)
10622 {
10623 Medium *pMedium = pAtt->i_getMedium();
10624 Assert(pMedium);
10625
10626 MediumLockList *pMediumLockList(new MediumLockList());
10627 aTreeLock.release();
10628 aMachineLock.release();
10629 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10630 NULL /* pToLockWrite */,
10631 false /* fMediumLockWriteAll */,
10632 NULL,
10633 *pMediumLockList);
10634 aMachineLock.acquire();
10635 aTreeLock.acquire();
10636 if (FAILED(hrc))
10637 {
10638 delete pMediumLockList;
10639 throw hrc;
10640 }
10641 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10642 if (FAILED(hrc))
10643 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
10644 }
10645 }
10646
10647 /* Now lock all media. If this fails, nothing is locked. */
10648 aTreeLock.release();
10649 aMachineLock.release();
10650 hrc = lockedMediaMap->Lock();
10651 aMachineLock.acquire();
10652 aTreeLock.acquire();
10653 if (FAILED(hrc))
10654 throw setError(hrc, tr("Locking of attached media failed"));
10655 }
10656
10657 /* remember the current list (note that we don't use backup() since
10658 * mMediumAttachments may be already backed up) */
10659 MediumAttachmentList atts = *mMediumAttachments.data();
10660
10661 /* start from scratch */
10662 mMediumAttachments->clear();
10663
10664 /* go through remembered attachments and create diffs for normal hard
10665 * disks and attach them */
10666 for (MediumAttachmentList::const_iterator
10667 it = atts.begin();
10668 it != atts.end();
10669 ++it)
10670 {
10671 MediumAttachment *pAtt = *it;
10672
10673 DeviceType_T devType = pAtt->i_getType();
10674 Medium *pMedium = pAtt->i_getMedium();
10675
10676 if ( devType != DeviceType_HardDisk
10677 || pMedium == NULL
10678 || pMedium->i_getType() != MediumType_Normal)
10679 {
10680 /* copy the attachment as is */
10681
10682 /** @todo the progress object created in SessionMachine::TakeSnaphot
10683 * only expects operations for hard disks. Later other
10684 * device types need to show up in the progress as well. */
10685 if (devType == DeviceType_HardDisk)
10686 {
10687 if (pMedium == NULL)
10688 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10689 aWeight); // weight
10690 else
10691 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10692 pMedium->i_getBase()->i_getName().c_str()).raw(),
10693 aWeight); // weight
10694 }
10695
10696 mMediumAttachments->push_back(pAtt);
10697 continue;
10698 }
10699
10700 /* need a diff */
10701 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10702 pMedium->i_getBase()->i_getName().c_str()).raw(),
10703 aWeight); // weight
10704
10705 Utf8Str strFullSnapshotFolder;
10706 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10707
10708 ComObjPtr<Medium> diff;
10709 diff.createObject();
10710 // store the diff in the same registry as the parent
10711 // (this cannot fail here because we can't create implicit diffs for
10712 // unregistered images)
10713 Guid uuidRegistryParent;
10714 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10715 Assert(fInRegistry); NOREF(fInRegistry);
10716 hrc = diff->init(mParent,
10717 pMedium->i_getPreferredDiffFormat(),
10718 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10719 uuidRegistryParent,
10720 DeviceType_HardDisk);
10721 if (FAILED(hrc))
10722 {
10723 /* Throwing an exception here causes the 'diff' object to go out of scope
10724 * which triggers its destructor (ComObjPtr<Medium>::~ComObjPtr) which will
10725 * ultimately call Medium::uninit() which acquires the media tree lock
10726 * (VirtualBox::i_getMediaTreeLockHandle()) so drop the media tree lock here. */
10727 aTreeLock.release();
10728 throw hrc;
10729 }
10730
10731 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10732 * the push_back? Looks like we're going to release medium with the
10733 * wrong kind of lock (general issue with if we fail anywhere at all)
10734 * and an orphaned VDI in the snapshots folder. */
10735
10736 /* update the appropriate lock list */
10737 MediumLockList *pMediumLockList;
10738 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
10739 AssertComRCThrowRC(hrc);
10740 if (aOnline)
10741 {
10742 aTreeLock.release();
10743 aMachineLock.release();
10744 /* The currently attached medium will be read-only, change
10745 * the lock type to read. */
10746 hrc = pMediumLockList->Update(pMedium, false);
10747 aMachineLock.acquire();
10748 aTreeLock.acquire();
10749 AssertComRCThrowRC(hrc);
10750 }
10751
10752 /* release the locks before the potentially lengthy operation */
10753 aTreeLock.release();
10754 aMachineLock.release();
10755 hrc = pMedium->i_createDiffStorage(diff,
10756 pMedium->i_getPreferredDiffVariant(),
10757 pMediumLockList,
10758 NULL /* aProgress */,
10759 true /* aWait */,
10760 false /* aNotify */);
10761 aMachineLock.acquire();
10762 aTreeLock.acquire();
10763 if (FAILED(hrc))
10764 {
10765 /* As above, 'diff' will go out of scope via the 'throw' here resulting in
10766 * Medium::uninit() being called which acquires the media tree lock. */
10767 aTreeLock.release();
10768 throw hrc;
10769 }
10770
10771 /* actual lock list update is done in Machine::i_commitMedia */
10772
10773 hrc = diff->i_addBackReference(mData->mUuid);
10774 AssertComRCThrowRC(hrc);
10775
10776 /* add a new attachment */
10777 ComObjPtr<MediumAttachment> attachment;
10778 attachment.createObject();
10779 hrc = attachment->init(this,
10780 diff,
10781 pAtt->i_getControllerName(),
10782 pAtt->i_getPort(),
10783 pAtt->i_getDevice(),
10784 DeviceType_HardDisk,
10785 true /* aImplicit */,
10786 false /* aPassthrough */,
10787 false /* aTempEject */,
10788 pAtt->i_getNonRotational(),
10789 pAtt->i_getDiscard(),
10790 pAtt->i_getHotPluggable(),
10791 pAtt->i_getBandwidthGroup());
10792 if (FAILED(hrc)) {
10793 /* As above, 'diff' will go out of scope via the 'throw' here resulting in
10794 * Medium::uninit() being called which acquires the media tree lock. */
10795 aTreeLock.release();
10796 throw hrc;
10797 }
10798
10799 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10800 AssertComRCThrowRC(hrc);
10801 mMediumAttachments->push_back(attachment);
10802 }
10803 }
10804 catch (HRESULT hrcXcpt)
10805 {
10806 hrc = hrcXcpt;
10807 }
10808
10809 /* unlock all hard disks we locked when there is no VM */
10810 if (!aOnline)
10811 {
10812 ErrorInfoKeeper eik;
10813
10814 HRESULT hrc2 = lockedMediaMap->Clear();
10815 AssertComRC(hrc2);
10816 }
10817
10818 return hrc;
10819}
10820
10821/**
10822 * Deletes implicit differencing hard disks created either by
10823 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10824 * mMediumAttachments.
10825 *
10826 * Note that to delete hard disks created by #attachDevice() this method is
10827 * called from #i_rollbackMedia() when the changes are rolled back.
10828 *
10829 * @note Locks this object and the media tree for writing.
10830 */
10831HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10832{
10833 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10834
10835 AutoCaller autoCaller(this);
10836 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10837
10838 AutoMultiWriteLock2 alock(this->lockHandle(),
10839 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10840
10841 /* We absolutely must have backed up state. */
10842 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10843
10844 /* Check if there are any implicitly created diff images. */
10845 bool fImplicitDiffs = false;
10846 for (MediumAttachmentList::const_iterator
10847 it = mMediumAttachments->begin();
10848 it != mMediumAttachments->end();
10849 ++it)
10850 {
10851 const ComObjPtr<MediumAttachment> &pAtt = *it;
10852 if (pAtt->i_isImplicit())
10853 {
10854 fImplicitDiffs = true;
10855 break;
10856 }
10857 }
10858 /* If there is nothing to do, leave early. This saves lots of image locking
10859 * effort. It also avoids a MachineStateChanged event without real reason.
10860 * This is important e.g. when loading a VM config, because there should be
10861 * no events. Otherwise API clients can become thoroughly confused for
10862 * inaccessible VMs (the code for loading VM configs uses this method for
10863 * cleanup if the config makes no sense), as they take such events as an
10864 * indication that the VM is alive, and they would force the VM config to
10865 * be reread, leading to an endless loop. */
10866 if (!fImplicitDiffs)
10867 return S_OK;
10868
10869 HRESULT hrc = S_OK;
10870 MachineState_T oldState = mData->mMachineState;
10871
10872 /* will release the lock before the potentially lengthy operation,
10873 * so protect with the special state (unless already protected) */
10874 if ( oldState != MachineState_Snapshotting
10875 && oldState != MachineState_OnlineSnapshotting
10876 && oldState != MachineState_LiveSnapshotting
10877 && oldState != MachineState_RestoringSnapshot
10878 && oldState != MachineState_DeletingSnapshot
10879 && oldState != MachineState_DeletingSnapshotOnline
10880 && oldState != MachineState_DeletingSnapshotPaused
10881 )
10882 i_setMachineState(MachineState_SettingUp);
10883
10884 // use appropriate locked media map (online or offline)
10885 MediumLockListMap lockedMediaOffline;
10886 MediumLockListMap *lockedMediaMap;
10887 if (aOnline)
10888 lockedMediaMap = &mData->mSession.mLockedMedia;
10889 else
10890 lockedMediaMap = &lockedMediaOffline;
10891
10892 try
10893 {
10894 if (!aOnline)
10895 {
10896 /* lock all attached hard disks early to detect "in use"
10897 * situations before deleting actual diffs */
10898 for (MediumAttachmentList::const_iterator
10899 it = mMediumAttachments->begin();
10900 it != mMediumAttachments->end();
10901 ++it)
10902 {
10903 MediumAttachment *pAtt = *it;
10904 if (pAtt->i_getType() == DeviceType_HardDisk)
10905 {
10906 Medium *pMedium = pAtt->i_getMedium();
10907 Assert(pMedium);
10908
10909 MediumLockList *pMediumLockList(new MediumLockList());
10910 alock.release();
10911 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10912 NULL /* pToLockWrite */,
10913 false /* fMediumLockWriteAll */,
10914 NULL,
10915 *pMediumLockList);
10916 alock.acquire();
10917
10918 if (FAILED(hrc))
10919 {
10920 delete pMediumLockList;
10921 throw hrc;
10922 }
10923
10924 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10925 if (FAILED(hrc))
10926 throw hrc;
10927 }
10928 }
10929
10930 if (FAILED(hrc))
10931 throw hrc;
10932 } // end of offline
10933
10934 /* Lock lists are now up to date and include implicitly created media */
10935
10936 /* Go through remembered attachments and delete all implicitly created
10937 * diffs and fix up the attachment information */
10938 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10939 MediumAttachmentList implicitAtts;
10940 for (MediumAttachmentList::const_iterator
10941 it = mMediumAttachments->begin();
10942 it != mMediumAttachments->end();
10943 ++it)
10944 {
10945 ComObjPtr<MediumAttachment> pAtt = *it;
10946 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10947 if (pMedium.isNull())
10948 continue;
10949
10950 // Implicit attachments go on the list for deletion and back references are removed.
10951 if (pAtt->i_isImplicit())
10952 {
10953 /* Deassociate and mark for deletion */
10954 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10955 hrc = pMedium->i_removeBackReference(mData->mUuid);
10956 if (FAILED(hrc))
10957 throw hrc;
10958 implicitAtts.push_back(pAtt);
10959 continue;
10960 }
10961
10962 /* Was this medium attached before? */
10963 if (!i_findAttachment(oldAtts, pMedium))
10964 {
10965 /* no: de-associate */
10966 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10967 hrc = pMedium->i_removeBackReference(mData->mUuid);
10968 if (FAILED(hrc))
10969 throw hrc;
10970 continue;
10971 }
10972 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10973 }
10974
10975 /* If there are implicit attachments to delete, throw away the lock
10976 * map contents (which will unlock all media) since the medium
10977 * attachments will be rolled back. Below we need to completely
10978 * recreate the lock map anyway since it is infinitely complex to
10979 * do this incrementally (would need reconstructing each attachment
10980 * change, which would be extremely hairy). */
10981 if (implicitAtts.size() != 0)
10982 {
10983 ErrorInfoKeeper eik;
10984
10985 HRESULT hrc2 = lockedMediaMap->Clear();
10986 AssertComRC(hrc2);
10987 }
10988
10989 /* rollback hard disk changes */
10990 mMediumAttachments.rollback();
10991
10992 MultiResult mrc(S_OK);
10993
10994 // Delete unused implicit diffs.
10995 if (implicitAtts.size() != 0)
10996 {
10997 alock.release();
10998
10999 for (MediumAttachmentList::const_iterator
11000 it = implicitAtts.begin();
11001 it != implicitAtts.end();
11002 ++it)
11003 {
11004 // Remove medium associated with this attachment.
11005 ComObjPtr<MediumAttachment> pAtt = *it;
11006 Assert(pAtt);
11007 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11008 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11009 Assert(pMedium);
11010
11011 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11012 // continue on delete failure, just collect error messages
11013 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
11014 pMedium->i_getLocationFull().c_str() ));
11015 mrc = hrc;
11016 }
11017 // Clear the list of deleted implicit attachments now, while not
11018 // holding the lock, as it will ultimately trigger Medium::uninit()
11019 // calls which assume that the media tree lock isn't held.
11020 implicitAtts.clear();
11021
11022 alock.acquire();
11023
11024 /* if there is a VM recreate media lock map as mentioned above,
11025 * otherwise it is a waste of time and we leave things unlocked */
11026 if (aOnline)
11027 {
11028 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11029 /* must never be NULL, but better safe than sorry */
11030 if (!pMachine.isNull())
11031 {
11032 alock.release();
11033 hrc = mData->mSession.mMachine->i_lockMedia();
11034 alock.acquire();
11035 if (FAILED(hrc))
11036 throw hrc;
11037 }
11038 }
11039 }
11040 }
11041 catch (HRESULT hrcXcpt)
11042 {
11043 hrc = hrcXcpt;
11044 }
11045
11046 if (mData->mMachineState == MachineState_SettingUp)
11047 i_setMachineState(oldState);
11048
11049 /* unlock all hard disks we locked when there is no VM */
11050 if (!aOnline)
11051 {
11052 ErrorInfoKeeper eik;
11053
11054 HRESULT hrc2 = lockedMediaMap->Clear();
11055 AssertComRC(hrc2);
11056 }
11057
11058 return hrc;
11059}
11060
11061
11062/**
11063 * Looks through the given list of media attachments for one with the given parameters
11064 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11065 * can be searched as well if needed.
11066 *
11067 * @param ll
11068 * @param aControllerName
11069 * @param aControllerPort
11070 * @param aDevice
11071 * @return
11072 */
11073MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11074 const Utf8Str &aControllerName,
11075 LONG aControllerPort,
11076 LONG aDevice)
11077{
11078 for (MediumAttachmentList::const_iterator
11079 it = ll.begin();
11080 it != ll.end();
11081 ++it)
11082 {
11083 MediumAttachment *pAttach = *it;
11084 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11085 return pAttach;
11086 }
11087
11088 return NULL;
11089}
11090
11091/**
11092 * Looks through the given list of media attachments for one with the given parameters
11093 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11094 * can be searched as well if needed.
11095 *
11096 * @param ll
11097 * @param pMedium
11098 * @return
11099 */
11100MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11101 ComObjPtr<Medium> pMedium)
11102{
11103 for (MediumAttachmentList::const_iterator
11104 it = ll.begin();
11105 it != ll.end();
11106 ++it)
11107 {
11108 MediumAttachment *pAttach = *it;
11109 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11110 if (pMediumThis == pMedium)
11111 return pAttach;
11112 }
11113
11114 return NULL;
11115}
11116
11117/**
11118 * Looks through the given list of media attachments for one with the given parameters
11119 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11120 * can be searched as well if needed.
11121 *
11122 * @param ll
11123 * @param id
11124 * @return
11125 */
11126MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11127 Guid &id)
11128{
11129 for (MediumAttachmentList::const_iterator
11130 it = ll.begin();
11131 it != ll.end();
11132 ++it)
11133 {
11134 MediumAttachment *pAttach = *it;
11135 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11136 if (pMediumThis->i_getId() == id)
11137 return pAttach;
11138 }
11139
11140 return NULL;
11141}
11142
11143/**
11144 * Main implementation for Machine::DetachDevice. This also gets called
11145 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11146 *
11147 * @param pAttach Medium attachment to detach.
11148 * @param writeLock Machine write lock which the caller must have locked once.
11149 * This may be released temporarily in here.
11150 * @param pSnapshot If NULL, then the detachment is for the current machine.
11151 * Otherwise this is for a SnapshotMachine, and this must be
11152 * its snapshot.
11153 * @return
11154 */
11155HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11156 AutoWriteLock &writeLock,
11157 Snapshot *pSnapshot)
11158{
11159 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11160 DeviceType_T mediumType = pAttach->i_getType();
11161
11162 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11163
11164 if (pAttach->i_isImplicit())
11165 {
11166 /* attempt to implicitly delete the implicitly created diff */
11167
11168 /// @todo move the implicit flag from MediumAttachment to Medium
11169 /// and forbid any hard disk operation when it is implicit. Or maybe
11170 /// a special media state for it to make it even more simple.
11171
11172 Assert(mMediumAttachments.isBackedUp());
11173
11174 /* will release the lock before the potentially lengthy operation, so
11175 * protect with the special state */
11176 MachineState_T oldState = mData->mMachineState;
11177 i_setMachineState(MachineState_SettingUp);
11178
11179 writeLock.release();
11180
11181 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11182
11183 writeLock.acquire();
11184
11185 i_setMachineState(oldState);
11186
11187 if (FAILED(hrc)) return hrc;
11188 }
11189
11190 i_setModified(IsModified_Storage);
11191 mMediumAttachments.backup();
11192 mMediumAttachments->remove(pAttach);
11193
11194 if (!oldmedium.isNull())
11195 {
11196 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11197 if (pSnapshot)
11198 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11199 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11200 else if (mediumType != DeviceType_HardDisk)
11201 oldmedium->i_removeBackReference(mData->mUuid);
11202 }
11203
11204 return S_OK;
11205}
11206
11207/**
11208 * Goes thru all media of the given list and
11209 *
11210 * 1) calls i_detachDevice() on each of them for this machine and
11211 * 2) adds all Medium objects found in the process to the given list,
11212 * depending on cleanupMode.
11213 *
11214 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11215 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11216 * media to the list.
11217 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11218 * also removable media if they are located in the VM folder and referenced
11219 * only by this VM (media prepared by unattended installer).
11220 *
11221 * This gets called from Machine::Unregister, both for the actual Machine and
11222 * the SnapshotMachine objects that might be found in the snapshots.
11223 *
11224 * Requires caller and locking. The machine lock must be passed in because it
11225 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11226 *
11227 * @param writeLock Machine lock from top-level caller; this gets passed to
11228 * i_detachDevice.
11229 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11230 * object if called for a SnapshotMachine.
11231 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11232 * added to llMedia; if Full, then all media get added;
11233 * otherwise no media get added.
11234 * @param llMedia Caller's list to receive Medium objects which got detached so
11235 * caller can close() them, depending on cleanupMode.
11236 * @return
11237 */
11238HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11239 Snapshot *pSnapshot,
11240 CleanupMode_T cleanupMode,
11241 MediaList &llMedia)
11242{
11243 Assert(isWriteLockOnCurrentThread());
11244
11245 HRESULT hrc;
11246
11247 // make a temporary list because i_detachDevice invalidates iterators into
11248 // mMediumAttachments
11249 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11250
11251 for (MediumAttachmentList::iterator
11252 it = llAttachments2.begin();
11253 it != llAttachments2.end();
11254 ++it)
11255 {
11256 ComObjPtr<MediumAttachment> &pAttach = *it;
11257 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11258
11259 if (!pMedium.isNull())
11260 {
11261 AutoCaller mac(pMedium);
11262 if (FAILED(mac.hrc())) return mac.hrc();
11263 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11264 DeviceType_T devType = pMedium->i_getDeviceType();
11265 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11266 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11267 strMediumLocation.stripFilename();
11268 Utf8Str strMachineFolder = i_getSettingsFileFull();
11269 strMachineFolder.stripFilename();
11270 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11271 && devType == DeviceType_HardDisk)
11272 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11273 && ( devType == DeviceType_HardDisk
11274 || ( cBackRefs <= 1
11275 && strMediumLocation == strMachineFolder
11276 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11277 || (cleanupMode == CleanupMode_Full)
11278 )
11279 {
11280 llMedia.push_back(pMedium);
11281 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11282 /* Not allowed to keep this lock as below we need the parent
11283 * medium lock, and the lock order is parent to child. */
11284 lock.release();
11285 /*
11286 * Search for media which are not attached to any machine, but
11287 * in the chain to an attached disk. Media are only consided
11288 * if they are:
11289 * - have only one child
11290 * - no references to any machines
11291 * - are of normal medium type
11292 */
11293 while (!pParent.isNull())
11294 {
11295 AutoCaller mac1(pParent);
11296 if (FAILED(mac1.hrc())) return mac1.hrc();
11297 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11298 if (pParent->i_getChildren().size() == 1)
11299 {
11300 if ( pParent->i_getMachineBackRefCount() == 0
11301 && pParent->i_getType() == MediumType_Normal
11302 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11303 llMedia.push_back(pParent);
11304 }
11305 else
11306 break;
11307 pParent = pParent->i_getParent();
11308 }
11309 }
11310 }
11311
11312 // real machine: then we need to use the proper method
11313 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11314
11315 if (FAILED(hrc))
11316 return hrc;
11317 }
11318
11319 return S_OK;
11320}
11321
11322/**
11323 * Perform deferred hard disk detachments.
11324 *
11325 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11326 * changed (not backed up).
11327 *
11328 * If @a aOnline is @c true then this method will also unlock the old hard
11329 * disks for which the new implicit diffs were created and will lock these new
11330 * diffs for writing.
11331 *
11332 * @param aOnline Whether the VM was online prior to this operation.
11333 *
11334 * @note Locks this object for writing!
11335 */
11336void Machine::i_commitMedia(bool aOnline /*= false*/)
11337{
11338 AutoCaller autoCaller(this);
11339 AssertComRCReturnVoid(autoCaller.hrc());
11340
11341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11342
11343 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11344
11345 HRESULT hrc = S_OK;
11346
11347 /* no attach/detach operations -- nothing to do */
11348 if (!mMediumAttachments.isBackedUp())
11349 return;
11350
11351 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11352 bool fMediaNeedsLocking = false;
11353
11354 /* enumerate new attachments */
11355 for (MediumAttachmentList::const_iterator
11356 it = mMediumAttachments->begin();
11357 it != mMediumAttachments->end();
11358 ++it)
11359 {
11360 MediumAttachment *pAttach = *it;
11361
11362 pAttach->i_commit();
11363
11364 Medium *pMedium = pAttach->i_getMedium();
11365 bool fImplicit = pAttach->i_isImplicit();
11366
11367 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11368 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11369 fImplicit));
11370
11371 /** @todo convert all this Machine-based voodoo to MediumAttachment
11372 * based commit logic. */
11373 if (fImplicit)
11374 {
11375 /* convert implicit attachment to normal */
11376 pAttach->i_setImplicit(false);
11377
11378 if ( aOnline
11379 && pMedium
11380 && pAttach->i_getType() == DeviceType_HardDisk
11381 )
11382 {
11383 /* update the appropriate lock list */
11384 MediumLockList *pMediumLockList;
11385 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11386 AssertComRC(hrc);
11387 if (pMediumLockList)
11388 {
11389 /* unlock if there's a need to change the locking */
11390 if (!fMediaNeedsLocking)
11391 {
11392 Assert(mData->mSession.mLockedMedia.IsLocked());
11393 hrc = mData->mSession.mLockedMedia.Unlock();
11394 AssertComRC(hrc);
11395 fMediaNeedsLocking = true;
11396 }
11397 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
11398 AssertComRC(hrc);
11399 hrc = pMediumLockList->Append(pMedium, true);
11400 AssertComRC(hrc);
11401 }
11402 }
11403
11404 continue;
11405 }
11406
11407 if (pMedium)
11408 {
11409 /* was this medium attached before? */
11410 for (MediumAttachmentList::iterator
11411 oldIt = oldAtts.begin();
11412 oldIt != oldAtts.end();
11413 ++oldIt)
11414 {
11415 MediumAttachment *pOldAttach = *oldIt;
11416 if (pOldAttach->i_getMedium() == pMedium)
11417 {
11418 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11419
11420 /* yes: remove from old to avoid de-association */
11421 oldAtts.erase(oldIt);
11422 break;
11423 }
11424 }
11425 }
11426 }
11427
11428 /* enumerate remaining old attachments and de-associate from the
11429 * current machine state */
11430 for (MediumAttachmentList::const_iterator
11431 it = oldAtts.begin();
11432 it != oldAtts.end();
11433 ++it)
11434 {
11435 MediumAttachment *pAttach = *it;
11436 Medium *pMedium = pAttach->i_getMedium();
11437
11438 /* Detach only hard disks, since DVD/floppy media is detached
11439 * instantly in MountMedium. */
11440 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11441 {
11442 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11443
11444 /* now de-associate from the current machine state */
11445 hrc = pMedium->i_removeBackReference(mData->mUuid);
11446 AssertComRC(hrc);
11447
11448 if (aOnline)
11449 {
11450 /* unlock since medium is not used anymore */
11451 MediumLockList *pMediumLockList;
11452 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11453 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
11454 {
11455 /* this happens for online snapshots, there the attachment
11456 * is changing, but only to a diff image created under
11457 * the old one, so there is no separate lock list */
11458 Assert(!pMediumLockList);
11459 }
11460 else
11461 {
11462 AssertComRC(hrc);
11463 if (pMediumLockList)
11464 {
11465 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
11466 AssertComRC(hrc);
11467 }
11468 }
11469 }
11470 }
11471 }
11472
11473 /* take media locks again so that the locking state is consistent */
11474 if (fMediaNeedsLocking)
11475 {
11476 Assert(aOnline);
11477 hrc = mData->mSession.mLockedMedia.Lock();
11478 AssertComRC(hrc);
11479 }
11480
11481 /* commit the hard disk changes */
11482 mMediumAttachments.commit();
11483
11484 if (i_isSessionMachine())
11485 {
11486 /*
11487 * Update the parent machine to point to the new owner.
11488 * This is necessary because the stored parent will point to the
11489 * session machine otherwise and cause crashes or errors later
11490 * when the session machine gets invalid.
11491 */
11492 /** @todo Change the MediumAttachment class to behave like any other
11493 * class in this regard by creating peer MediumAttachment
11494 * objects for session machines and share the data with the peer
11495 * machine.
11496 */
11497 for (MediumAttachmentList::const_iterator
11498 it = mMediumAttachments->begin();
11499 it != mMediumAttachments->end();
11500 ++it)
11501 (*it)->i_updateParentMachine(mPeer);
11502
11503 /* attach new data to the primary machine and reshare it */
11504 mPeer->mMediumAttachments.attach(mMediumAttachments);
11505 }
11506
11507 return;
11508}
11509
11510/**
11511 * Perform deferred deletion of implicitly created diffs.
11512 *
11513 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11514 * changed (not backed up).
11515 *
11516 * @note Locks this object for writing!
11517 */
11518void Machine::i_rollbackMedia()
11519{
11520 AutoCaller autoCaller(this);
11521 AssertComRCReturnVoid(autoCaller.hrc());
11522
11523 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11524 LogFlowThisFunc(("Entering rollbackMedia\n"));
11525
11526 HRESULT hrc = S_OK;
11527
11528 /* no attach/detach operations -- nothing to do */
11529 if (!mMediumAttachments.isBackedUp())
11530 return;
11531
11532 /* enumerate new attachments */
11533 for (MediumAttachmentList::const_iterator
11534 it = mMediumAttachments->begin();
11535 it != mMediumAttachments->end();
11536 ++it)
11537 {
11538 MediumAttachment *pAttach = *it;
11539 /* Fix up the backrefs for DVD/floppy media. */
11540 if (pAttach->i_getType() != DeviceType_HardDisk)
11541 {
11542 Medium *pMedium = pAttach->i_getMedium();
11543 if (pMedium)
11544 {
11545 hrc = pMedium->i_removeBackReference(mData->mUuid);
11546 AssertComRC(hrc);
11547 }
11548 }
11549
11550 (*it)->i_rollback();
11551
11552 pAttach = *it;
11553 /* Fix up the backrefs for DVD/floppy media. */
11554 if (pAttach->i_getType() != DeviceType_HardDisk)
11555 {
11556 Medium *pMedium = pAttach->i_getMedium();
11557 if (pMedium)
11558 {
11559 hrc = pMedium->i_addBackReference(mData->mUuid);
11560 AssertComRC(hrc);
11561 }
11562 }
11563 }
11564
11565 /** @todo convert all this Machine-based voodoo to MediumAttachment
11566 * based rollback logic. */
11567 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11568
11569 return;
11570}
11571
11572/**
11573 * Returns true if the settings file is located in the directory named exactly
11574 * as the machine; this means, among other things, that the machine directory
11575 * should be auto-renamed.
11576 *
11577 * @param aSettingsDir if not NULL, the full machine settings file directory
11578 * name will be assigned there.
11579 *
11580 * @note Doesn't lock anything.
11581 * @note Not thread safe (must be called from this object's lock).
11582 */
11583bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11584{
11585 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11586 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11587 if (aSettingsDir)
11588 *aSettingsDir = strMachineDirName;
11589 strMachineDirName.stripPath(); // vmname
11590 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11591 strConfigFileOnly.stripPath() // vmname.vbox
11592 .stripSuffix(); // vmname
11593 /** @todo hack, make somehow use of ComposeMachineFilename */
11594 if (mUserData->s.fDirectoryIncludesUUID)
11595 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11596
11597 AssertReturn(!strMachineDirName.isEmpty(), false);
11598 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11599
11600 return strMachineDirName == strConfigFileOnly;
11601}
11602
11603/**
11604 * Discards all changes to machine settings.
11605 *
11606 * @param aNotify Whether to notify the direct session about changes or not.
11607 *
11608 * @note Locks objects for writing!
11609 */
11610void Machine::i_rollback(bool aNotify)
11611{
11612 AutoCaller autoCaller(this);
11613 AssertComRCReturn(autoCaller.hrc(), (void)0);
11614
11615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11616
11617 if (!mStorageControllers.isNull())
11618 {
11619 if (mStorageControllers.isBackedUp())
11620 {
11621 /* unitialize all new devices (absent in the backed up list). */
11622 StorageControllerList *backedList = mStorageControllers.backedUpData();
11623 for (StorageControllerList::const_iterator
11624 it = mStorageControllers->begin();
11625 it != mStorageControllers->end();
11626 ++it)
11627 {
11628 if ( std::find(backedList->begin(), backedList->end(), *it)
11629 == backedList->end()
11630 )
11631 {
11632 (*it)->uninit();
11633 }
11634 }
11635
11636 /* restore the list */
11637 mStorageControllers.rollback();
11638 }
11639
11640 /* rollback any changes to devices after restoring the list */
11641 if (mData->flModifications & IsModified_Storage)
11642 {
11643 for (StorageControllerList::const_iterator
11644 it = mStorageControllers->begin();
11645 it != mStorageControllers->end();
11646 ++it)
11647 {
11648 (*it)->i_rollback();
11649 }
11650 }
11651 }
11652
11653 if (!mUSBControllers.isNull())
11654 {
11655 if (mUSBControllers.isBackedUp())
11656 {
11657 /* unitialize all new devices (absent in the backed up list). */
11658 USBControllerList *backedList = mUSBControllers.backedUpData();
11659 for (USBControllerList::const_iterator
11660 it = mUSBControllers->begin();
11661 it != mUSBControllers->end();
11662 ++it)
11663 {
11664 if ( std::find(backedList->begin(), backedList->end(), *it)
11665 == backedList->end()
11666 )
11667 {
11668 (*it)->uninit();
11669 }
11670 }
11671
11672 /* restore the list */
11673 mUSBControllers.rollback();
11674 }
11675
11676 /* rollback any changes to devices after restoring the list */
11677 if (mData->flModifications & IsModified_USB)
11678 {
11679 for (USBControllerList::const_iterator
11680 it = mUSBControllers->begin();
11681 it != mUSBControllers->end();
11682 ++it)
11683 {
11684 (*it)->i_rollback();
11685 }
11686 }
11687 }
11688
11689 mUserData.rollback();
11690
11691 mHWData.rollback();
11692
11693 if (mData->flModifications & IsModified_Storage)
11694 i_rollbackMedia();
11695
11696 if (mPlatform)
11697 {
11698 mPlatform->i_rollback();
11699 i_platformPropertiesUpdate();
11700 }
11701
11702 if (mFirmwareSettings)
11703 mFirmwareSettings->i_rollback();
11704
11705 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11706 mRecordingSettings->i_rollback();
11707
11708 if (mTrustedPlatformModule)
11709 mTrustedPlatformModule->i_rollback();
11710
11711 if (mNvramStore)
11712 mNvramStore->i_rollback();
11713
11714 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11715 mGraphicsAdapter->i_rollback();
11716
11717 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11718 mVRDEServer->i_rollback();
11719
11720 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
11721 mAudioSettings->i_rollback();
11722
11723 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11724 mUSBDeviceFilters->i_rollback();
11725
11726 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11727 mBandwidthControl->i_rollback();
11728
11729 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
11730 mGuestDebugControl->i_rollback();
11731
11732 if (mPlatform && (mData->flModifications & IsModified_Platform))
11733 {
11734 ChipsetType_T enmChipset;
11735 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11736 ComAssertComRC(hrc);
11737
11738 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipset));
11739 }
11740
11741 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11742 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11743 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11744
11745 if (mData->flModifications & IsModified_NetworkAdapters)
11746 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11747 if ( mNetworkAdapters[slot]
11748 && mNetworkAdapters[slot]->i_isModified())
11749 {
11750 mNetworkAdapters[slot]->i_rollback();
11751 networkAdapters[slot] = mNetworkAdapters[slot];
11752 }
11753
11754 if (mData->flModifications & IsModified_SerialPorts)
11755 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11756 if ( mSerialPorts[slot]
11757 && mSerialPorts[slot]->i_isModified())
11758 {
11759 mSerialPorts[slot]->i_rollback();
11760 serialPorts[slot] = mSerialPorts[slot];
11761 }
11762
11763 if (mData->flModifications & IsModified_ParallelPorts)
11764 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11765 if ( mParallelPorts[slot]
11766 && mParallelPorts[slot]->i_isModified())
11767 {
11768 mParallelPorts[slot]->i_rollback();
11769 parallelPorts[slot] = mParallelPorts[slot];
11770 }
11771
11772 if (aNotify)
11773 {
11774 /* inform the direct session about changes */
11775
11776 ComObjPtr<Machine> that = this;
11777 uint32_t flModifications = mData->flModifications;
11778 alock.release();
11779
11780 if (flModifications & IsModified_SharedFolders)
11781 that->i_onSharedFolderChange();
11782
11783 if (flModifications & IsModified_VRDEServer)
11784 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11785 if (flModifications & IsModified_USB)
11786 that->i_onUSBControllerChange();
11787
11788 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11789 if (networkAdapters[slot])
11790 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11791 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11792 if (serialPorts[slot])
11793 that->i_onSerialPortChange(serialPorts[slot]);
11794 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11795 if (parallelPorts[slot])
11796 that->i_onParallelPortChange(parallelPorts[slot]);
11797
11798 if (flModifications & IsModified_Storage)
11799 {
11800 for (StorageControllerList::const_iterator
11801 it = mStorageControllers->begin();
11802 it != mStorageControllers->end();
11803 ++it)
11804 {
11805 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11806 }
11807 }
11808
11809 if (flModifications & IsModified_GuestDebugControl)
11810 that->i_onGuestDebugControlChange(mGuestDebugControl);
11811
11812#if 0
11813 if (flModifications & IsModified_BandwidthControl)
11814 that->onBandwidthControlChange();
11815#endif
11816 }
11817}
11818
11819/**
11820 * Commits all the changes to machine settings.
11821 *
11822 * Note that this operation is supposed to never fail.
11823 *
11824 * @note Locks this object and children for writing.
11825 */
11826void Machine::i_commit()
11827{
11828 AutoCaller autoCaller(this);
11829 AssertComRCReturnVoid(autoCaller.hrc());
11830
11831 AutoCaller peerCaller(mPeer);
11832 AssertComRCReturnVoid(peerCaller.hrc());
11833
11834 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11835
11836 /*
11837 * use safe commit to ensure Snapshot machines (that share mUserData)
11838 * will still refer to a valid memory location
11839 */
11840 mUserData.commitCopy();
11841
11842 mHWData.commit();
11843
11844 if (mMediumAttachments.isBackedUp())
11845 i_commitMedia(Global::IsOnline(mData->mMachineState));
11846
11847 mPlatform->i_commit();
11848 mFirmwareSettings->i_commit();
11849 mRecordingSettings->i_commit();
11850 mTrustedPlatformModule->i_commit();
11851 mNvramStore->i_commit();
11852 mGraphicsAdapter->i_commit();
11853 mVRDEServer->i_commit();
11854 mAudioSettings->i_commit();
11855 mUSBDeviceFilters->i_commit();
11856 mBandwidthControl->i_commit();
11857 mGuestDebugControl->i_commit();
11858
11859 /* Since mNetworkAdapters is a list which might have been changed (resized)
11860 * without using the Backupable<> template we need to handle the copying
11861 * of the list entries manually, including the creation of peers for the
11862 * new objects. */
11863 ChipsetType_T enmChipset;
11864 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11865 ComAssertComRC(hrc);
11866
11867 bool commitNetworkAdapters = false;
11868 size_t const newSize = PlatformProperties::s_getMaxNetworkAdapters(enmChipset);
11869 if (mPeer)
11870 {
11871 size_t const oldSize = mNetworkAdapters.size();
11872 size_t const oldPeerSize = mPeer->mNetworkAdapters.size();
11873
11874 /* commit everything, even the ones which will go away */
11875 for (size_t slot = 0; slot < oldSize; slot++)
11876 mNetworkAdapters[slot]->i_commit();
11877 /* copy over the new entries, creating a peer and uninit the original */
11878 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, oldPeerSize));
11879 /* make sure to have enough room for iterating over the (newly added) slots down below */
11880 if (newSize > oldSize)
11881 {
11882 mNetworkAdapters.resize(newSize);
11883
11884 com::Utf8Str osTypeId;
11885 ComObjPtr<GuestOSType> osType = NULL;
11886 hrc = getOSTypeId(osTypeId);
11887 if (SUCCEEDED(hrc))
11888 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
11889
11890 for (size_t slot = oldSize; slot < newSize; slot++)
11891 {
11892 mNetworkAdapters[slot].createObject();
11893 mNetworkAdapters[slot]->init(this, (ULONG)slot);
11894 mNetworkAdapters[slot]->i_applyDefaults(SUCCEEDED(hrc) ? osType : NULL);
11895 }
11896 }
11897 for (size_t slot = 0; slot < newSize; slot++)
11898 {
11899 /* look if this adapter has a peer device */
11900 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11901 if (!peer)
11902 {
11903 /* no peer means the adapter is a newly created one;
11904 * create a peer owning data this data share it with */
11905 peer.createObject();
11906 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11907 }
11908 mPeer->mNetworkAdapters[slot] = peer;
11909 }
11910 /* uninit any no longer needed network adapters */
11911 for (size_t slot = newSize; slot < oldSize; ++slot)
11912 mNetworkAdapters[slot]->uninit();
11913 for (size_t slot = newSize; slot < oldPeerSize; ++slot)
11914 {
11915 if (mPeer->mNetworkAdapters[slot])
11916 mPeer->mNetworkAdapters[slot]->uninit();
11917 }
11918 /* Keep the original network adapter count until this point, so that
11919 * discarding a chipset type change will not lose settings. */
11920 mNetworkAdapters.resize(newSize);
11921 mPeer->mNetworkAdapters.resize(newSize);
11922 }
11923 else
11924 {
11925 /* we have no peer (our parent is the newly created machine);
11926 * just commit changes to the network adapters */
11927 commitNetworkAdapters = true;
11928 }
11929 if (commitNetworkAdapters)
11930 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11931 mNetworkAdapters[slot]->i_commit();
11932
11933 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11934 mSerialPorts[slot]->i_commit();
11935 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11936 mParallelPorts[slot]->i_commit();
11937
11938 bool commitStorageControllers = false;
11939
11940 if (mStorageControllers.isBackedUp())
11941 {
11942 mStorageControllers.commit();
11943
11944 if (mPeer)
11945 {
11946 /* Commit all changes to new controllers (this will reshare data with
11947 * peers for those who have peers) */
11948 StorageControllerList *newList = new StorageControllerList();
11949 for (StorageControllerList::const_iterator
11950 it = mStorageControllers->begin();
11951 it != mStorageControllers->end();
11952 ++it)
11953 {
11954 (*it)->i_commit();
11955
11956 /* look if this controller has a peer device */
11957 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11958 if (!peer)
11959 {
11960 /* no peer means the device is a newly created one;
11961 * create a peer owning data this device share it with */
11962 peer.createObject();
11963 peer->init(mPeer, *it, true /* aReshare */);
11964 }
11965 else
11966 {
11967 /* remove peer from the old list */
11968 mPeer->mStorageControllers->remove(peer);
11969 }
11970 /* and add it to the new list */
11971 newList->push_back(peer);
11972 }
11973
11974 /* uninit old peer's controllers that are left */
11975 for (StorageControllerList::const_iterator
11976 it = mPeer->mStorageControllers->begin();
11977 it != mPeer->mStorageControllers->end();
11978 ++it)
11979 {
11980 (*it)->uninit();
11981 }
11982
11983 /* attach new list of controllers to our peer */
11984 mPeer->mStorageControllers.attach(newList);
11985 }
11986 else
11987 {
11988 /* we have no peer (our parent is the newly created machine);
11989 * just commit changes to devices */
11990 commitStorageControllers = true;
11991 }
11992 }
11993 else
11994 {
11995 /* the list of controllers itself is not changed,
11996 * just commit changes to controllers themselves */
11997 commitStorageControllers = true;
11998 }
11999
12000 if (commitStorageControllers)
12001 {
12002 for (StorageControllerList::const_iterator
12003 it = mStorageControllers->begin();
12004 it != mStorageControllers->end();
12005 ++it)
12006 {
12007 (*it)->i_commit();
12008 }
12009 }
12010
12011 bool commitUSBControllers = false;
12012
12013 if (mUSBControllers.isBackedUp())
12014 {
12015 mUSBControllers.commit();
12016
12017 if (mPeer)
12018 {
12019 /* Commit all changes to new controllers (this will reshare data with
12020 * peers for those who have peers) */
12021 USBControllerList *newList = new USBControllerList();
12022 for (USBControllerList::const_iterator
12023 it = mUSBControllers->begin();
12024 it != mUSBControllers->end();
12025 ++it)
12026 {
12027 (*it)->i_commit();
12028
12029 /* look if this controller has a peer device */
12030 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12031 if (!peer)
12032 {
12033 /* no peer means the device is a newly created one;
12034 * create a peer owning data this device share it with */
12035 peer.createObject();
12036 peer->init(mPeer, *it, true /* aReshare */);
12037 }
12038 else
12039 {
12040 /* remove peer from the old list */
12041 mPeer->mUSBControllers->remove(peer);
12042 }
12043 /* and add it to the new list */
12044 newList->push_back(peer);
12045 }
12046
12047 /* uninit old peer's controllers that are left */
12048 for (USBControllerList::const_iterator
12049 it = mPeer->mUSBControllers->begin();
12050 it != mPeer->mUSBControllers->end();
12051 ++it)
12052 {
12053 (*it)->uninit();
12054 }
12055
12056 /* attach new list of controllers to our peer */
12057 mPeer->mUSBControllers.attach(newList);
12058 }
12059 else
12060 {
12061 /* we have no peer (our parent is the newly created machine);
12062 * just commit changes to devices */
12063 commitUSBControllers = true;
12064 }
12065 }
12066 else
12067 {
12068 /* the list of controllers itself is not changed,
12069 * just commit changes to controllers themselves */
12070 commitUSBControllers = true;
12071 }
12072
12073 if (commitUSBControllers)
12074 {
12075 for (USBControllerList::const_iterator
12076 it = mUSBControllers->begin();
12077 it != mUSBControllers->end();
12078 ++it)
12079 {
12080 (*it)->i_commit();
12081 }
12082 }
12083
12084 if (i_isSessionMachine())
12085 {
12086 /* attach new data to the primary machine and reshare it */
12087 mPeer->mUserData.attach(mUserData);
12088 mPeer->mHWData.attach(mHWData);
12089 /* mmMediumAttachments is reshared by fixupMedia */
12090 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12091 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12092 }
12093}
12094
12095/**
12096 * Copies all the hardware data from the given machine.
12097 *
12098 * Currently, only called when the VM is being restored from a snapshot. In
12099 * particular, this implies that the VM is not running during this method's
12100 * call.
12101 *
12102 * @note This method must be called from under this object's lock.
12103 *
12104 * @note This method doesn't call #i_commit(), so all data remains backed up and
12105 * unsaved.
12106 */
12107void Machine::i_copyFrom(Machine *aThat)
12108{
12109 AssertReturnVoid(!i_isSnapshotMachine());
12110 AssertReturnVoid(aThat->i_isSnapshotMachine());
12111
12112 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12113
12114 mHWData.assignCopy(aThat->mHWData);
12115
12116 // create copies of all shared folders (mHWData after attaching a copy
12117 // contains just references to original objects)
12118 for (HWData::SharedFolderList::iterator
12119 it = mHWData->mSharedFolders.begin();
12120 it != mHWData->mSharedFolders.end();
12121 ++it)
12122 {
12123 ComObjPtr<SharedFolder> folder;
12124 folder.createObject();
12125 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12126 AssertComRC(hrc);
12127 *it = folder;
12128 }
12129
12130 mPlatform->i_copyFrom(aThat->mPlatform);
12131 i_platformPropertiesUpdate();
12132 mFirmwareSettings->i_copyFrom(aThat->mFirmwareSettings);
12133 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12134 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12135 mNvramStore->i_copyFrom(aThat->mNvramStore);
12136 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12137 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12138 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12139 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12140 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12141 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12142
12143 /* create private copies of all controllers */
12144 mStorageControllers.backup();
12145 mStorageControllers->clear();
12146 for (StorageControllerList::const_iterator
12147 it = aThat->mStorageControllers->begin();
12148 it != aThat->mStorageControllers->end();
12149 ++it)
12150 {
12151 ComObjPtr<StorageController> ctrl;
12152 ctrl.createObject();
12153 ctrl->initCopy(this, *it);
12154 mStorageControllers->push_back(ctrl);
12155 }
12156
12157 /* create private copies of all USB controllers */
12158 mUSBControllers.backup();
12159 mUSBControllers->clear();
12160 for (USBControllerList::const_iterator
12161 it = aThat->mUSBControllers->begin();
12162 it != aThat->mUSBControllers->end();
12163 ++it)
12164 {
12165 ComObjPtr<USBController> ctrl;
12166 ctrl.createObject();
12167 ctrl->initCopy(this, *it);
12168 mUSBControllers->push_back(ctrl);
12169 }
12170
12171 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12172 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12173 {
12174 if (mNetworkAdapters[slot].isNotNull())
12175 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12176 else
12177 {
12178 unconst(mNetworkAdapters[slot]).createObject();
12179 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12180 }
12181 }
12182 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12183 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12184 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12185 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12186}
12187
12188/**
12189 * Returns whether the given storage controller is hotplug capable.
12190 *
12191 * @returns true if the controller supports hotplugging
12192 * false otherwise.
12193 * @param enmCtrlType The controller type to check for.
12194 */
12195bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12196{
12197 BOOL aHotplugCapable = FALSE;
12198 HRESULT hrc = mPlatformProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12199 AssertComRC(hrc);
12200
12201 return RT_BOOL(aHotplugCapable);
12202}
12203
12204#ifdef VBOX_WITH_RESOURCE_USAGE_API
12205
12206void Machine::i_getDiskList(MediaList &list)
12207{
12208 for (MediumAttachmentList::const_iterator
12209 it = mMediumAttachments->begin();
12210 it != mMediumAttachments->end();
12211 ++it)
12212 {
12213 MediumAttachment *pAttach = *it;
12214 /* just in case */
12215 AssertContinue(pAttach);
12216
12217 AutoCaller localAutoCallerA(pAttach);
12218 if (FAILED(localAutoCallerA.hrc())) continue;
12219
12220 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12221
12222 if (pAttach->i_getType() == DeviceType_HardDisk)
12223 list.push_back(pAttach->i_getMedium());
12224 }
12225}
12226
12227void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12228{
12229 AssertReturnVoid(isWriteLockOnCurrentThread());
12230 AssertPtrReturnVoid(aCollector);
12231
12232 pm::CollectorHAL *hal = aCollector->getHAL();
12233 /* Create sub metrics */
12234 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12235 "Percentage of processor time spent in user mode by the VM process.");
12236 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12237 "Percentage of processor time spent in kernel mode by the VM process.");
12238 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12239 "Size of resident portion of VM process in memory.");
12240 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12241 "Actual size of all VM disks combined.");
12242 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12243 "Network receive rate.");
12244 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12245 "Network transmit rate.");
12246 /* Create and register base metrics */
12247 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12248 cpuLoadUser, cpuLoadKernel);
12249 aCollector->registerBaseMetric(cpuLoad);
12250 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12251 ramUsageUsed);
12252 aCollector->registerBaseMetric(ramUsage);
12253 MediaList disks;
12254 i_getDiskList(disks);
12255 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12256 diskUsageUsed);
12257 aCollector->registerBaseMetric(diskUsage);
12258
12259 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12260 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12261 new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12263 new pm::AggregateMin()));
12264 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12265 new pm::AggregateMax()));
12266 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12267 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12268 new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12270 new pm::AggregateMin()));
12271 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12272 new pm::AggregateMax()));
12273
12274 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12275 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12276 new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12278 new pm::AggregateMin()));
12279 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12280 new pm::AggregateMax()));
12281
12282 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12283 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12284 new pm::AggregateAvg()));
12285 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12286 new pm::AggregateMin()));
12287 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12288 new pm::AggregateMax()));
12289
12290
12291 /* Guest metrics collector */
12292 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12293 aCollector->registerGuest(mCollectorGuest);
12294 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12295
12296 /* Create sub metrics */
12297 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12298 "Percentage of processor time spent in user mode as seen by the guest.");
12299 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12300 "Percentage of processor time spent in kernel mode as seen by the guest.");
12301 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12302 "Percentage of processor time spent idling as seen by the guest.");
12303
12304 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12305 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12306 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12307 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12308 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12309 pm::SubMetric *guestMemCache = new pm::SubMetric(
12310 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12311
12312 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12313 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12314
12315 /* Create and register base metrics */
12316 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12317 machineNetRx, machineNetTx);
12318 aCollector->registerBaseMetric(machineNetRate);
12319
12320 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12321 guestLoadUser, guestLoadKernel, guestLoadIdle);
12322 aCollector->registerBaseMetric(guestCpuLoad);
12323
12324 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12325 guestMemTotal, guestMemFree,
12326 guestMemBalloon, guestMemShared,
12327 guestMemCache, guestPagedTotal);
12328 aCollector->registerBaseMetric(guestCpuMem);
12329
12330 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12331 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12336 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12374
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12379
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12384}
12385
12386void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12387{
12388 AssertReturnVoid(isWriteLockOnCurrentThread());
12389
12390 if (aCollector)
12391 {
12392 aCollector->unregisterMetricsFor(aMachine);
12393 aCollector->unregisterBaseMetricsFor(aMachine);
12394 }
12395}
12396
12397#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12398
12399/**
12400 * Updates the machine's platform properties based on the current platform architecture.
12401 *
12402 * @note Called internally when committing, rolling back or loading settings.
12403 */
12404void Machine::i_platformPropertiesUpdate()
12405{
12406 if (mPlatform)
12407 {
12408 /* Update architecture for platform properties. */
12409 PlatformArchitecture_T platformArchitecture;
12410 HRESULT hrc = mPlatform->getArchitecture(&platformArchitecture);
12411 ComAssertComRC(hrc);
12412 hrc = mPlatformProperties->i_setArchitecture(platformArchitecture);
12413 ComAssertComRC(hrc);
12414 }
12415}
12416
12417
12418////////////////////////////////////////////////////////////////////////////////
12419
12420DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12421
12422HRESULT SessionMachine::FinalConstruct()
12423{
12424 LogFlowThisFunc(("\n"));
12425
12426 mClientToken = NULL;
12427
12428 return BaseFinalConstruct();
12429}
12430
12431void SessionMachine::FinalRelease()
12432{
12433 LogFlowThisFunc(("\n"));
12434
12435 Assert(!mClientToken);
12436 /* paranoia, should not hang around any more */
12437 if (mClientToken)
12438 {
12439 delete mClientToken;
12440 mClientToken = NULL;
12441 }
12442
12443 uninit(Uninit::Unexpected);
12444
12445 BaseFinalRelease();
12446}
12447
12448/**
12449 * @note Must be called only by Machine::LockMachine() from its own write lock.
12450 */
12451HRESULT SessionMachine::init(Machine *aMachine)
12452{
12453 LogFlowThisFuncEnter();
12454 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12455
12456 AssertReturn(aMachine, E_INVALIDARG);
12457
12458 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12459
12460 /* Enclose the state transition NotReady->InInit->Ready */
12461 AutoInitSpan autoInitSpan(this);
12462 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12463
12464 HRESULT hrc = S_OK;
12465
12466 RT_ZERO(mAuthLibCtx);
12467
12468 /* create the machine client token */
12469 try
12470 {
12471 mClientToken = new ClientToken(aMachine, this);
12472 if (!mClientToken->isReady())
12473 {
12474 delete mClientToken;
12475 mClientToken = NULL;
12476 hrc = E_FAIL;
12477 }
12478 }
12479 catch (std::bad_alloc &)
12480 {
12481 hrc = E_OUTOFMEMORY;
12482 }
12483 if (FAILED(hrc))
12484 return hrc;
12485
12486 /* memorize the peer Machine */
12487 unconst(mPeer) = aMachine;
12488 /* share the parent pointer */
12489 unconst(mParent) = aMachine->mParent;
12490
12491 /* take the pointers to data to share */
12492 mData.share(aMachine->mData);
12493 mSSData.share(aMachine->mSSData);
12494
12495 mUserData.share(aMachine->mUserData);
12496 mHWData.share(aMachine->mHWData);
12497 mMediumAttachments.share(aMachine->mMediumAttachments);
12498
12499 mStorageControllers.allocate();
12500 for (StorageControllerList::const_iterator
12501 it = aMachine->mStorageControllers->begin();
12502 it != aMachine->mStorageControllers->end();
12503 ++it)
12504 {
12505 ComObjPtr<StorageController> ctl;
12506 ctl.createObject();
12507 ctl->init(this, *it);
12508 mStorageControllers->push_back(ctl);
12509 }
12510
12511 mUSBControllers.allocate();
12512 for (USBControllerList::const_iterator
12513 it = aMachine->mUSBControllers->begin();
12514 it != aMachine->mUSBControllers->end();
12515 ++it)
12516 {
12517 ComObjPtr<USBController> ctl;
12518 ctl.createObject();
12519 ctl->init(this, *it);
12520 mUSBControllers->push_back(ctl);
12521 }
12522
12523 unconst(mPlatformProperties).createObject();
12524 mPlatformProperties->init(mParent);
12525 unconst(mPlatform).createObject();
12526 mPlatform->init(this, aMachine->mPlatform);
12527
12528 i_platformPropertiesUpdate();
12529
12530 unconst(mFirmwareSettings).createObject();
12531 mFirmwareSettings->init(this, aMachine->mFirmwareSettings);
12532
12533 unconst(mRecordingSettings).createObject();
12534 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12535
12536 unconst(mTrustedPlatformModule).createObject();
12537 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12538
12539 unconst(mNvramStore).createObject();
12540 mNvramStore->init(this, aMachine->mNvramStore);
12541
12542 /* create another GraphicsAdapter object that will be mutable */
12543 unconst(mGraphicsAdapter).createObject();
12544 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12545 /* create another VRDEServer object that will be mutable */
12546 unconst(mVRDEServer).createObject();
12547 mVRDEServer->init(this, aMachine->mVRDEServer);
12548 /* create another audio settings object that will be mutable */
12549 unconst(mAudioSettings).createObject();
12550 mAudioSettings->init(this, aMachine->mAudioSettings);
12551 /* create a list of serial ports that will be mutable */
12552 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12553 {
12554 unconst(mSerialPorts[slot]).createObject();
12555 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12556 }
12557 /* create a list of parallel ports that will be mutable */
12558 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12559 {
12560 unconst(mParallelPorts[slot]).createObject();
12561 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12562 }
12563
12564 /* create another USB device filters object that will be mutable */
12565 unconst(mUSBDeviceFilters).createObject();
12566 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12567
12568 /* create a list of network adapters that will be mutable */
12569 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12570 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12571 {
12572 unconst(mNetworkAdapters[slot]).createObject();
12573 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12574 }
12575
12576 /* create another bandwidth control object that will be mutable */
12577 unconst(mBandwidthControl).createObject();
12578 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12579
12580 unconst(mGuestDebugControl).createObject();
12581 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
12582
12583 /* default is to delete saved state on Saved -> PoweredOff transition */
12584 mRemoveSavedState = true;
12585
12586 /* Confirm a successful initialization when it's the case */
12587 autoInitSpan.setSucceeded();
12588
12589 miNATNetworksStarted = 0;
12590
12591 LogFlowThisFuncLeave();
12592 return hrc;
12593}
12594
12595/**
12596 * Uninitializes this session object. If the reason is other than
12597 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12598 * or the client watcher code.
12599 *
12600 * @param aReason uninitialization reason
12601 *
12602 * @note Locks mParent + this object for writing.
12603 */
12604void SessionMachine::uninit(Uninit::Reason aReason)
12605{
12606 LogFlowThisFuncEnter();
12607 LogFlowThisFunc(("reason=%d\n", aReason));
12608
12609 /*
12610 * Strongly reference ourselves to prevent this object deletion after
12611 * mData->mSession.mMachine.setNull() below (which can release the last
12612 * reference and call the destructor). Important: this must be done before
12613 * accessing any members (and before AutoUninitSpan that does it as well).
12614 * This self reference will be released as the very last step on return.
12615 */
12616 ComObjPtr<SessionMachine> selfRef;
12617 if (aReason != Uninit::Unexpected)
12618 selfRef = this;
12619
12620 /* Enclose the state transition Ready->InUninit->NotReady */
12621 AutoUninitSpan autoUninitSpan(this);
12622 if (autoUninitSpan.uninitDone())
12623 {
12624 LogFlowThisFunc(("Already uninitialized\n"));
12625 LogFlowThisFuncLeave();
12626 return;
12627 }
12628
12629 if (autoUninitSpan.initFailed())
12630 {
12631 /* We've been called by init() because it's failed. It's not really
12632 * necessary (nor it's safe) to perform the regular uninit sequence
12633 * below, the following is enough.
12634 */
12635 LogFlowThisFunc(("Initialization failed.\n"));
12636 /* destroy the machine client token */
12637 if (mClientToken)
12638 {
12639 delete mClientToken;
12640 mClientToken = NULL;
12641 }
12642 uninitDataAndChildObjects();
12643 mData.free();
12644 unconst(mParent) = NULL;
12645 unconst(mPeer) = NULL;
12646 LogFlowThisFuncLeave();
12647 return;
12648 }
12649
12650 MachineState_T lastState;
12651 {
12652 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12653 lastState = mData->mMachineState;
12654 }
12655 NOREF(lastState);
12656
12657#ifdef VBOX_WITH_USB
12658 // release all captured USB devices, but do this before requesting the locks below
12659 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12660 {
12661 /* Console::captureUSBDevices() is called in the VM process only after
12662 * setting the machine state to Starting or Restoring.
12663 * Console::detachAllUSBDevices() will be called upon successful
12664 * termination. So, we need to release USB devices only if there was
12665 * an abnormal termination of a running VM.
12666 *
12667 * This is identical to SessionMachine::DetachAllUSBDevices except
12668 * for the aAbnormal argument. */
12669 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12670 AssertComRC(hrc);
12671 NOREF(hrc);
12672
12673 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12674 if (service)
12675 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12676 }
12677#endif /* VBOX_WITH_USB */
12678
12679 /* We need to lock this object in uninit() because the lock is shared
12680 * with mPeer (as well as data we modify below). mParent lock is needed
12681 * by several calls to it.
12682 * We can't use AutoMultiWriteLock2 here as some error code paths
12683 * acquire the machine lock so we need to be able to drop the machine
12684 * lock individually in those cases. */
12685 AutoWriteLock aVBoxLock(mParent COMMA_LOCKVAL_SRC_POS);
12686 AutoWriteLock aMachineLock(this COMMA_LOCKVAL_SRC_POS);
12687
12688#ifdef VBOX_WITH_RESOURCE_USAGE_API
12689 /*
12690 * It is safe to call Machine::i_unregisterMetrics() here because
12691 * PerformanceCollector::samplerCallback no longer accesses guest methods
12692 * holding the lock.
12693 */
12694 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12695 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12696 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12697 if (mCollectorGuest)
12698 {
12699 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12700 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12701 mCollectorGuest = NULL;
12702 }
12703#endif
12704
12705 if (aReason == Uninit::Abnormal)
12706 {
12707 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12708
12709 /*
12710 * Move the VM to the 'Aborted' machine state unless we are restoring a
12711 * VM that was in the 'Saved' machine state. In that case, if the VM
12712 * fails before reaching either the 'Restoring' machine state or the
12713 * 'Running' machine state then we set the machine state to
12714 * 'AbortedSaved' in order to preserve the saved state file so that the
12715 * VM can be restored in the future.
12716 */
12717 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12718 i_setMachineState(MachineState_AbortedSaved);
12719 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12720 i_setMachineState(MachineState_Aborted);
12721 }
12722
12723 // any machine settings modified?
12724 if (mData->flModifications)
12725 {
12726 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12727 aMachineLock.release();
12728 discardSettings();
12729 mParent->i_unmarkRegistryModified(i_getId());
12730 aMachineLock.acquire();
12731 }
12732
12733 mData->mSession.mPID = NIL_RTPROCESS;
12734
12735 if (aReason == Uninit::Unexpected)
12736 {
12737 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12738 * client watcher thread to update the set of machines that have open
12739 * sessions. */
12740 mParent->i_updateClientWatcher();
12741 }
12742
12743 /* uninitialize all remote controls */
12744 if (mData->mSession.mRemoteControls.size())
12745 {
12746 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12747 mData->mSession.mRemoteControls.size()));
12748
12749 /* Always restart a the beginning, since the iterator is invalidated
12750 * by using erase(). */
12751 for (Data::Session::RemoteControlList::iterator
12752 it = mData->mSession.mRemoteControls.begin();
12753 it != mData->mSession.mRemoteControls.end();
12754 it = mData->mSession.mRemoteControls.begin())
12755 {
12756 ComPtr<IInternalSessionControl> pControl = *it;
12757 mData->mSession.mRemoteControls.erase(it);
12758 aMachineLock.release();
12759 aVBoxLock.release();
12760 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12761 HRESULT hrc = pControl->Uninitialize();
12762 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
12763 if (FAILED(hrc))
12764 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12765 aVBoxLock.acquire();
12766 aMachineLock.acquire();
12767 }
12768 mData->mSession.mRemoteControls.clear();
12769 }
12770
12771 /* Remove all references to the NAT network service. The service will stop
12772 * if all references (also from other VMs) are removed. */
12773 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12774 {
12775 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12776 {
12777 BOOL enabled;
12778 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12779 if ( FAILED(hrc)
12780 || !enabled)
12781 continue;
12782
12783 NetworkAttachmentType_T type;
12784 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12785 if ( SUCCEEDED(hrc)
12786 && type == NetworkAttachmentType_NATNetwork)
12787 {
12788 Bstr name;
12789 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12790 if (SUCCEEDED(hrc))
12791 {
12792 aMachineLock.release();
12793 aVBoxLock.release();
12794 Utf8Str strName(name);
12795 LogRel(("VM '%s' stops using NAT network '%s'\n",
12796 mUserData->s.strName.c_str(), strName.c_str()));
12797 mParent->i_natNetworkRefDec(strName);
12798 aVBoxLock.acquire();
12799 aMachineLock.acquire();
12800 }
12801 }
12802 }
12803 }
12804
12805 /*
12806 * An expected uninitialization can come only from #i_checkForDeath().
12807 * Otherwise it means that something's gone really wrong (for example,
12808 * the Session implementation has released the VirtualBox reference
12809 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12810 * etc). However, it's also possible, that the client releases the IPC
12811 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12812 * but the VirtualBox release event comes first to the server process.
12813 * This case is practically possible, so we should not assert on an
12814 * unexpected uninit, just log a warning.
12815 */
12816
12817 if (aReason == Uninit::Unexpected)
12818 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12819
12820 if (aReason != Uninit::Normal)
12821 {
12822 mData->mSession.mDirectControl.setNull();
12823 }
12824 else
12825 {
12826 /* this must be null here (see #OnSessionEnd()) */
12827 Assert(mData->mSession.mDirectControl.isNull());
12828 Assert(mData->mSession.mState == SessionState_Unlocking);
12829 Assert(!mData->mSession.mProgress.isNull());
12830 }
12831 if (mData->mSession.mProgress)
12832 {
12833 if (aReason == Uninit::Normal)
12834 mData->mSession.mProgress->i_notifyComplete(S_OK);
12835 else
12836 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12837 COM_IIDOF(ISession),
12838 getComponentName(),
12839 tr("The VM session was aborted"));
12840 mData->mSession.mProgress.setNull();
12841 }
12842
12843 if (mConsoleTaskData.mProgress)
12844 {
12845 Assert(aReason == Uninit::Abnormal);
12846 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12847 COM_IIDOF(ISession),
12848 getComponentName(),
12849 tr("The VM session was aborted"));
12850 mConsoleTaskData.mProgress.setNull();
12851 }
12852
12853 /* remove the association between the peer machine and this session machine */
12854 Assert( (SessionMachine*)mData->mSession.mMachine == this
12855 || aReason == Uninit::Unexpected);
12856
12857 /* reset the rest of session data */
12858 mData->mSession.mLockType = LockType_Null;
12859 mData->mSession.mMachine.setNull();
12860 mData->mSession.mState = SessionState_Unlocked;
12861 mData->mSession.mName.setNull();
12862
12863 /* destroy the machine client token before leaving the exclusive lock */
12864 if (mClientToken)
12865 {
12866 delete mClientToken;
12867 mClientToken = NULL;
12868 }
12869
12870 /* fire an event */
12871 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12872
12873 uninitDataAndChildObjects();
12874
12875 /* free the essential data structure last */
12876 mData.free();
12877
12878 /* release the exclusive locks before setting the below two to NULL */
12879 aMachineLock.release();
12880 aVBoxLock.release();
12881
12882 unconst(mParent) = NULL;
12883 unconst(mPeer) = NULL;
12884
12885 AuthLibUnload(&mAuthLibCtx);
12886
12887 LogFlowThisFuncLeave();
12888}
12889
12890// util::Lockable interface
12891////////////////////////////////////////////////////////////////////////////////
12892
12893/**
12894 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12895 * with the primary Machine instance (mPeer).
12896 */
12897RWLockHandle *SessionMachine::lockHandle() const
12898{
12899 AssertReturn(mPeer != NULL, NULL);
12900 return mPeer->lockHandle();
12901}
12902
12903// IInternalMachineControl methods
12904////////////////////////////////////////////////////////////////////////////////
12905
12906/**
12907 * Passes collected guest statistics to performance collector object
12908 */
12909HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12910 ULONG aCpuKernel, ULONG aCpuIdle,
12911 ULONG aMemTotal, ULONG aMemFree,
12912 ULONG aMemBalloon, ULONG aMemShared,
12913 ULONG aMemCache, ULONG aPageTotal,
12914 ULONG aAllocVMM, ULONG aFreeVMM,
12915 ULONG aBalloonedVMM, ULONG aSharedVMM,
12916 ULONG aVmNetRx, ULONG aVmNetTx)
12917{
12918#ifdef VBOX_WITH_RESOURCE_USAGE_API
12919 if (mCollectorGuest)
12920 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12921 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12922 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12923 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12924
12925 return S_OK;
12926#else
12927 NOREF(aValidStats);
12928 NOREF(aCpuUser);
12929 NOREF(aCpuKernel);
12930 NOREF(aCpuIdle);
12931 NOREF(aMemTotal);
12932 NOREF(aMemFree);
12933 NOREF(aMemBalloon);
12934 NOREF(aMemShared);
12935 NOREF(aMemCache);
12936 NOREF(aPageTotal);
12937 NOREF(aAllocVMM);
12938 NOREF(aFreeVMM);
12939 NOREF(aBalloonedVMM);
12940 NOREF(aSharedVMM);
12941 NOREF(aVmNetRx);
12942 NOREF(aVmNetTx);
12943 return E_NOTIMPL;
12944#endif
12945}
12946
12947////////////////////////////////////////////////////////////////////////////////
12948//
12949// SessionMachine task records
12950//
12951////////////////////////////////////////////////////////////////////////////////
12952
12953/**
12954 * Task record for saving the machine state.
12955 */
12956class SessionMachine::SaveStateTask
12957 : public Machine::Task
12958{
12959public:
12960 SaveStateTask(SessionMachine *m,
12961 Progress *p,
12962 const Utf8Str &t,
12963 Reason_T enmReason,
12964 const Utf8Str &strStateFilePath)
12965 : Task(m, p, t),
12966 m_enmReason(enmReason),
12967 m_strStateFilePath(strStateFilePath)
12968 {}
12969
12970private:
12971 void handler()
12972 {
12973 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12974 }
12975
12976 Reason_T m_enmReason;
12977 Utf8Str m_strStateFilePath;
12978
12979 friend class SessionMachine;
12980};
12981
12982/**
12983 * Task thread implementation for SessionMachine::SaveState(), called from
12984 * SessionMachine::taskHandler().
12985 *
12986 * @note Locks this object for writing.
12987 *
12988 * @param task
12989 */
12990void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12991{
12992 LogFlowThisFuncEnter();
12993
12994 AutoCaller autoCaller(this);
12995 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12996 if (FAILED(autoCaller.hrc()))
12997 {
12998 /* we might have been uninitialized because the session was accidentally
12999 * closed by the client, so don't assert */
13000 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
13001 task.m_pProgress->i_notifyComplete(hrc);
13002 LogFlowThisFuncLeave();
13003 return;
13004 }
13005
13006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13007
13008 HRESULT hrc = S_OK;
13009
13010 try
13011 {
13012 ComPtr<IInternalSessionControl> directControl;
13013 if (mData->mSession.mLockType == LockType_VM)
13014 directControl = mData->mSession.mDirectControl;
13015 if (directControl.isNull())
13016 throw setError(VBOX_E_INVALID_VM_STATE,
13017 tr("Trying to save state without a running VM"));
13018 alock.release();
13019 BOOL fSuspendedBySave;
13020 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13021 Assert(!fSuspendedBySave);
13022 alock.acquire();
13023
13024 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
13025 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
13026 throw E_FAIL);
13027
13028 if (SUCCEEDED(hrc))
13029 {
13030 mSSData->strStateFilePath = task.m_strStateFilePath;
13031
13032 /* save all VM settings */
13033 hrc = i_saveSettings(NULL, alock);
13034 // no need to check whether VirtualBox.xml needs saving also since
13035 // we can't have a name change pending at this point
13036 }
13037 else
13038 {
13039 // On failure, set the state to the state we had at the beginning.
13040 i_setMachineState(task.m_machineStateBackup);
13041 i_updateMachineStateOnClient();
13042
13043 // Delete the saved state file (might have been already created).
13044 // No need to check whether this is shared with a snapshot here
13045 // because we certainly created a fresh saved state file here.
13046 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13047 }
13048 }
13049 catch (HRESULT hrcXcpt)
13050 {
13051 hrc = hrcXcpt;
13052 }
13053
13054 task.m_pProgress->i_notifyComplete(hrc);
13055
13056 LogFlowThisFuncLeave();
13057}
13058
13059/**
13060 * @note Locks this object for writing.
13061 */
13062HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13063{
13064 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13065}
13066
13067HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13068{
13069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13070
13071 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13072 if (FAILED(hrc)) return hrc;
13073
13074 if ( mData->mMachineState != MachineState_Running
13075 && mData->mMachineState != MachineState_Paused
13076 )
13077 return setError(VBOX_E_INVALID_VM_STATE,
13078 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13079 Global::stringifyMachineState(mData->mMachineState));
13080
13081 ComObjPtr<Progress> pProgress;
13082 pProgress.createObject();
13083 hrc = pProgress->init(i_getVirtualBox(),
13084 static_cast<IMachine *>(this) /* aInitiator */,
13085 tr("Saving the execution state of the virtual machine"),
13086 FALSE /* aCancelable */);
13087 if (FAILED(hrc))
13088 return hrc;
13089
13090 Utf8Str strStateFilePath;
13091 i_composeSavedStateFilename(strStateFilePath);
13092
13093 /* create and start the task on a separate thread (note that it will not
13094 * start working until we release alock) */
13095 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13096 hrc = pTask->createThread();
13097 if (FAILED(hrc))
13098 return hrc;
13099
13100 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13101 i_setMachineState(MachineState_Saving);
13102 i_updateMachineStateOnClient();
13103
13104 pProgress.queryInterfaceTo(aProgress.asOutParam());
13105
13106 return S_OK;
13107}
13108
13109/**
13110 * @note Locks this object for writing.
13111 */
13112HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13113{
13114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13115
13116 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13117 if (FAILED(hrc)) return hrc;
13118
13119 if ( mData->mMachineState != MachineState_PoweredOff
13120 && mData->mMachineState != MachineState_Teleported
13121 && mData->mMachineState != MachineState_Aborted
13122 )
13123 return setError(VBOX_E_INVALID_VM_STATE,
13124 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13125 Global::stringifyMachineState(mData->mMachineState));
13126
13127 com::Utf8Str stateFilePathFull;
13128 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13129 if (RT_FAILURE(vrc))
13130 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13131 tr("Invalid saved state file path '%s' (%Rrc)"),
13132 aSavedStateFile.c_str(),
13133 vrc);
13134
13135 mSSData->strStateFilePath = stateFilePathFull;
13136
13137 /* The below i_setMachineState() will detect the state transition and will
13138 * update the settings file */
13139
13140 return i_setMachineState(MachineState_Saved);
13141}
13142
13143/**
13144 * @note Locks this object for writing.
13145 */
13146HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13147{
13148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13149
13150 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13151 if (FAILED(hrc)) return hrc;
13152
13153 if ( mData->mMachineState != MachineState_Saved
13154 && mData->mMachineState != MachineState_AbortedSaved)
13155 return setError(VBOX_E_INVALID_VM_STATE,
13156 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13157 Global::stringifyMachineState(mData->mMachineState));
13158
13159 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13160
13161 /*
13162 * Saved -> PoweredOff transition will be detected in the SessionMachine
13163 * and properly handled.
13164 */
13165 hrc = i_setMachineState(MachineState_PoweredOff);
13166 return hrc;
13167}
13168
13169
13170/**
13171 * @note Locks the same as #i_setMachineState() does.
13172 */
13173HRESULT SessionMachine::updateState(MachineState_T aState)
13174{
13175 return i_setMachineState(aState);
13176}
13177
13178/**
13179 * @note Locks this object for writing.
13180 */
13181HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13182{
13183 IProgress *pProgress(aProgress);
13184
13185 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13186
13187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13188
13189 if (mData->mSession.mState != SessionState_Locked)
13190 return VBOX_E_INVALID_OBJECT_STATE;
13191
13192 if (!mData->mSession.mProgress.isNull())
13193 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13194
13195 /* If we didn't reference the NAT network service yet, add a reference to
13196 * force a start */
13197 if (miNATNetworksStarted < 1)
13198 {
13199 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13200 {
13201 BOOL enabled;
13202 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13203 if ( FAILED(hrc)
13204 || !enabled)
13205 continue;
13206
13207 NetworkAttachmentType_T type;
13208 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13209 if ( SUCCEEDED(hrc)
13210 && type == NetworkAttachmentType_NATNetwork)
13211 {
13212 Bstr name;
13213 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13214 if (SUCCEEDED(hrc))
13215 {
13216 Utf8Str strName(name);
13217 LogRel(("VM '%s' starts using NAT network '%s'\n",
13218 mUserData->s.strName.c_str(), strName.c_str()));
13219 mPeer->lockHandle()->unlockWrite();
13220 mParent->i_natNetworkRefInc(strName);
13221#ifdef RT_LOCK_STRICT
13222 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13223#else
13224 mPeer->lockHandle()->lockWrite();
13225#endif
13226 }
13227 }
13228 }
13229 miNATNetworksStarted++;
13230 }
13231
13232 LogFlowThisFunc(("returns S_OK.\n"));
13233 return S_OK;
13234}
13235
13236/**
13237 * @note Locks this object for writing.
13238 */
13239HRESULT SessionMachine::endPowerUp(LONG aResult)
13240{
13241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13242
13243 if (mData->mSession.mState != SessionState_Locked)
13244 return VBOX_E_INVALID_OBJECT_STATE;
13245
13246 /* Finalize the LaunchVMProcess progress object. */
13247 if (mData->mSession.mProgress)
13248 {
13249 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13250 mData->mSession.mProgress.setNull();
13251 }
13252
13253 if (SUCCEEDED((HRESULT)aResult))
13254 {
13255#ifdef VBOX_WITH_RESOURCE_USAGE_API
13256 /* The VM has been powered up successfully, so it makes sense
13257 * now to offer the performance metrics for a running machine
13258 * object. Doing it earlier wouldn't be safe. */
13259 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13260 mData->mSession.mPID);
13261#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13262 }
13263
13264 return S_OK;
13265}
13266
13267/**
13268 * @note Locks this object for writing.
13269 */
13270HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13271{
13272 LogFlowThisFuncEnter();
13273
13274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13275
13276 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13277 E_FAIL);
13278
13279 /* create a progress object to track operation completion */
13280 ComObjPtr<Progress> pProgress;
13281 pProgress.createObject();
13282 pProgress->init(i_getVirtualBox(),
13283 static_cast<IMachine *>(this) /* aInitiator */,
13284 tr("Stopping the virtual machine"),
13285 FALSE /* aCancelable */);
13286
13287 /* fill in the console task data */
13288 mConsoleTaskData.mLastState = mData->mMachineState;
13289 mConsoleTaskData.mProgress = pProgress;
13290
13291 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13292 i_setMachineState(MachineState_Stopping);
13293
13294 pProgress.queryInterfaceTo(aProgress.asOutParam());
13295
13296 return S_OK;
13297}
13298
13299/**
13300 * @note Locks this object for writing.
13301 */
13302HRESULT SessionMachine::endPoweringDown(LONG aResult,
13303 const com::Utf8Str &aErrMsg)
13304{
13305 HRESULT const hrcResult = (HRESULT)aResult;
13306 LogFlowThisFuncEnter();
13307
13308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13309
13310 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13311 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13312 && mConsoleTaskData.mLastState != MachineState_Null,
13313 E_FAIL);
13314
13315 /*
13316 * On failure, set the state to the state we had when BeginPoweringDown()
13317 * was called (this is expected by Console::PowerDown() and the associated
13318 * task). On success the VM process already changed the state to
13319 * MachineState_PoweredOff, so no need to do anything.
13320 */
13321 if (FAILED(hrcResult))
13322 i_setMachineState(mConsoleTaskData.mLastState);
13323
13324 /* notify the progress object about operation completion */
13325 Assert(mConsoleTaskData.mProgress);
13326 if (SUCCEEDED(hrcResult))
13327 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13328 else
13329 {
13330 if (aErrMsg.length())
13331 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13332 COM_IIDOF(ISession),
13333 getComponentName(),
13334 aErrMsg.c_str());
13335 else
13336 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13337 }
13338
13339 /* clear out the temporary saved state data */
13340 mConsoleTaskData.mLastState = MachineState_Null;
13341 mConsoleTaskData.mProgress.setNull();
13342
13343 LogFlowThisFuncLeave();
13344 return S_OK;
13345}
13346
13347
13348/**
13349 * Goes through the USB filters of the given machine to see if the given
13350 * device matches any filter or not.
13351 *
13352 * @note Locks the same as USBController::hasMatchingFilter() does.
13353 */
13354HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13355 BOOL *aMatched,
13356 ULONG *aMaskedInterfaces)
13357{
13358 LogFlowThisFunc(("\n"));
13359
13360#ifdef VBOX_WITH_USB
13361 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13362#else
13363 NOREF(aDevice);
13364 NOREF(aMaskedInterfaces);
13365 *aMatched = FALSE;
13366#endif
13367
13368 return S_OK;
13369}
13370
13371/**
13372 * @note Locks the same as Host::captureUSBDevice() does.
13373 */
13374HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378#ifdef VBOX_WITH_USB
13379 /* if captureDeviceForVM() fails, it must have set extended error info */
13380 clearError();
13381 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13382 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13383 return hrc;
13384
13385 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13386 AssertReturn(service, E_FAIL);
13387 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13388#else
13389 RT_NOREF(aId, aCaptureFilename);
13390 return E_NOTIMPL;
13391#endif
13392}
13393
13394/**
13395 * @note Locks the same as Host::detachUSBDevice() does.
13396 */
13397HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13398 BOOL aDone)
13399{
13400 LogFlowThisFunc(("\n"));
13401
13402#ifdef VBOX_WITH_USB
13403 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13404 AssertReturn(service, E_FAIL);
13405 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13406#else
13407 NOREF(aId);
13408 NOREF(aDone);
13409 return E_NOTIMPL;
13410#endif
13411}
13412
13413/**
13414 * Inserts all machine filters to the USB proxy service and then calls
13415 * Host::autoCaptureUSBDevices().
13416 *
13417 * Called by Console from the VM process upon VM startup.
13418 *
13419 * @note Locks what called methods lock.
13420 */
13421HRESULT SessionMachine::autoCaptureUSBDevices()
13422{
13423 LogFlowThisFunc(("\n"));
13424
13425#ifdef VBOX_WITH_USB
13426 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13427 AssertComRC(hrc);
13428 NOREF(hrc);
13429
13430 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13431 AssertReturn(service, E_FAIL);
13432 return service->autoCaptureDevicesForVM(this);
13433#else
13434 return S_OK;
13435#endif
13436}
13437
13438/**
13439 * Removes all machine filters from the USB proxy service and then calls
13440 * Host::detachAllUSBDevices().
13441 *
13442 * Called by Console from the VM process upon normal VM termination or by
13443 * SessionMachine::uninit() upon abnormal VM termination (from under the
13444 * Machine/SessionMachine lock).
13445 *
13446 * @note Locks what called methods lock.
13447 */
13448HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13449{
13450 LogFlowThisFunc(("\n"));
13451
13452#ifdef VBOX_WITH_USB
13453 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13454 AssertComRC(hrc);
13455 NOREF(hrc);
13456
13457 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13458 AssertReturn(service, E_FAIL);
13459 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13460#else
13461 NOREF(aDone);
13462 return S_OK;
13463#endif
13464}
13465
13466/**
13467 * @note Locks this object for writing.
13468 */
13469HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13470 ComPtr<IProgress> &aProgress)
13471{
13472 LogFlowThisFuncEnter();
13473
13474 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13475 /*
13476 * We don't assert below because it might happen that a non-direct session
13477 * informs us it is closed right after we've been uninitialized -- it's ok.
13478 */
13479
13480 /* get IInternalSessionControl interface */
13481 ComPtr<IInternalSessionControl> control(aSession);
13482
13483 ComAssertRet(!control.isNull(), E_INVALIDARG);
13484
13485 /* Creating a Progress object requires the VirtualBox lock, and
13486 * thus locking it here is required by the lock order rules. */
13487 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13488
13489 if (control == mData->mSession.mDirectControl)
13490 {
13491 /* The direct session is being normally closed by the client process
13492 * ----------------------------------------------------------------- */
13493
13494 /* go to the closing state (essential for all open*Session() calls and
13495 * for #i_checkForDeath()) */
13496 Assert(mData->mSession.mState == SessionState_Locked);
13497 mData->mSession.mState = SessionState_Unlocking;
13498
13499 /* set direct control to NULL to release the remote instance */
13500 mData->mSession.mDirectControl.setNull();
13501 LogFlowThisFunc(("Direct control is set to NULL\n"));
13502
13503 if (mData->mSession.mProgress)
13504 {
13505 /* finalize the progress, someone might wait if a frontend
13506 * closes the session before powering on the VM. */
13507 mData->mSession.mProgress->notifyComplete(E_FAIL,
13508 COM_IIDOF(ISession),
13509 getComponentName(),
13510 tr("The VM session was closed before any attempt to power it on"));
13511 mData->mSession.mProgress.setNull();
13512 }
13513
13514 /* Create the progress object the client will use to wait until
13515 * #i_checkForDeath() is called to uninitialize this session object after
13516 * it releases the IPC semaphore.
13517 * Note! Because we're "reusing" mProgress here, this must be a proxy
13518 * object just like for LaunchVMProcess. */
13519 Assert(mData->mSession.mProgress.isNull());
13520 ComObjPtr<ProgressProxy> progress;
13521 progress.createObject();
13522 ComPtr<IUnknown> pPeer(mPeer);
13523 progress->init(mParent, pPeer,
13524 Bstr(tr("Closing session")).raw(),
13525 FALSE /* aCancelable */);
13526 progress.queryInterfaceTo(aProgress.asOutParam());
13527 mData->mSession.mProgress = progress;
13528 }
13529 else
13530 {
13531 /* the remote session is being normally closed */
13532 bool found = false;
13533 for (Data::Session::RemoteControlList::iterator
13534 it = mData->mSession.mRemoteControls.begin();
13535 it != mData->mSession.mRemoteControls.end();
13536 ++it)
13537 {
13538 if (control == *it)
13539 {
13540 found = true;
13541 // This MUST be erase(it), not remove(*it) as the latter
13542 // triggers a very nasty use after free due to the place where
13543 // the value "lives".
13544 mData->mSession.mRemoteControls.erase(it);
13545 break;
13546 }
13547 }
13548 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13549 E_INVALIDARG);
13550 }
13551
13552 /* signal the client watcher thread, because the client is going away */
13553 mParent->i_updateClientWatcher();
13554
13555 LogFlowThisFuncLeave();
13556 return S_OK;
13557}
13558
13559HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13560 std::vector<com::Utf8Str> &aValues,
13561 std::vector<LONG64> &aTimestamps,
13562 std::vector<com::Utf8Str> &aFlags)
13563{
13564 LogFlowThisFunc(("\n"));
13565
13566#ifdef VBOX_WITH_GUEST_PROPS
13567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13568
13569 size_t cEntries = mHWData->mGuestProperties.size();
13570 aNames.resize(cEntries);
13571 aValues.resize(cEntries);
13572 aTimestamps.resize(cEntries);
13573 aFlags.resize(cEntries);
13574
13575 size_t i = 0;
13576 for (HWData::GuestPropertyMap::const_iterator
13577 it = mHWData->mGuestProperties.begin();
13578 it != mHWData->mGuestProperties.end();
13579 ++it, ++i)
13580 {
13581 aNames[i] = it->first;
13582 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
13583 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13584
13585 aValues[i] = it->second.strValue;
13586 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
13587 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13588
13589 aTimestamps[i] = it->second.mTimestamp;
13590
13591 /* If it is NULL, keep it NULL. */
13592 if (it->second.mFlags)
13593 {
13594 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13595 GuestPropWriteFlags(it->second.mFlags, szFlags);
13596 aFlags[i] = szFlags;
13597 }
13598 else
13599 aFlags[i] = "";
13600 }
13601 return S_OK;
13602#else
13603 ReturnComNotImplemented();
13604#endif
13605}
13606
13607HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13608 const com::Utf8Str &aValue,
13609 LONG64 aTimestamp,
13610 const com::Utf8Str &aFlags,
13611 BOOL fWasDeleted)
13612{
13613 LogFlowThisFunc(("\n"));
13614
13615#ifdef VBOX_WITH_GUEST_PROPS
13616 try
13617 {
13618 /*
13619 * Convert input up front.
13620 */
13621 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13622 if (aFlags.length())
13623 {
13624 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13625 AssertRCReturn(vrc, E_INVALIDARG);
13626 }
13627
13628 /*
13629 * Now grab the object lock, validate the state and do the update.
13630 */
13631
13632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13633
13634 if (!Global::IsOnline(mData->mMachineState))
13635 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13636
13637 i_setModified(IsModified_MachineData);
13638 mHWData.backup();
13639
13640 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13641 if (it != mHWData->mGuestProperties.end())
13642 {
13643 if (!fWasDeleted)
13644 {
13645 it->second.strValue = aValue;
13646 it->second.mTimestamp = aTimestamp;
13647 it->second.mFlags = fFlags;
13648 }
13649 else
13650 mHWData->mGuestProperties.erase(it);
13651
13652 mData->mGuestPropertiesModified = TRUE;
13653 }
13654 else if (!fWasDeleted)
13655 {
13656 HWData::GuestProperty prop;
13657 prop.strValue = aValue;
13658 prop.mTimestamp = aTimestamp;
13659 prop.mFlags = fFlags;
13660
13661 mHWData->mGuestProperties[aName] = prop;
13662 mData->mGuestPropertiesModified = TRUE;
13663 }
13664
13665 alock.release();
13666
13667 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13668 }
13669 catch (...)
13670 {
13671 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13672 }
13673 return S_OK;
13674#else
13675 ReturnComNotImplemented();
13676#endif
13677}
13678
13679
13680HRESULT SessionMachine::lockMedia()
13681{
13682 AutoMultiWriteLock2 alock(this->lockHandle(),
13683 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13684
13685 AssertReturn( mData->mMachineState == MachineState_Starting
13686 || mData->mMachineState == MachineState_Restoring
13687 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13688
13689 clearError();
13690 alock.release();
13691 return i_lockMedia();
13692}
13693
13694HRESULT SessionMachine::unlockMedia()
13695{
13696 HRESULT hrc = i_unlockMedia();
13697 return hrc;
13698}
13699
13700HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13701 ComPtr<IMediumAttachment> &aNewAttachment)
13702{
13703 // request the host lock first, since might be calling Host methods for getting host drives;
13704 // next, protect the media tree all the while we're in here, as well as our member variables
13705 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13706 this->lockHandle(),
13707 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13708
13709 IMediumAttachment *iAttach = aAttachment;
13710 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13711
13712 Utf8Str ctrlName;
13713 LONG lPort;
13714 LONG lDevice;
13715 bool fTempEject;
13716 {
13717 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13718
13719 /* Need to query the details first, as the IMediumAttachment reference
13720 * might be to the original settings, which we are going to change. */
13721 ctrlName = pAttach->i_getControllerName();
13722 lPort = pAttach->i_getPort();
13723 lDevice = pAttach->i_getDevice();
13724 fTempEject = pAttach->i_getTempEject();
13725 }
13726
13727 if (!fTempEject)
13728 {
13729 /* Remember previously mounted medium. The medium before taking the
13730 * backup is not necessarily the same thing. */
13731 ComObjPtr<Medium> oldmedium;
13732 oldmedium = pAttach->i_getMedium();
13733
13734 i_setModified(IsModified_Storage);
13735 mMediumAttachments.backup();
13736
13737 // The backup operation makes the pAttach reference point to the
13738 // old settings. Re-get the correct reference.
13739 pAttach = i_findAttachment(*mMediumAttachments.data(),
13740 ctrlName,
13741 lPort,
13742 lDevice);
13743
13744 {
13745 AutoCaller autoAttachCaller(this);
13746 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
13747
13748 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13749 if (!oldmedium.isNull())
13750 oldmedium->i_removeBackReference(mData->mUuid);
13751
13752 pAttach->i_updateMedium(NULL);
13753 pAttach->i_updateEjected();
13754 }
13755
13756 i_setModified(IsModified_Storage);
13757 }
13758 else
13759 {
13760 {
13761 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13762 pAttach->i_updateEjected();
13763 }
13764 }
13765
13766 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13767
13768 return S_OK;
13769}
13770
13771HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13772 com::Utf8Str &aResult)
13773{
13774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13775
13776 HRESULT hrc = S_OK;
13777
13778 if (!mAuthLibCtx.hAuthLibrary)
13779 {
13780 /* Load the external authentication library. */
13781 Bstr authLibrary;
13782 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13783
13784 Utf8Str filename = authLibrary;
13785
13786 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13787 if (RT_FAILURE(vrc))
13788 hrc = setErrorBoth(E_FAIL, vrc,
13789 tr("Could not load the external authentication library '%s' (%Rrc)"),
13790 filename.c_str(), vrc);
13791 }
13792
13793 /* The auth library might need the machine lock. */
13794 alock.release();
13795
13796 if (FAILED(hrc))
13797 return hrc;
13798
13799 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13800 {
13801 enum VRDEAuthParams
13802 {
13803 parmUuid = 1,
13804 parmGuestJudgement,
13805 parmUser,
13806 parmPassword,
13807 parmDomain,
13808 parmClientId
13809 };
13810
13811 AuthResult result = AuthResultAccessDenied;
13812
13813 Guid uuid(aAuthParams[parmUuid]);
13814 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13815 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13816
13817 result = AuthLibAuthenticate(&mAuthLibCtx,
13818 uuid.raw(), guestJudgement,
13819 aAuthParams[parmUser].c_str(),
13820 aAuthParams[parmPassword].c_str(),
13821 aAuthParams[parmDomain].c_str(),
13822 u32ClientId);
13823
13824 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13825 size_t cbPassword = aAuthParams[parmPassword].length();
13826 if (cbPassword)
13827 {
13828 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13829 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13830 }
13831
13832 if (result == AuthResultAccessGranted)
13833 aResult = "granted";
13834 else
13835 aResult = "denied";
13836
13837 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13838 aAuthParams[parmUser].c_str(), aResult.c_str()));
13839 }
13840 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13841 {
13842 enum VRDEAuthDisconnectParams
13843 {
13844 parmUuid = 1,
13845 parmClientId
13846 };
13847
13848 Guid uuid(aAuthParams[parmUuid]);
13849 uint32_t u32ClientId = 0;
13850 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13851 }
13852 else
13853 {
13854 hrc = E_INVALIDARG;
13855 }
13856
13857 return hrc;
13858}
13859
13860// public methods only for internal purposes
13861/////////////////////////////////////////////////////////////////////////////
13862
13863#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13864/**
13865 * Called from the client watcher thread to check for expected or unexpected
13866 * death of the client process that has a direct session to this machine.
13867 *
13868 * On Win32 and on OS/2, this method is called only when we've got the
13869 * mutex (i.e. the client has either died or terminated normally) so it always
13870 * returns @c true (the client is terminated, the session machine is
13871 * uninitialized).
13872 *
13873 * On other platforms, the method returns @c true if the client process has
13874 * terminated normally or abnormally and the session machine was uninitialized,
13875 * and @c false if the client process is still alive.
13876 *
13877 * @note Locks this object for writing.
13878 */
13879bool SessionMachine::i_checkForDeath()
13880{
13881 Uninit::Reason reason;
13882 bool terminated = false;
13883
13884 /* Enclose autoCaller with a block because calling uninit() from under it
13885 * will deadlock. */
13886 {
13887 AutoCaller autoCaller(this);
13888 if (!autoCaller.isOk())
13889 {
13890 /* return true if not ready, to cause the client watcher to exclude
13891 * the corresponding session from watching */
13892 LogFlowThisFunc(("Already uninitialized!\n"));
13893 return true;
13894 }
13895
13896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13897
13898 /* Determine the reason of death: if the session state is Closing here,
13899 * everything is fine. Otherwise it means that the client did not call
13900 * OnSessionEnd() before it released the IPC semaphore. This may happen
13901 * either because the client process has abnormally terminated, or
13902 * because it simply forgot to call ISession::Close() before exiting. We
13903 * threat the latter also as an abnormal termination (see
13904 * Session::uninit() for details). */
13905 reason = mData->mSession.mState == SessionState_Unlocking ?
13906 Uninit::Normal :
13907 Uninit::Abnormal;
13908
13909 if (mClientToken)
13910 terminated = mClientToken->release();
13911 } /* AutoCaller block */
13912
13913 if (terminated)
13914 uninit(reason);
13915
13916 return terminated;
13917}
13918
13919void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13920{
13921 LogFlowThisFunc(("\n"));
13922
13923 strTokenId.setNull();
13924
13925 AutoCaller autoCaller(this);
13926 AssertComRCReturnVoid(autoCaller.hrc());
13927
13928 Assert(mClientToken);
13929 if (mClientToken)
13930 mClientToken->getId(strTokenId);
13931}
13932#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13933IToken *SessionMachine::i_getToken()
13934{
13935 LogFlowThisFunc(("\n"));
13936
13937 AutoCaller autoCaller(this);
13938 AssertComRCReturn(autoCaller.hrc(), NULL);
13939
13940 Assert(mClientToken);
13941 if (mClientToken)
13942 return mClientToken->getToken();
13943 else
13944 return NULL;
13945}
13946#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13947
13948Machine::ClientToken *SessionMachine::i_getClientToken()
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.hrc(), NULL);
13954
13955 return mClientToken;
13956}
13957
13958
13959/**
13960 * @note Locks this object for reading.
13961 */
13962HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13968
13969 ComPtr<IInternalSessionControl> directControl;
13970 {
13971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13972 if (mData->mSession.mLockType == LockType_VM)
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 /* ignore notifications sent after #OnSessionEnd() is called */
13977 if (!directControl)
13978 return S_OK;
13979
13980 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13981}
13982
13983/**
13984 * @note Locks this object for reading.
13985 */
13986HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13987 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13988 const Utf8Str &aGuestIp, LONG aGuestPort)
13989{
13990 LogFlowThisFunc(("\n"));
13991
13992 AutoCaller autoCaller(this);
13993 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13994
13995 ComPtr<IInternalSessionControl> directControl;
13996 {
13997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13998 if (mData->mSession.mLockType == LockType_VM)
13999 directControl = mData->mSession.mDirectControl;
14000 }
14001
14002 /* ignore notifications sent after #OnSessionEnd() is called */
14003 if (!directControl)
14004 return S_OK;
14005 /*
14006 * instead acting like callback we ask IVirtualBox deliver corresponding event
14007 */
14008
14009 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14010 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14011 return S_OK;
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 if (mData->mSession.mLockType == LockType_VM)
14028 directControl = mData->mSession.mDirectControl;
14029 }
14030
14031 /* ignore notifications sent after #OnSessionEnd() is called */
14032 if (!directControl)
14033 return S_OK;
14034
14035 return directControl->OnAudioAdapterChange(audioAdapter);
14036}
14037
14038/**
14039 * @note Locks this object for reading.
14040 */
14041HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14042{
14043 LogFlowThisFunc(("\n"));
14044
14045 AutoCaller autoCaller(this);
14046 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14047
14048 ComPtr<IInternalSessionControl> directControl;
14049 {
14050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14051 if (mData->mSession.mLockType == LockType_VM)
14052 directControl = mData->mSession.mDirectControl;
14053 }
14054
14055 /* ignore notifications sent after #OnSessionEnd() is called */
14056 if (!directControl)
14057 return S_OK;
14058
14059 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14060}
14061
14062/**
14063 * @note Locks this object for reading.
14064 */
14065HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14066{
14067 LogFlowThisFunc(("\n"));
14068
14069 AutoCaller autoCaller(this);
14070 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14071
14072 ComPtr<IInternalSessionControl> directControl;
14073 {
14074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14075 if (mData->mSession.mLockType == LockType_VM)
14076 directControl = mData->mSession.mDirectControl;
14077 }
14078
14079 /* ignore notifications sent after #OnSessionEnd() is called */
14080 if (!directControl)
14081 return S_OK;
14082
14083 return directControl->OnSerialPortChange(serialPort);
14084}
14085
14086/**
14087 * @note Locks this object for reading.
14088 */
14089HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14095
14096 ComPtr<IInternalSessionControl> directControl;
14097 {
14098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14099 if (mData->mSession.mLockType == LockType_VM)
14100 directControl = mData->mSession.mDirectControl;
14101 }
14102
14103 /* ignore notifications sent after #OnSessionEnd() is called */
14104 if (!directControl)
14105 return S_OK;
14106
14107 return directControl->OnParallelPortChange(parallelPort);
14108}
14109
14110/**
14111 * @note Locks this object for reading.
14112 */
14113HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 if (mData->mSession.mLockType == LockType_VM)
14124 directControl = mData->mSession.mDirectControl;
14125 }
14126
14127 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14128
14129 /* ignore notifications sent after #OnSessionEnd() is called */
14130 if (!directControl)
14131 return S_OK;
14132
14133 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14134}
14135
14136/**
14137 * @note Locks this object for reading.
14138 */
14139HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 if (mData->mSession.mLockType == LockType_VM)
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 mParent->i_onMediumChanged(aAttachment);
14154
14155 /* ignore notifications sent after #OnSessionEnd() is called */
14156 if (!directControl)
14157 return S_OK;
14158
14159 return directControl->OnMediumChange(aAttachment, aForce);
14160}
14161
14162HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnVMProcessPriorityChange(aPriority);
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 if (mData->mSession.mLockType == LockType_VM)
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnCPUChange(aCPU, aRemove);
14205}
14206
14207HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14213
14214 ComPtr<IInternalSessionControl> directControl;
14215 {
14216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14217 if (mData->mSession.mLockType == LockType_VM)
14218 directControl = mData->mSession.mDirectControl;
14219 }
14220
14221 /* ignore notifications sent after #OnSessionEnd() is called */
14222 if (!directControl)
14223 return S_OK;
14224
14225 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14226}
14227
14228/**
14229 * @note Locks this object for reading.
14230 */
14231HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14232{
14233 LogFlowThisFunc(("\n"));
14234
14235 AutoCaller autoCaller(this);
14236 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14237
14238 ComPtr<IInternalSessionControl> directControl;
14239 {
14240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14241 if (mData->mSession.mLockType == LockType_VM)
14242 directControl = mData->mSession.mDirectControl;
14243 }
14244
14245 /* ignore notifications sent after #OnSessionEnd() is called */
14246 if (!directControl)
14247 return S_OK;
14248
14249 return directControl->OnVRDEServerChange(aRestart);
14250}
14251
14252/**
14253 * @note Caller needs to take the machine's lock if needed.
14254 */
14255HRESULT SessionMachine::i_onRecordingStateChange(BOOL aEnable, IProgress **aProgress)
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 if (mData->mSession.mLockType == LockType_VM)
14265 directControl = mData->mSession.mDirectControl;
14266 }
14267
14268 /* ignore notifications sent after #OnSessionEnd() is called */
14269 if (!directControl)
14270 return S_OK;
14271
14272 return directControl->OnRecordingStateChange(aEnable, aProgress);
14273}
14274
14275/**
14276 * @note Locks this object for reading.
14277 */
14278HRESULT SessionMachine::i_onRecordingScreenStateChange(BOOL aEnable, ULONG aScreen)
14279{
14280 LogFlowThisFunc(("\n"));
14281
14282 AutoCaller autoCaller(this);
14283 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14284
14285 ComPtr<IInternalSessionControl> directControl;
14286 {
14287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14288 if (mData->mSession.mLockType == LockType_VM)
14289 directControl = mData->mSession.mDirectControl;
14290 }
14291
14292 /* ignore notifications sent after #OnSessionEnd() is called */
14293 if (!directControl)
14294 return S_OK;
14295
14296 return directControl->OnRecordingScreenStateChange(aEnable, aScreen);
14297}
14298
14299/**
14300 * @note Locks this object for reading.
14301 */
14302HRESULT SessionMachine::i_onUSBControllerChange()
14303{
14304 LogFlowThisFunc(("\n"));
14305
14306 AutoCaller autoCaller(this);
14307 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14308
14309 ComPtr<IInternalSessionControl> directControl;
14310 {
14311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14312 if (mData->mSession.mLockType == LockType_VM)
14313 directControl = mData->mSession.mDirectControl;
14314 }
14315
14316 /* ignore notifications sent after #OnSessionEnd() is called */
14317 if (!directControl)
14318 return S_OK;
14319
14320 return directControl->OnUSBControllerChange();
14321}
14322
14323/**
14324 * @note Locks this object for reading.
14325 */
14326HRESULT SessionMachine::i_onSharedFolderChange()
14327{
14328 LogFlowThisFunc(("\n"));
14329
14330 AutoCaller autoCaller(this);
14331 AssertComRCReturnRC(autoCaller.hrc());
14332
14333 ComPtr<IInternalSessionControl> directControl;
14334 {
14335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14336 if (mData->mSession.mLockType == LockType_VM)
14337 directControl = mData->mSession.mDirectControl;
14338 }
14339
14340 /* ignore notifications sent after #OnSessionEnd() is called */
14341 if (!directControl)
14342 return S_OK;
14343
14344 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14345}
14346
14347/**
14348 * @note Locks this object for reading.
14349 */
14350HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14351{
14352 LogFlowThisFunc(("\n"));
14353
14354 AutoCaller autoCaller(this);
14355 AssertComRCReturnRC(autoCaller.hrc());
14356
14357 ComPtr<IInternalSessionControl> directControl;
14358 {
14359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14360 if (mData->mSession.mLockType == LockType_VM)
14361 directControl = mData->mSession.mDirectControl;
14362 }
14363
14364 /* ignore notifications sent after #OnSessionEnd() is called */
14365 if (!directControl)
14366 return S_OK;
14367
14368 return directControl->OnClipboardModeChange(aClipboardMode);
14369}
14370
14371/**
14372 * @note Locks this object for reading.
14373 */
14374HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14375{
14376 LogFlowThisFunc(("\n"));
14377
14378 AutoCaller autoCaller(this);
14379 AssertComRCReturnRC(autoCaller.hrc());
14380
14381 ComPtr<IInternalSessionControl> directControl;
14382 {
14383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14384 if (mData->mSession.mLockType == LockType_VM)
14385 directControl = mData->mSession.mDirectControl;
14386 }
14387
14388 /* ignore notifications sent after #OnSessionEnd() is called */
14389 if (!directControl)
14390 return S_OK;
14391
14392 return directControl->OnClipboardFileTransferModeChange(aEnable);
14393}
14394
14395/**
14396 * @note Locks this object for reading.
14397 */
14398HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14399{
14400 LogFlowThisFunc(("\n"));
14401
14402 AutoCaller autoCaller(this);
14403 AssertComRCReturnRC(autoCaller.hrc());
14404
14405 ComPtr<IInternalSessionControl> directControl;
14406 {
14407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14408 if (mData->mSession.mLockType == LockType_VM)
14409 directControl = mData->mSession.mDirectControl;
14410 }
14411
14412 /* ignore notifications sent after #OnSessionEnd() is called */
14413 if (!directControl)
14414 return S_OK;
14415
14416 return directControl->OnDnDModeChange(aDnDMode);
14417}
14418
14419/**
14420 * @note Locks this object for reading.
14421 */
14422HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14423{
14424 LogFlowThisFunc(("\n"));
14425
14426 AutoCaller autoCaller(this);
14427 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14428
14429 ComPtr<IInternalSessionControl> directControl;
14430 {
14431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14432 if (mData->mSession.mLockType == LockType_VM)
14433 directControl = mData->mSession.mDirectControl;
14434 }
14435
14436 /* ignore notifications sent after #OnSessionEnd() is called */
14437 if (!directControl)
14438 return S_OK;
14439
14440 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14441}
14442
14443/**
14444 * @note Locks this object for reading.
14445 */
14446HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14447{
14448 LogFlowThisFunc(("\n"));
14449
14450 AutoCaller autoCaller(this);
14451 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14452
14453 ComPtr<IInternalSessionControl> directControl;
14454 {
14455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14456 if (mData->mSession.mLockType == LockType_VM)
14457 directControl = mData->mSession.mDirectControl;
14458 }
14459
14460 /* ignore notifications sent after #OnSessionEnd() is called */
14461 if (!directControl)
14462 return S_OK;
14463
14464 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14465}
14466
14467/**
14468 * @note Locks this object for reading.
14469 */
14470HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14471{
14472 LogFlowThisFunc(("\n"));
14473
14474 AutoCaller autoCaller(this);
14475 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14476
14477 ComPtr<IInternalSessionControl> directControl;
14478 {
14479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14480 if (mData->mSession.mLockType == LockType_VM)
14481 directControl = mData->mSession.mDirectControl;
14482 }
14483
14484 /* ignore notifications sent after #OnSessionEnd() is called */
14485 if (!directControl)
14486 return S_OK;
14487
14488 return directControl->OnGuestDebugControlChange(guestDebugControl);
14489}
14490
14491/**
14492 * Returns @c true if this machine's USB controller reports it has a matching
14493 * filter for the given USB device and @c false otherwise.
14494 *
14495 * @note locks this object for reading.
14496 */
14497bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14498{
14499 AutoCaller autoCaller(this);
14500 /* silently return if not ready -- this method may be called after the
14501 * direct machine session has been called */
14502 if (!autoCaller.isOk())
14503 return false;
14504
14505#ifdef VBOX_WITH_USB
14506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14507
14508 switch (mData->mMachineState)
14509 {
14510 case MachineState_Starting:
14511 case MachineState_Restoring:
14512 case MachineState_TeleportingIn:
14513 case MachineState_Paused:
14514 case MachineState_Running:
14515 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14516 * elsewhere... */
14517 alock.release();
14518 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14519 default: break;
14520 }
14521#else
14522 NOREF(aDevice);
14523 NOREF(aMaskedIfs);
14524#endif
14525 return false;
14526}
14527
14528/**
14529 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14530 */
14531HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14532 IVirtualBoxErrorInfo *aError,
14533 ULONG aMaskedIfs,
14534 const com::Utf8Str &aCaptureFilename)
14535{
14536 LogFlowThisFunc(("\n"));
14537
14538 AutoCaller autoCaller(this);
14539
14540 /* This notification may happen after the machine object has been
14541 * uninitialized (the session was closed), so don't assert. */
14542 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14543
14544 ComPtr<IInternalSessionControl> directControl;
14545 {
14546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14547 if (mData->mSession.mLockType == LockType_VM)
14548 directControl = mData->mSession.mDirectControl;
14549 }
14550
14551 /* fail on notifications sent after #OnSessionEnd() is called, it is
14552 * expected by the caller */
14553 if (!directControl)
14554 return E_FAIL;
14555
14556 /* No locks should be held at this point. */
14557 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14558 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14559
14560 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14561}
14562
14563/**
14564 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14565 */
14566HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14567 IVirtualBoxErrorInfo *aError)
14568{
14569 LogFlowThisFunc(("\n"));
14570
14571 AutoCaller autoCaller(this);
14572
14573 /* This notification may happen after the machine object has been
14574 * uninitialized (the session was closed), so don't assert. */
14575 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14576
14577 ComPtr<IInternalSessionControl> directControl;
14578 {
14579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14580 if (mData->mSession.mLockType == LockType_VM)
14581 directControl = mData->mSession.mDirectControl;
14582 }
14583
14584 /* fail on notifications sent after #OnSessionEnd() is called, it is
14585 * expected by the caller */
14586 if (!directControl)
14587 return E_FAIL;
14588
14589 /* No locks should be held at this point. */
14590 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14591 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14592
14593 return directControl->OnUSBDeviceDetach(aId, aError);
14594}
14595
14596// protected methods
14597/////////////////////////////////////////////////////////////////////////////
14598
14599/**
14600 * Deletes the given file if it is no longer in use by either the current machine state
14601 * (if the machine is "saved") or any of the machine's snapshots.
14602 *
14603 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14604 * but is different for each SnapshotMachine. When calling this, the order of calling this
14605 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14606 * is therefore critical. I know, it's all rather messy.
14607 *
14608 * @param strStateFile
14609 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14610 * the test for whether the saved state file is in use.
14611 */
14612void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14613 Snapshot *pSnapshotToIgnore)
14614{
14615 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14616 if ( (strStateFile.isNotEmpty())
14617 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14618 )
14619 // ... and it must also not be shared with other snapshots
14620 if ( !mData->mFirstSnapshot
14621 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14622 // this checks the SnapshotMachine's state file paths
14623 )
14624 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
14625}
14626
14627/**
14628 * Locks the attached media.
14629 *
14630 * All attached hard disks are locked for writing and DVD/floppy are locked for
14631 * reading. Parents of attached hard disks (if any) are locked for reading.
14632 *
14633 * This method also performs accessibility check of all media it locks: if some
14634 * media is inaccessible, the method will return a failure and a bunch of
14635 * extended error info objects per each inaccessible medium.
14636 *
14637 * Note that this method is atomic: if it returns a success, all media are
14638 * locked as described above; on failure no media is locked at all (all
14639 * succeeded individual locks will be undone).
14640 *
14641 * The caller is responsible for doing the necessary state sanity checks.
14642 *
14643 * The locks made by this method must be undone by calling #unlockMedia() when
14644 * no more needed.
14645 */
14646HRESULT SessionMachine::i_lockMedia()
14647{
14648 AutoCaller autoCaller(this);
14649 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14650
14651 AutoMultiWriteLock2 alock(this->lockHandle(),
14652 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14653
14654 /* bail out if trying to lock things with already set up locking */
14655 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14656
14657 MultiResult hrcMult(S_OK);
14658
14659 /* Collect locking information for all medium objects attached to the VM. */
14660 for (MediumAttachmentList::const_iterator
14661 it = mMediumAttachments->begin();
14662 it != mMediumAttachments->end();
14663 ++it)
14664 {
14665 MediumAttachment *pAtt = *it;
14666 DeviceType_T devType = pAtt->i_getType();
14667 Medium *pMedium = pAtt->i_getMedium();
14668
14669 MediumLockList *pMediumLockList(new MediumLockList());
14670 // There can be attachments without a medium (floppy/dvd), and thus
14671 // it's impossible to create a medium lock list. It still makes sense
14672 // to have the empty medium lock list in the map in case a medium is
14673 // attached later.
14674 if (pMedium != NULL)
14675 {
14676 MediumType_T mediumType = pMedium->i_getType();
14677 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14678 || mediumType == MediumType_Shareable;
14679 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14680
14681 alock.release();
14682 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14683 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14684 false /* fMediumLockWriteAll */,
14685 NULL,
14686 *pMediumLockList);
14687 alock.acquire();
14688 if (FAILED(hrcMult))
14689 {
14690 delete pMediumLockList;
14691 mData->mSession.mLockedMedia.Clear();
14692 break;
14693 }
14694 }
14695
14696 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14697 if (FAILED(hrc))
14698 {
14699 mData->mSession.mLockedMedia.Clear();
14700 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
14701 break;
14702 }
14703 }
14704
14705 if (SUCCEEDED(hrcMult))
14706 {
14707 /* Now lock all media. If this fails, nothing is locked. */
14708 alock.release();
14709 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
14710 alock.acquire();
14711 if (FAILED(hrc))
14712 hrcMult = setError(hrc,
14713 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14714 }
14715
14716 return hrcMult;
14717}
14718
14719/**
14720 * Undoes the locks made by by #lockMedia().
14721 */
14722HRESULT SessionMachine::i_unlockMedia()
14723{
14724 AutoCaller autoCaller(this);
14725 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
14726
14727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14728
14729 /* we may be holding important error info on the current thread;
14730 * preserve it */
14731 ErrorInfoKeeper eik;
14732
14733 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
14734 AssertComRC(hrc);
14735 return hrc;
14736}
14737
14738/**
14739 * Helper to change the machine state (reimplementation).
14740 *
14741 * @note Locks this object for writing.
14742 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14743 * it can cause crashes in random places due to unexpectedly committing
14744 * the current settings. The caller is responsible for that. The call
14745 * to saveStateSettings is fine, because this method does not commit.
14746 */
14747HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14748{
14749 LogFlowThisFuncEnter();
14750
14751 AutoCaller autoCaller(this);
14752 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14753
14754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14755
14756 MachineState_T oldMachineState = mData->mMachineState;
14757
14758 AssertMsgReturn(oldMachineState != aMachineState,
14759 ("oldMachineState=%s, aMachineState=%s\n",
14760 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14761 E_FAIL);
14762
14763 HRESULT hrc = S_OK;
14764
14765 int stsFlags = 0;
14766 bool deleteSavedState = false;
14767
14768 /* detect some state transitions */
14769
14770 if ( ( ( oldMachineState == MachineState_Saved
14771 || oldMachineState == MachineState_AbortedSaved
14772 )
14773 && aMachineState == MachineState_Restoring
14774 )
14775 || ( ( oldMachineState == MachineState_PoweredOff
14776 || oldMachineState == MachineState_Teleported
14777 || oldMachineState == MachineState_Aborted
14778 )
14779 && ( aMachineState == MachineState_TeleportingIn
14780 || aMachineState == MachineState_Starting
14781 )
14782 )
14783 )
14784 {
14785 /* The EMT thread is about to start */
14786
14787 /* Nothing to do here for now... */
14788
14789 /// @todo NEWMEDIA don't let mDVDDrive and other children
14790 /// change anything when in the Starting/Restoring state
14791 }
14792 else if ( ( oldMachineState == MachineState_Running
14793 || oldMachineState == MachineState_Paused
14794 || oldMachineState == MachineState_Teleporting
14795 || oldMachineState == MachineState_OnlineSnapshotting
14796 || oldMachineState == MachineState_LiveSnapshotting
14797 || oldMachineState == MachineState_Stuck
14798 || oldMachineState == MachineState_Starting
14799 || oldMachineState == MachineState_Stopping
14800 || oldMachineState == MachineState_Saving
14801 || oldMachineState == MachineState_Restoring
14802 || oldMachineState == MachineState_TeleportingPausedVM
14803 || oldMachineState == MachineState_TeleportingIn
14804 )
14805 && ( aMachineState == MachineState_PoweredOff
14806 || aMachineState == MachineState_Saved
14807 || aMachineState == MachineState_Teleported
14808 || aMachineState == MachineState_Aborted
14809 || aMachineState == MachineState_AbortedSaved
14810 )
14811 )
14812 {
14813 /* The EMT thread has just stopped, unlock attached media. Note that as
14814 * opposed to locking that is done from Console, we do unlocking here
14815 * because the VM process may have aborted before having a chance to
14816 * properly unlock all media it locked. */
14817
14818 unlockMedia();
14819 }
14820
14821 if (oldMachineState == MachineState_Restoring)
14822 {
14823 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14824 {
14825 /*
14826 * delete the saved state file once the machine has finished
14827 * restoring from it (note that Console sets the state from
14828 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14829 * to give the user an ability to fix an error and retry --
14830 * we keep the saved state file in this case)
14831 */
14832 deleteSavedState = true;
14833 }
14834 }
14835 else if ( ( oldMachineState == MachineState_Saved
14836 || oldMachineState == MachineState_AbortedSaved
14837 )
14838 && ( aMachineState == MachineState_PoweredOff
14839 || aMachineState == MachineState_Teleported
14840 )
14841 )
14842 {
14843 /* delete the saved state after SessionMachine::discardSavedState() is called */
14844 deleteSavedState = true;
14845 mData->mCurrentStateModified = TRUE;
14846 stsFlags |= SaveSTS_CurStateModified;
14847 }
14848 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14849 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14850
14851 if ( aMachineState == MachineState_Starting
14852 || aMachineState == MachineState_Restoring
14853 || aMachineState == MachineState_TeleportingIn
14854 )
14855 {
14856 /* set the current state modified flag to indicate that the current
14857 * state is no more identical to the state in the
14858 * current snapshot */
14859 if (!mData->mCurrentSnapshot.isNull())
14860 {
14861 mData->mCurrentStateModified = TRUE;
14862 stsFlags |= SaveSTS_CurStateModified;
14863 }
14864 }
14865
14866 if (deleteSavedState)
14867 {
14868 if (mRemoveSavedState)
14869 {
14870 Assert(!mSSData->strStateFilePath.isEmpty());
14871
14872 // it is safe to delete the saved state file if ...
14873 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14874 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14875 // ... none of the snapshots share the saved state file
14876 )
14877 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
14878 }
14879
14880 mSSData->strStateFilePath.setNull();
14881 stsFlags |= SaveSTS_StateFilePath;
14882 }
14883
14884 /* redirect to the underlying peer machine */
14885 mPeer->i_setMachineState(aMachineState);
14886
14887 if ( oldMachineState != MachineState_RestoringSnapshot
14888 && ( aMachineState == MachineState_PoweredOff
14889 || aMachineState == MachineState_Teleported
14890 || aMachineState == MachineState_Aborted
14891 || aMachineState == MachineState_AbortedSaved
14892 || aMachineState == MachineState_Saved))
14893 {
14894 /* the machine has stopped execution
14895 * (or the saved state file was adopted) */
14896 stsFlags |= SaveSTS_StateTimeStamp;
14897 }
14898
14899 if ( ( oldMachineState == MachineState_PoweredOff
14900 || oldMachineState == MachineState_Aborted
14901 || oldMachineState == MachineState_Teleported
14902 )
14903 && aMachineState == MachineState_Saved)
14904 {
14905 /* the saved state file was adopted */
14906 Assert(!mSSData->strStateFilePath.isEmpty());
14907 stsFlags |= SaveSTS_StateFilePath;
14908 }
14909
14910#ifdef VBOX_WITH_GUEST_PROPS
14911 if ( aMachineState == MachineState_PoweredOff
14912 || aMachineState == MachineState_Aborted
14913 || aMachineState == MachineState_Teleported)
14914 {
14915 /* Make sure any transient guest properties get removed from the
14916 * property store on shutdown. */
14917 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14918
14919 /* remove it from the settings representation */
14920 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14921 for (settings::GuestPropertiesList::iterator
14922 it = llGuestProperties.begin();
14923 it != llGuestProperties.end();
14924 /*nothing*/)
14925 {
14926 const settings::GuestProperty &prop = *it;
14927 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14928 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14929 {
14930 it = llGuestProperties.erase(it);
14931 fNeedsSaving = true;
14932 }
14933 else
14934 {
14935 ++it;
14936 }
14937 }
14938
14939 /* Additionally remove it from the HWData representation. Required to
14940 * keep everything in sync, as this is what the API keeps using. */
14941 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14942 for (HWData::GuestPropertyMap::iterator
14943 it = llHWGuestProperties.begin();
14944 it != llHWGuestProperties.end();
14945 /*nothing*/)
14946 {
14947 uint32_t fFlags = it->second.mFlags;
14948 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14949 {
14950 /* iterator where we need to continue after the erase call
14951 * (C++03 is a fact still, and it doesn't return the iterator
14952 * which would allow continuing) */
14953 HWData::GuestPropertyMap::iterator it2 = it;
14954 ++it2;
14955 llHWGuestProperties.erase(it);
14956 it = it2;
14957 fNeedsSaving = true;
14958 }
14959 else
14960 {
14961 ++it;
14962 }
14963 }
14964
14965 if (fNeedsSaving)
14966 {
14967 mData->mCurrentStateModified = TRUE;
14968 stsFlags |= SaveSTS_CurStateModified;
14969 }
14970 }
14971#endif /* VBOX_WITH_GUEST_PROPS */
14972
14973 hrc = i_saveStateSettings(stsFlags);
14974
14975 if ( ( oldMachineState != MachineState_PoweredOff
14976 && oldMachineState != MachineState_Aborted
14977 && oldMachineState != MachineState_Teleported
14978 )
14979 && ( aMachineState == MachineState_PoweredOff
14980 || aMachineState == MachineState_Aborted
14981 || aMachineState == MachineState_Teleported
14982 )
14983 )
14984 {
14985 /* we've been shut down for any reason */
14986 /* no special action so far */
14987 }
14988
14989 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
14990 LogFlowThisFuncLeave();
14991 return hrc;
14992}
14993
14994/**
14995 * Sends the current machine state value to the VM process.
14996 *
14997 * @note Locks this object for reading, then calls a client process.
14998 */
14999HRESULT SessionMachine::i_updateMachineStateOnClient()
15000{
15001 AutoCaller autoCaller(this);
15002 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15003
15004 ComPtr<IInternalSessionControl> directControl;
15005 {
15006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15007 AssertReturn(!!mData, E_FAIL);
15008 if (mData->mSession.mLockType == LockType_VM)
15009 directControl = mData->mSession.mDirectControl;
15010
15011 /* directControl may be already set to NULL here in #OnSessionEnd()
15012 * called too early by the direct session process while there is still
15013 * some operation (like deleting the snapshot) in progress. The client
15014 * process in this case is waiting inside Session::close() for the
15015 * "end session" process object to complete, while #uninit() called by
15016 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15017 * operation to complete. For now, we accept this inconsistent behavior
15018 * and simply do nothing here. */
15019
15020 if (mData->mSession.mState == SessionState_Unlocking)
15021 return S_OK;
15022 }
15023
15024 /* ignore notifications sent after #OnSessionEnd() is called */
15025 if (!directControl)
15026 return S_OK;
15027
15028 return directControl->UpdateMachineState(mData->mMachineState);
15029}
15030
15031
15032/*static*/
15033HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15034{
15035 va_list args;
15036 va_start(args, pcszMsg);
15037 HRESULT hrc = setErrorInternalV(aResultCode,
15038 getStaticClassIID(),
15039 getStaticComponentName(),
15040 pcszMsg, args,
15041 false /* aWarning */,
15042 true /* aLogIt */);
15043 va_end(args);
15044 return hrc;
15045}
15046
15047
15048HRESULT Machine::updateState(MachineState_T aState)
15049{
15050 NOREF(aState);
15051 ReturnComNotImplemented();
15052}
15053
15054HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15055{
15056 NOREF(aProgress);
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::endPowerUp(LONG aResult)
15061{
15062 NOREF(aResult);
15063 ReturnComNotImplemented();
15064}
15065
15066HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15067{
15068 NOREF(aProgress);
15069 ReturnComNotImplemented();
15070}
15071
15072HRESULT Machine::endPoweringDown(LONG aResult,
15073 const com::Utf8Str &aErrMsg)
15074{
15075 NOREF(aResult);
15076 NOREF(aErrMsg);
15077 ReturnComNotImplemented();
15078}
15079
15080HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15081 BOOL *aMatched,
15082 ULONG *aMaskedInterfaces)
15083{
15084 NOREF(aDevice);
15085 NOREF(aMatched);
15086 NOREF(aMaskedInterfaces);
15087 ReturnComNotImplemented();
15088
15089}
15090
15091HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15092{
15093 NOREF(aId); NOREF(aCaptureFilename);
15094 ReturnComNotImplemented();
15095}
15096
15097HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15098 BOOL aDone)
15099{
15100 NOREF(aId);
15101 NOREF(aDone);
15102 ReturnComNotImplemented();
15103}
15104
15105HRESULT Machine::autoCaptureUSBDevices()
15106{
15107 ReturnComNotImplemented();
15108}
15109
15110HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15111{
15112 NOREF(aDone);
15113 ReturnComNotImplemented();
15114}
15115
15116HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15117 ComPtr<IProgress> &aProgress)
15118{
15119 NOREF(aSession);
15120 NOREF(aProgress);
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::finishOnlineMergeMedium()
15125{
15126 ReturnComNotImplemented();
15127}
15128
15129HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15130 std::vector<com::Utf8Str> &aValues,
15131 std::vector<LONG64> &aTimestamps,
15132 std::vector<com::Utf8Str> &aFlags)
15133{
15134 NOREF(aNames);
15135 NOREF(aValues);
15136 NOREF(aTimestamps);
15137 NOREF(aFlags);
15138 ReturnComNotImplemented();
15139}
15140
15141HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15142 const com::Utf8Str &aValue,
15143 LONG64 aTimestamp,
15144 const com::Utf8Str &aFlags,
15145 BOOL fWasDeleted)
15146{
15147 NOREF(aName);
15148 NOREF(aValue);
15149 NOREF(aTimestamp);
15150 NOREF(aFlags);
15151 NOREF(fWasDeleted);
15152 ReturnComNotImplemented();
15153}
15154
15155HRESULT Machine::lockMedia()
15156{
15157 ReturnComNotImplemented();
15158}
15159
15160HRESULT Machine::unlockMedia()
15161{
15162 ReturnComNotImplemented();
15163}
15164
15165HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15166 ComPtr<IMediumAttachment> &aNewAttachment)
15167{
15168 NOREF(aAttachment);
15169 NOREF(aNewAttachment);
15170 ReturnComNotImplemented();
15171}
15172
15173HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15174 ULONG aCpuUser,
15175 ULONG aCpuKernel,
15176 ULONG aCpuIdle,
15177 ULONG aMemTotal,
15178 ULONG aMemFree,
15179 ULONG aMemBalloon,
15180 ULONG aMemShared,
15181 ULONG aMemCache,
15182 ULONG aPagedTotal,
15183 ULONG aMemAllocTotal,
15184 ULONG aMemFreeTotal,
15185 ULONG aMemBalloonTotal,
15186 ULONG aMemSharedTotal,
15187 ULONG aVmNetRx,
15188 ULONG aVmNetTx)
15189{
15190 NOREF(aValidStats);
15191 NOREF(aCpuUser);
15192 NOREF(aCpuKernel);
15193 NOREF(aCpuIdle);
15194 NOREF(aMemTotal);
15195 NOREF(aMemFree);
15196 NOREF(aMemBalloon);
15197 NOREF(aMemShared);
15198 NOREF(aMemCache);
15199 NOREF(aPagedTotal);
15200 NOREF(aMemAllocTotal);
15201 NOREF(aMemFreeTotal);
15202 NOREF(aMemBalloonTotal);
15203 NOREF(aMemSharedTotal);
15204 NOREF(aVmNetRx);
15205 NOREF(aVmNetTx);
15206 ReturnComNotImplemented();
15207}
15208
15209HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15210 com::Utf8Str &aResult)
15211{
15212 NOREF(aAuthParams);
15213 NOREF(aResult);
15214 ReturnComNotImplemented();
15215}
15216
15217HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15218{
15219 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15220
15221 AutoCaller autoCaller(this);
15222 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15223
15224 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15225 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15226 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15227 if (FAILED(hrc)) return hrc;
15228
15229 NOREF(aFlags);
15230 com::Utf8Str osTypeId;
15231 ComObjPtr<GuestOSType> osType = NULL;
15232
15233 /* Get the guest os type as a string from the VB. */
15234 hrc = getOSTypeId(osTypeId);
15235 if (FAILED(hrc)) return hrc;
15236
15237 /* Get the os type obj that coresponds, can be used to get
15238 * the defaults for this guest OS. */
15239 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15240 if (FAILED(hrc)) return hrc;
15241
15242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15243
15244 mPlatform->i_applyDefaults(osType);
15245
15246 /* This one covers IOAPICEnabled. */
15247 mFirmwareSettings->i_applyDefaults(osType);
15248
15249 /* Initialize default record settings. */
15250 mRecordingSettings->i_applyDefaults();
15251
15252 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15253 if (FAILED(hrc)) return hrc;
15254
15255 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15256 if (FAILED(hrc)) return hrc;
15257
15258 /* Graphics stuff. */
15259 GraphicsControllerType_T graphicsController;
15260 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15261 if (FAILED(hrc)) return hrc;
15262
15263 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15264 if (FAILED(hrc)) return hrc;
15265
15266 ULONG vramSize;
15267 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15268 if (FAILED(hrc)) return hrc;
15269
15270 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15271 if (FAILED(hrc)) return hrc;
15272
15273 BOOL fAccelerate2DVideoEnabled;
15274 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15275 if (FAILED(hrc)) return hrc;
15276
15277 hrc = mGraphicsAdapter->SetFeature(GraphicsFeature_Acceleration2DVideo, fAccelerate2DVideoEnabled);
15278 if (FAILED(hrc))
15279 {
15280 if (hrc != VBOX_E_NOT_SUPPORTED)
15281 return hrc;
15282 }
15283
15284 BOOL fAccelerate3DEnabled;
15285 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15286 if (FAILED(hrc)) return hrc;
15287
15288 hrc = mGraphicsAdapter->SetFeature(GraphicsFeature_Acceleration2DVideo, fAccelerate3DEnabled);
15289 if (FAILED(hrc))
15290 {
15291 if (hrc != VBOX_E_NOT_SUPPORTED)
15292 return hrc;
15293 }
15294
15295 /* Apply network adapters defaults */
15296 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15297 mNetworkAdapters[slot]->i_applyDefaults(osType);
15298
15299 /* Apply serial port defaults */
15300 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15301 mSerialPorts[slot]->i_applyDefaults(osType);
15302
15303 /* Apply parallel port defaults - not OS dependent*/
15304 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15305 mParallelPorts[slot]->i_applyDefaults();
15306
15307 /* This one covers the TPM type. */
15308 mTrustedPlatformModule->i_applyDefaults(osType);
15309
15310 /* This one covers secure boot. */
15311 hrc = mNvramStore->i_applyDefaults(osType);
15312 if (FAILED(hrc)) return hrc;
15313
15314 /* Audio stuff. */
15315 hrc = mAudioSettings->i_applyDefaults(osType);
15316 if (FAILED(hrc)) return hrc;
15317
15318 /* Storage Controllers */
15319 StorageControllerType_T hdStorageControllerType;
15320 StorageBus_T hdStorageBusType;
15321 StorageControllerType_T dvdStorageControllerType;
15322 StorageBus_T dvdStorageBusType;
15323 BOOL recommendedFloppy;
15324 ComPtr<IStorageController> floppyController;
15325 ComPtr<IStorageController> hdController;
15326 ComPtr<IStorageController> dvdController;
15327 Utf8Str strFloppyName, strDVDName, strHDName;
15328
15329 /* GUI auto generates controller names using bus type. Do the same*/
15330 strFloppyName = StorageController::i_controllerNameFromBusType(StorageBus_Floppy);
15331
15332 /* Floppy recommended? add one. */
15333 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15334 if (FAILED(hrc)) return hrc;
15335 if (recommendedFloppy)
15336 {
15337 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15338 if (FAILED(hrc)) return hrc;
15339 }
15340
15341 /* Setup one DVD storage controller. */
15342 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15343 if (FAILED(hrc)) return hrc;
15344
15345 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15346 if (FAILED(hrc)) return hrc;
15347
15348 strDVDName = StorageController::i_controllerNameFromBusType(dvdStorageBusType);
15349
15350 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15351 if (FAILED(hrc)) return hrc;
15352
15353 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15354 if (FAILED(hrc)) return hrc;
15355
15356 /* Setup one HDD storage controller. */
15357 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15358 if (FAILED(hrc)) return hrc;
15359
15360 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15361 if (FAILED(hrc)) return hrc;
15362
15363 strHDName = StorageController::i_controllerNameFromBusType(hdStorageBusType);
15364
15365 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15366 {
15367 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15368 if (FAILED(hrc)) return hrc;
15369
15370 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15371 if (FAILED(hrc)) return hrc;
15372 }
15373 else
15374 {
15375 /* The HD controller is the same as DVD: */
15376 hdController = dvdController;
15377 }
15378
15379 /* Limit the AHCI port count if it's used because windows has trouble with
15380 * too many ports and other guest (OS X in particular) may take extra long
15381 * boot: */
15382
15383 // pParent = static_cast<Medium*>(aP)
15384 IStorageController *temp = hdController;
15385 ComObjPtr<StorageController> storageController;
15386 storageController = static_cast<StorageController *>(temp);
15387
15388 // tempHDController = aHDController;
15389 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15390 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15391 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15392 storageController->COMSETTER(PortCount)(1);
15393
15394 /* VirtioSCSI configures only one port per default -- set two ports here, one for HDD and one for DVD drive. */
15395 if (hdStorageControllerType == StorageControllerType_VirtioSCSI)
15396 {
15397 hrc = storageController->COMSETTER(PortCount)(2);
15398 if (FAILED(hrc)) return hrc;
15399 }
15400
15401 /* USB stuff */
15402
15403 bool ohciEnabled = false;
15404
15405 ComPtr<IUSBController> usbController;
15406 BOOL recommendedUSB3;
15407 BOOL recommendedUSB;
15408 BOOL usbProxyAvailable;
15409
15410 getUSBProxyAvailable(&usbProxyAvailable);
15411 if (FAILED(hrc)) return hrc;
15412
15413 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15414 if (FAILED(hrc)) return hrc;
15415 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15416 if (FAILED(hrc)) return hrc;
15417
15418 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15419 {
15420 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15421 if (FAILED(hrc)) return hrc;
15422
15423 /* xHci includes OHCI */
15424 ohciEnabled = true;
15425 }
15426 if ( !ohciEnabled
15427 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15428 {
15429 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15430 if (FAILED(hrc)) return hrc;
15431 ohciEnabled = true;
15432
15433 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15434 if (FAILED(hrc)) return hrc;
15435 }
15436
15437 /* Set recommended human interface device types: */
15438 BOOL recommendedUSBHID;
15439 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15440 if (FAILED(hrc)) return hrc;
15441
15442 if (recommendedUSBHID)
15443 {
15444 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15445 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15446 if (!ohciEnabled && !usbDeviceFilters.isNull())
15447 {
15448 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15449 if (FAILED(hrc)) return hrc;
15450 }
15451 }
15452
15453 BOOL recommendedUSBTablet;
15454 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15455 if (FAILED(hrc)) return hrc;
15456
15457 if (recommendedUSBTablet)
15458 {
15459 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15460 if (!ohciEnabled && !usbDeviceFilters.isNull())
15461 {
15462 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15463 if (FAILED(hrc)) return hrc;
15464 }
15465 }
15466
15467 /* Enable the VMMDev testing feature for bootsector VMs: */
15468 if (osTypeId == GUEST_OS_ID_STR_X64("VBoxBS"))
15469 {
15470 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15471 if (FAILED(hrc))
15472 return hrc;
15473 }
15474
15475 return S_OK;
15476}
15477
15478#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15479/**
15480 * Task record for change encryption settins.
15481 */
15482class Machine::ChangeEncryptionTask
15483 : public Machine::Task
15484{
15485public:
15486 ChangeEncryptionTask(Machine *m,
15487 Progress *p,
15488 const Utf8Str &t,
15489 const com::Utf8Str &aCurrentPassword,
15490 const com::Utf8Str &aCipher,
15491 const com::Utf8Str &aNewPassword,
15492 const com::Utf8Str &aNewPasswordId,
15493 const BOOL aForce,
15494 const MediaList &llMedia)
15495 : Task(m, p, t),
15496 mstrNewPassword(aNewPassword),
15497 mstrCurrentPassword(aCurrentPassword),
15498 mstrCipher(aCipher),
15499 mstrNewPasswordId(aNewPasswordId),
15500 mForce(aForce),
15501 mllMedia(llMedia)
15502 {}
15503
15504 ~ChangeEncryptionTask()
15505 {
15506 if (mstrNewPassword.length())
15507 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15508 if (mstrCurrentPassword.length())
15509 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15510 if (m_pCryptoIf)
15511 {
15512 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15513 m_pCryptoIf = NULL;
15514 }
15515 }
15516
15517 Utf8Str mstrNewPassword;
15518 Utf8Str mstrCurrentPassword;
15519 Utf8Str mstrCipher;
15520 Utf8Str mstrNewPasswordId;
15521 BOOL mForce;
15522 MediaList mllMedia;
15523 PCVBOXCRYPTOIF m_pCryptoIf;
15524private:
15525 void handler()
15526 {
15527 try
15528 {
15529 m_pMachine->i_changeEncryptionHandler(*this);
15530 }
15531 catch (...)
15532 {
15533 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
15534 }
15535 }
15536
15537 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
15538};
15539
15540/**
15541 * Scans specified directory and fills list by files found
15542 *
15543 * @returns VBox status code.
15544 * @param lstFiles
15545 * @param strDir
15546 * @param filePattern
15547 */
15548int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
15549 const com::Utf8Str &strPattern)
15550{
15551 /* To get all entries including subdirectories. */
15552 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
15553 if (!pszFilePattern)
15554 return VERR_NO_STR_MEMORY;
15555
15556 PRTDIRENTRYEX pDirEntry = NULL;
15557 RTDIR hDir;
15558 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
15559 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
15560 if (RT_SUCCESS(vrc))
15561 {
15562 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
15563 if (pDirEntry)
15564 {
15565 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
15566 != VERR_NO_MORE_FILES)
15567 {
15568 char *pszFilePath = NULL;
15569
15570 if (vrc == VERR_BUFFER_OVERFLOW)
15571 {
15572 /* allocate new buffer. */
15573 RTMemFree(pDirEntry);
15574 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
15575 if (!pDirEntry)
15576 {
15577 vrc = VERR_NO_MEMORY;
15578 break;
15579 }
15580 /* Retry. */
15581 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
15582 if (RT_FAILURE(vrc))
15583 break;
15584 }
15585 else if (RT_FAILURE(vrc))
15586 break;
15587
15588 /* Exclude . and .. */
15589 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
15590 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
15591 continue;
15592 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
15593 {
15594 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15595 if (!pszSubDirPath)
15596 {
15597 vrc = VERR_NO_STR_MEMORY;
15598 break;
15599 }
15600 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
15601 RTMemFree(pszSubDirPath);
15602 if (RT_FAILURE(vrc))
15603 break;
15604 continue;
15605 }
15606
15607 /* We got the new entry. */
15608 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
15609 continue;
15610
15611 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
15612 continue;
15613
15614 /* Prepend the path to the libraries. */
15615 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15616 if (!pszFilePath)
15617 {
15618 vrc = VERR_NO_STR_MEMORY;
15619 break;
15620 }
15621
15622 lstFiles.push_back(pszFilePath);
15623 RTStrFree(pszFilePath);
15624 }
15625
15626 RTMemFree(pDirEntry);
15627 }
15628 else
15629 vrc = VERR_NO_MEMORY;
15630
15631 RTDirClose(hDir);
15632 }
15633 else
15634 {
15635 /* On Windows the above immediately signals that there are no
15636 * files matching, while on other platforms enumerating the
15637 * files below fails. Either way: stop searching. */
15638 }
15639
15640 if ( vrc == VERR_NO_MORE_FILES
15641 || vrc == VERR_FILE_NOT_FOUND
15642 || vrc == VERR_PATH_NOT_FOUND)
15643 vrc = VINF_SUCCESS;
15644 RTStrFree(pszFilePattern);
15645 return vrc;
15646}
15647
15648/**
15649 * Helper to set up an I/O stream to read or write a possibly encrypted file.
15650 *
15651 * @returns VBox status code.
15652 * @param pszFilename The file to open.
15653 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
15654 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
15655 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
15656 * @param fOpen The open flags for the file.
15657 * @param phVfsIos Where to store the handle to the I/O stream on success.
15658 */
15659int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
15660 const char *pszKeyStore, const char *pszPassword,
15661 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
15662{
15663 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
15664 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
15665 if (RT_SUCCESS(vrc))
15666 {
15667 if (pCryptoIf)
15668 {
15669 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
15670 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
15671 if (RT_SUCCESS(vrc))
15672 {
15673 RTVfsFileRelease(hVfsFile);
15674 hVfsFile = hVfsFileCrypto;
15675 }
15676 }
15677
15678 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
15679 RTVfsFileRelease(hVfsFile);
15680 }
15681
15682 return vrc;
15683}
15684
15685/**
15686 * Helper function processing all actions for one component (saved state files,
15687 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
15688 *
15689 * @param task
15690 * @param strDirectory
15691 * @param strFilePattern
15692 * @param strMagic
15693 * @param strKeyStore
15694 * @param strKeyId
15695 * @return
15696 */
15697HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
15698 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
15699 com::Utf8Str &strKeyId, int iCipherMode)
15700{
15701 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
15702 && task.mstrCipher.isEmpty()
15703 && task.mstrNewPassword.isEmpty()
15704 && task.mstrNewPasswordId.isEmpty();
15705 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
15706 && task.mstrCipher.isNotEmpty()
15707 && task.mstrNewPassword.isNotEmpty()
15708 && task.mstrNewPasswordId.isNotEmpty();
15709
15710 /* check if the cipher is changed which causes the reencryption*/
15711
15712 const char *pszTaskCipher = NULL;
15713 if (task.mstrCipher.isNotEmpty())
15714 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
15715
15716 if (!task.mForce && !fDecrypt && !fEncrypt)
15717 {
15718 char *pszCipher = NULL;
15719 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
15720 NULL /*pszPassword*/,
15721 NULL /*ppbKey*/,
15722 NULL /*pcbKey*/,
15723 &pszCipher);
15724 if (RT_SUCCESS(vrc))
15725 {
15726 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
15727 RTMemFree(pszCipher);
15728 }
15729 else
15730 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
15731 strFilePattern.c_str(), vrc);
15732 }
15733
15734 /* Only the password needs to be changed */
15735 if (!task.mForce && !fDecrypt && !fEncrypt)
15736 {
15737 Assert(task.m_pCryptoIf);
15738
15739 VBOXCRYPTOCTX hCryptoCtx;
15740 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
15741 if (RT_FAILURE(vrc))
15742 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
15743 strFilePattern.c_str(), vrc);
15744 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15745 if (RT_FAILURE(vrc))
15746 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
15747 strFilePattern.c_str(), vrc);
15748
15749 char *pszKeyStore = NULL;
15750 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15751 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15752 if (RT_FAILURE(vrc))
15753 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
15754 strFilePattern.c_str(), vrc);
15755 strKeyStore = pszKeyStore;
15756 RTMemFree(pszKeyStore);
15757 strKeyId = task.mstrNewPasswordId;
15758 return S_OK;
15759 }
15760
15761 /* Reencryption required */
15762 HRESULT hrc = S_OK;
15763 int vrc = VINF_SUCCESS;
15764
15765 std::list<com::Utf8Str> lstFiles;
15766 if (SUCCEEDED(hrc))
15767 {
15768 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
15769 if (RT_FAILURE(vrc))
15770 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
15771 }
15772 com::Utf8Str strNewKeyStore;
15773 if (SUCCEEDED(hrc))
15774 {
15775 if (!fDecrypt)
15776 {
15777 VBOXCRYPTOCTX hCryptoCtx;
15778 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
15779 if (RT_FAILURE(vrc))
15780 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
15781 strFilePattern.c_str(), vrc);
15782
15783 char *pszKeyStore = NULL;
15784 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15785 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15786 if (RT_FAILURE(vrc))
15787 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
15788 strFilePattern.c_str(), vrc);
15789 strNewKeyStore = pszKeyStore;
15790 RTMemFree(pszKeyStore);
15791 }
15792
15793 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15794 it != lstFiles.end();
15795 ++it)
15796 {
15797 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
15798 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
15799
15800 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
15801 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
15802
15803 vrc = i_createIoStreamForFile((*it).c_str(),
15804 fEncrypt ? NULL : task.m_pCryptoIf,
15805 fEncrypt ? NULL : strKeyStore.c_str(),
15806 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
15807 fOpenForRead, &hVfsIosOld);
15808 if (RT_SUCCESS(vrc))
15809 {
15810 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
15811 fDecrypt ? NULL : task.m_pCryptoIf,
15812 fDecrypt ? NULL : strNewKeyStore.c_str(),
15813 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
15814 fOpenForWrite, &hVfsIosNew);
15815 if (RT_FAILURE(vrc))
15816 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
15817 (*it + ".tmp").c_str(), vrc);
15818 }
15819 else
15820 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
15821
15822 if (RT_SUCCESS(vrc))
15823 {
15824 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
15825 if (RT_FAILURE(vrc))
15826 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
15827 (*it).c_str(), vrc);
15828 }
15829
15830 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
15831 RTVfsIoStrmRelease(hVfsIosOld);
15832 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
15833 RTVfsIoStrmRelease(hVfsIosNew);
15834 }
15835 }
15836
15837 if (SUCCEEDED(hrc))
15838 {
15839 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15840 it != lstFiles.end();
15841 ++it)
15842 {
15843 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
15844 if (RT_FAILURE(vrc))
15845 {
15846 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
15847 break;
15848 }
15849 }
15850 }
15851
15852 if (SUCCEEDED(hrc))
15853 {
15854 strKeyStore = strNewKeyStore;
15855 strKeyId = task.mstrNewPasswordId;
15856 }
15857
15858 return hrc;
15859}
15860
15861/**
15862 * Task thread implementation for Machine::changeEncryption(), called from
15863 * Machine::taskHandler().
15864 *
15865 * @note Locks this object for writing.
15866 *
15867 * @param task
15868 * @return
15869 */
15870void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
15871{
15872 LogFlowThisFuncEnter();
15873
15874 AutoCaller autoCaller(this);
15875 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
15876 if (FAILED(autoCaller.hrc()))
15877 {
15878 /* we might have been uninitialized because the session was accidentally
15879 * closed by the client, so don't assert */
15880 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
15881 task.m_pProgress->i_notifyComplete(hrc);
15882 LogFlowThisFuncLeave();
15883 return;
15884 }
15885
15886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15887
15888 HRESULT hrc = S_OK;
15889 com::Utf8Str strOldKeyId = mData->mstrKeyId;
15890 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
15891 try
15892 {
15893 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
15894 if (FAILED(hrc))
15895 throw hrc;
15896
15897 if (task.mstrCurrentPassword.isEmpty())
15898 {
15899 if (mData->mstrKeyStore.isNotEmpty())
15900 throw setError(VBOX_E_PASSWORD_INCORRECT,
15901 tr("The password given for the encrypted VM is incorrect"));
15902 }
15903 else
15904 {
15905 if (mData->mstrKeyStore.isEmpty())
15906 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15907 tr("The VM is not configured for encryption"));
15908 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
15909 if (hrc == VBOX_E_PASSWORD_INCORRECT)
15910 throw setError(VBOX_E_PASSWORD_INCORRECT,
15911 tr("The password to decrypt the VM is incorrect"));
15912 }
15913
15914 if (task.mstrCipher.isNotEmpty())
15915 {
15916 if ( task.mstrNewPassword.isEmpty()
15917 && task.mstrNewPasswordId.isEmpty()
15918 && task.mstrCurrentPassword.isNotEmpty())
15919 {
15920 /* An empty password and password ID will default to the current password. */
15921 task.mstrNewPassword = task.mstrCurrentPassword;
15922 }
15923 else if (task.mstrNewPassword.isEmpty())
15924 throw setError(VBOX_E_OBJECT_NOT_FOUND,
15925 tr("A password must be given for the VM encryption"));
15926 else if (task.mstrNewPasswordId.isEmpty())
15927 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15928 tr("A valid identifier for the password must be given"));
15929 }
15930 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
15931 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15932 tr("The password and password identifier must be empty if the output should be unencrypted"));
15933
15934 /*
15935 * Save config.
15936 * Must be first operation to prevent making encrypted copies
15937 * for old version of the config file.
15938 */
15939 int fSave = Machine::SaveS_Force;
15940 if (task.mstrNewPassword.isNotEmpty())
15941 {
15942 VBOXCRYPTOCTX hCryptoCtx;
15943
15944 int vrc = VINF_SUCCESS;
15945 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
15946 {
15947 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
15948 task.mstrNewPassword.c_str(), &hCryptoCtx);
15949 if (RT_FAILURE(vrc))
15950 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
15951 }
15952 else
15953 {
15954 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
15955 task.mstrCurrentPassword.c_str(),
15956 &hCryptoCtx);
15957 if (RT_FAILURE(vrc))
15958 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
15959 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15960 if (RT_FAILURE(vrc))
15961 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
15962 }
15963
15964 char *pszKeyStore;
15965 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15966 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15967 if (RT_FAILURE(vrc))
15968 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
15969 mData->mstrKeyStore = pszKeyStore;
15970 RTStrFree(pszKeyStore);
15971 mData->mstrKeyId = task.mstrNewPasswordId;
15972 size_t cbPassword = task.mstrNewPassword.length() + 1;
15973 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
15974 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
15975 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
15976 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
15977
15978 /*
15979 * Remove backuped config after saving because it can contain
15980 * unencrypted version of the config
15981 */
15982 fSave |= Machine::SaveS_RemoveBackup;
15983 }
15984 else
15985 {
15986 mData->mstrKeyId.setNull();
15987 mData->mstrKeyStore.setNull();
15988 }
15989
15990 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
15991 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
15992 Bstr bstrNewPassword(task.mstrNewPassword);
15993 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
15994 /* encrypt media */
15995 alock.release();
15996 for (MediaList::iterator it = task.mllMedia.begin();
15997 it != task.mllMedia.end();
15998 ++it)
15999 {
16000 ComPtr<IProgress> pProgress1;
16001 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16002 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16003 pProgress1.asOutParam());
16004 if (FAILED(hrc)) throw hrc;
16005 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16006 if (FAILED(hrc)) throw hrc;
16007 }
16008 alock.acquire();
16009
16010 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16011
16012 Utf8Str strFullSnapshotFolder;
16013 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16014
16015 /* .sav files (main and snapshots) */
16016 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16017 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16018 if (FAILED(hrc))
16019 /* the helper function already sets error object */
16020 throw hrc;
16021
16022 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16023
16024 /* .nvram files */
16025 com::Utf8Str strNVRAMKeyId;
16026 com::Utf8Str strNVRAMKeyStore;
16027 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16028 if (FAILED(hrc))
16029 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16030
16031 Utf8Str strMachineFolder;
16032 i_calculateFullPath(".", strMachineFolder);
16033
16034 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16035 if (FAILED(hrc))
16036 /* the helper function already sets error object */
16037 throw hrc;
16038
16039 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16040 if (FAILED(hrc))
16041 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16042
16043 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16044
16045 /* .log files */
16046 com::Utf8Str strLogFolder;
16047 i_getLogFolder(strLogFolder);
16048 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16049 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16050 if (FAILED(hrc))
16051 /* the helper function already sets error object */
16052 throw hrc;
16053
16054 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16055
16056 i_saveSettings(NULL, alock, fSave);
16057 }
16058 catch (HRESULT hrcXcpt)
16059 {
16060 hrc = hrcXcpt;
16061 mData->mstrKeyId = strOldKeyId;
16062 mData->mstrKeyStore = strOldKeyStore;
16063 }
16064
16065 task.m_pProgress->i_notifyComplete(hrc);
16066
16067 LogFlowThisFuncLeave();
16068}
16069#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16070
16071HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16072 const com::Utf8Str &aCipher,
16073 const com::Utf8Str &aNewPassword,
16074 const com::Utf8Str &aNewPasswordId,
16075 BOOL aForce,
16076 ComPtr<IProgress> &aProgress)
16077{
16078 LogFlowFuncEnter();
16079
16080#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16081 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16082 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16083#else
16084 /* make the VM accessible */
16085 if (!mData->mAccessible)
16086 {
16087 if ( aCurrentPassword.isEmpty()
16088 || mData->mstrKeyId.isEmpty())
16089 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16090
16091 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16092 if (FAILED(hrc))
16093 return hrc;
16094 }
16095
16096 AutoLimitedCaller autoCaller(this);
16097 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16098
16099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16100
16101 /* define media to be change encryption */
16102
16103 MediaList llMedia;
16104 for (MediumAttachmentList::iterator
16105 it = mMediumAttachments->begin();
16106 it != mMediumAttachments->end();
16107 ++it)
16108 {
16109 ComObjPtr<MediumAttachment> &pAttach = *it;
16110 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16111
16112 if (!pMedium.isNull())
16113 {
16114 AutoCaller mac(pMedium);
16115 if (FAILED(mac.hrc())) return mac.hrc();
16116 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16117 DeviceType_T devType = pMedium->i_getDeviceType();
16118 if (devType == DeviceType_HardDisk)
16119 {
16120 /*
16121 * We need to move to last child because the Medium::changeEncryption
16122 * encrypts all chain of specified medium with its parents.
16123 * Also we perform cheking of back reference and children for
16124 * all media in the chain to raise error before we start any action.
16125 * So, we first move into root parent and then we will move to last child
16126 * keeping latter in the list for encryption.
16127 */
16128
16129 /* move to root parent */
16130 ComObjPtr<Medium> pTmpMedium = pMedium;
16131 while (pTmpMedium.isNotNull())
16132 {
16133 AutoCaller mediumAC(pTmpMedium);
16134 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16135 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16136
16137 /* Cannot encrypt media which are attached to more than one virtual machine. */
16138 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16139 if (cBackRefs > 1)
16140 return setError(VBOX_E_INVALID_OBJECT_STATE,
16141 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16142 pTmpMedium->i_getName().c_str(), cBackRefs);
16143
16144 size_t cChildren = pTmpMedium->i_getChildren().size();
16145 if (cChildren > 1)
16146 return setError(VBOX_E_INVALID_OBJECT_STATE,
16147 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16148 pTmpMedium->i_getName().c_str(), cChildren);
16149
16150 pTmpMedium = pTmpMedium->i_getParent();
16151 }
16152 /* move to last child */
16153 pTmpMedium = pMedium;
16154 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16155 {
16156 AutoCaller mediumAC(pTmpMedium);
16157 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16158 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16159
16160 /* Cannot encrypt media which are attached to more than one virtual machine. */
16161 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16162 if (cBackRefs > 1)
16163 return setError(VBOX_E_INVALID_OBJECT_STATE,
16164 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16165 pTmpMedium->i_getName().c_str(), cBackRefs);
16166
16167 size_t cChildren = pTmpMedium->i_getChildren().size();
16168 if (cChildren > 1)
16169 return setError(VBOX_E_INVALID_OBJECT_STATE,
16170 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16171 pTmpMedium->i_getName().c_str(), cChildren);
16172
16173 pTmpMedium = pTmpMedium->i_getChildren().front();
16174 }
16175 llMedia.push_back(pTmpMedium);
16176 }
16177 }
16178 }
16179
16180 ComObjPtr<Progress> pProgress;
16181 pProgress.createObject();
16182 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16183 static_cast<IMachine*>(this) /* aInitiator */,
16184 tr("Change encryption"),
16185 TRUE /* fCancellable */,
16186 (ULONG)(4 + + llMedia.size()), // cOperations
16187 tr("Change encryption of the mediuma"));
16188 if (FAILED(hrc))
16189 return hrc;
16190
16191 /* create and start the task on a separate thread (note that it will not
16192 * start working until we release alock) */
16193 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16194 aCurrentPassword, aCipher, aNewPassword,
16195 aNewPasswordId, aForce, llMedia);
16196 hrc = pTask->createThread();
16197 pTask = NULL;
16198 if (FAILED(hrc))
16199 return hrc;
16200
16201 pProgress.queryInterfaceTo(aProgress.asOutParam());
16202
16203 LogFlowFuncLeave();
16204
16205 return S_OK;
16206#endif
16207}
16208
16209HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16210 com::Utf8Str &aPasswordId)
16211{
16212#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16213 RT_NOREF(aCipher, aPasswordId);
16214 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16215#else
16216 AutoLimitedCaller autoCaller(this);
16217 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16218
16219 PCVBOXCRYPTOIF pCryptoIf = NULL;
16220 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16221 if (FAILED(hrc)) return hrc; /* Error is set */
16222
16223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16224
16225 if (mData->mstrKeyStore.isNotEmpty())
16226 {
16227 char *pszCipher = NULL;
16228 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16229 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16230 if (RT_SUCCESS(vrc))
16231 {
16232 aCipher = getCipherStringWithoutMode(pszCipher);
16233 RTStrFree(pszCipher);
16234 aPasswordId = mData->mstrKeyId;
16235 }
16236 else
16237 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16238 tr("Failed to query the encryption settings with %Rrc"),
16239 vrc);
16240 }
16241 else
16242 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16243
16244 mParent->i_releaseCryptoIf(pCryptoIf);
16245
16246 return hrc;
16247#endif
16248}
16249
16250HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16251{
16252#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16253 RT_NOREF(aPassword);
16254 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16255#else
16256 AutoLimitedCaller autoCaller(this);
16257 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16258
16259 PCVBOXCRYPTOIF pCryptoIf = NULL;
16260 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16261 if (FAILED(hrc)) return hrc; /* Error is set */
16262
16263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16264
16265 if (mData->mstrKeyStore.isNotEmpty())
16266 {
16267 char *pszCipher = NULL;
16268 uint8_t *pbDek = NULL;
16269 size_t cbDek = 0;
16270 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16271 &pbDek, &cbDek, &pszCipher);
16272 if (RT_SUCCESS(vrc))
16273 {
16274 RTStrFree(pszCipher);
16275 RTMemSaferFree(pbDek, cbDek);
16276 }
16277 else
16278 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16279 tr("The password supplied for the encrypted machine is incorrect"));
16280 }
16281 else
16282 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16283
16284 mParent->i_releaseCryptoIf(pCryptoIf);
16285
16286 return hrc;
16287#endif
16288}
16289
16290HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16291 const com::Utf8Str &aPassword)
16292{
16293#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16294 RT_NOREF(aId, aPassword);
16295 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16296#else
16297 AutoLimitedCaller autoCaller(this);
16298 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16299
16300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16301
16302 size_t cbPassword = aPassword.length() + 1;
16303 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16304
16305 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16306
16307 if ( mData->mAccessible
16308 && mData->mSession.mState == SessionState_Locked
16309 && mData->mSession.mLockType == LockType_VM
16310 && mData->mSession.mDirectControl != NULL)
16311 {
16312 /* get the console from the direct session */
16313 ComPtr<IConsole> console;
16314 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16315 ComAssertComRC(hrc);
16316 /* send passsword to console */
16317 console->AddEncryptionPassword(Bstr(aId).raw(),
16318 Bstr(aPassword).raw(),
16319 TRUE);
16320 }
16321
16322 if (mData->mstrKeyId == aId)
16323 {
16324 HRESULT hrc = checkEncryptionPassword(aPassword);
16325 if (FAILED(hrc))
16326 return hrc;
16327
16328 if (SUCCEEDED(hrc))
16329 {
16330 /*
16331 * Encryption is used and password is correct,
16332 * Reinit the machine if required.
16333 */
16334 BOOL fAccessible;
16335 alock.release();
16336 getAccessible(&fAccessible);
16337 alock.acquire();
16338 }
16339 }
16340
16341 /*
16342 * Add the password into the NvramStore only after
16343 * the machine becomes accessible and the NvramStore
16344 * contains key id and key store.
16345 */
16346 if (mNvramStore.isNotNull())
16347 mNvramStore->i_addPassword(aId, aPassword);
16348
16349 return S_OK;
16350#endif
16351}
16352
16353HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16354 const std::vector<com::Utf8Str> &aPasswords)
16355{
16356#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16357 RT_NOREF(aIds, aPasswords);
16358 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16359#else
16360 if (aIds.size() != aPasswords.size())
16361 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16362
16363 HRESULT hrc = S_OK;
16364 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16365 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16366
16367 return hrc;
16368#endif
16369}
16370
16371HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16372{
16373#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16374 RT_NOREF(autoCaller, aId);
16375 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16376#else
16377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16378
16379 if ( mData->mAccessible
16380 && mData->mSession.mState == SessionState_Locked
16381 && mData->mSession.mLockType == LockType_VM
16382 && mData->mSession.mDirectControl != NULL)
16383 {
16384 /* get the console from the direct session */
16385 ComPtr<IConsole> console;
16386 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16387 ComAssertComRC(hrc);
16388 /* send passsword to console */
16389 console->RemoveEncryptionPassword(Bstr(aId).raw());
16390 }
16391
16392 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16393 {
16394 if (Global::IsOnlineOrTransient(mData->mMachineState))
16395 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16396 alock.release();
16397 autoCaller.release();
16398 /* return because all passwords are purged when machine becomes inaccessible; */
16399 return i_setInaccessible();
16400 }
16401
16402 if (mNvramStore.isNotNull())
16403 mNvramStore->i_removePassword(aId);
16404 mData->mpKeyStore->deleteSecretKey(aId);
16405 return S_OK;
16406#endif
16407}
16408
16409HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16410{
16411#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16412 RT_NOREF(autoCaller);
16413 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16414#else
16415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16416
16417 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16418 {
16419 if (Global::IsOnlineOrTransient(mData->mMachineState))
16420 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16421 alock.release();
16422 autoCaller.release();
16423 /* return because all passwords are purged when machine becomes inaccessible; */
16424 return i_setInaccessible();
16425 }
16426
16427 mNvramStore->i_removeAllPasswords();
16428 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16429 return S_OK;
16430#endif
16431}
16432
16433#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16434HRESULT Machine::i_setInaccessible()
16435{
16436 if (!mData->mAccessible)
16437 return S_OK;
16438
16439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16440 VirtualBox *pParent = mParent;
16441 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16442 Guid id(i_getId());
16443
16444 alock.release();
16445
16446 uninit();
16447 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16448
16449 alock.acquire();
16450 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16451 return hrc;
16452}
16453#endif
16454
16455/* This isn't handled entirely by the wrapper generator yet. */
16456#ifdef VBOX_WITH_XPCOM
16457NS_DECL_CLASSINFO(SessionMachine)
16458NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16459
16460NS_DECL_CLASSINFO(SnapshotMachine)
16461NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16462#endif
Note: See TracBrowser for help on using the repository browser.

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