VirtualBox

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

Last change on this file since 107526 was 107526, checked in by vboxsync, 9 days ago

src/VBox/Main/include/MachineImpl[.h|.cpp]: Fixed warnings found by Parfait (uninitialized attributes). Left a @todo. jiraref:VBP-1424

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 584.4 KB
Line 
1/* $Id: MachineImpl.cpp 107526 2025-01-08 15:24:26Z 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 fEncrypted = false;
216#endif
217}
218
219Machine::Data::~Data()
220{
221 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
222 {
223 RTSemEventMultiDestroy(mMachineStateDepsSem);
224 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
225 }
226 if (pMachineConfigFile)
227 {
228 delete pMachineConfigFile;
229 pMachineConfigFile = NULL;
230 }
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine::HWData structure
235/////////////////////////////////////////////////////////////////////////////
236
237Machine::HWData::HWData()
238{
239 /* default values for a newly created machine for x86. */
240 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
241 mMemorySize = 128;
242 mCPUCount = 1;
243 mCPUHotPlugEnabled = false;
244 mMemoryBalloonSize = 0;
245 mPageFusionEnabled = false;
246 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
247 mCpuIdPortabilityLevel = 0;
248 mExecEngine = VMExecutionEngine_NotSet;
249 mCpuProfile = "host";
250
251 /* default boot order: floppy - DVD - HDD */
252 mBootOrder[0] = DeviceType_Floppy;
253 mBootOrder[1] = DeviceType_DVD;
254 mBootOrder[2] = DeviceType_HardDisk;
255 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
256 mBootOrder[i] = DeviceType_Null;
257
258 mClipboardMode = ClipboardMode_Disabled;
259 mClipboardFileTransfersEnabled = FALSE;
260
261 mDnDMode = DnDMode_Disabled;
262
263 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
264 mPointingHIDType = PointingHIDType_PS2Mouse;
265 mParavirtProvider = ParavirtProvider_Default;
266 mEmulatedUSBCardReaderEnabled = FALSE;
267
268 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
269 mCPUAttached[i] = false;
270
271 mIOCacheEnabled = true;
272 mIOCacheSize = 5; /* 5MB */
273}
274
275Machine::HWData::~HWData()
276{
277}
278
279/////////////////////////////////////////////////////////////////////////////
280// Machine class
281/////////////////////////////////////////////////////////////////////////////
282
283// constructor / destructor
284/////////////////////////////////////////////////////////////////////////////
285
286Machine::Machine() :
287#ifdef VBOX_WITH_RESOURCE_USAGE_API
288 mCollectorGuest(NULL),
289#endif
290 mPeer(NULL),
291 mParent(NULL),
292 mSerialPorts(),
293 mParallelPorts(),
294 uRegistryNeedsSaving(0)
295{}
296
297Machine::~Machine()
298{}
299
300HRESULT Machine::FinalConstruct()
301{
302 LogFlowThisFunc(("\n"));
303 setTracked(0, 7200);//infinite, 2 hours
304 return BaseFinalConstruct();
305}
306
307void Machine::FinalRelease()
308{
309 LogFlowThisFunc(("\n"));
310 uninit();
311 BaseFinalRelease();
312}
313
314/**
315 * Initializes a new machine instance; this init() variant creates a new, empty machine.
316 * This gets called from VirtualBox::CreateMachine().
317 *
318 * @param aParent Associated parent object.
319 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
320 * @param strName Name for the machine.
321 * @param aArchitecture Architecture to use for the machine.
322 * If a valid guest OS type is set via \a aOsType, the guest OS' type will be used instead then.
323 * @param llGroups list of groups for the machine.
324 * @param strOsType OS Type string (stored as is if aOsType is NULL).
325 * @param aOsType OS Type of this machine or NULL.
326 * @param aId UUID for the new machine.
327 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
328 * @param fDirectoryIncludesUUID
329 * Whether the use a special VM directory naming scheme (includes the UUID).
330 * @param aCipher The cipher to encrypt the VM with.
331 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
332 * @param aPassword The password to encrypt the VM with.
333 *
334 * @return Success indicator. if not S_OK, the machine object is invalid.
335 */
336HRESULT Machine::init(VirtualBox *aParent,
337 const Utf8Str &strConfigFile,
338 const Utf8Str &strName,
339 PlatformArchitecture_T aArchitecture,
340 const StringsList &llGroups,
341 const Utf8Str &strOsType,
342 GuestOSType *aOsType,
343 const Guid &aId,
344 bool fForceOverwrite,
345 bool fDirectoryIncludesUUID,
346 const com::Utf8Str &aCipher,
347 const com::Utf8Str &aPasswordId,
348 const com::Utf8Str &aPassword)
349{
350 LogFlowThisFuncEnter();
351 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
352
353#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
354 RT_NOREF(aCipher);
355 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
356 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
357#endif
358
359 /* Enclose the state transition NotReady->InInit->Ready */
360 AutoInitSpan autoInitSpan(this);
361 AssertReturn(autoInitSpan.isOk(), E_FAIL);
362
363 HRESULT hrc = initImpl(aParent, strConfigFile);
364 if (FAILED(hrc)) return hrc;
365
366#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
367 com::Utf8Str strSsmKeyId;
368 com::Utf8Str strSsmKeyStore;
369 com::Utf8Str strNVRAMKeyId;
370 com::Utf8Str strNVRAMKeyStore;
371
372 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
373 {
374 /* Resolve the cryptographic interface. */
375 PCVBOXCRYPTOIF pCryptoIf = NULL;
376 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
377 if (SUCCEEDED(hrc))
378 {
379 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
380 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
381 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
382
383 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
384 {
385 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
386 if (!pszCipher)
387 {
388 hrc = setError(VBOX_E_NOT_SUPPORTED,
389 tr("The cipher '%s' is not supported"), aCipher.c_str());
390 break;
391 }
392
393 VBOXCRYPTOCTX hCryptoCtx;
394 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
395 if (RT_FAILURE(vrc))
396 {
397 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
398 break;
399 }
400
401 char *pszKeyStore;
402 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
403 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
404 AssertRC(vrc2);
405
406 if (RT_FAILURE(vrc))
407 {
408 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
409 break;
410 }
411
412 *(astrKeyStore[i]) = pszKeyStore;
413 RTMemFree(pszKeyStore);
414 *(astrKeyId[i]) = aPasswordId;
415 }
416
417 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
418 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
419
420 if (FAILED(hrc))
421 return hrc; /* Error is set. */
422 }
423 else
424 return hrc; /* Error is set. */
425 }
426#endif
427
428 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
429 if (FAILED(hrc)) return hrc;
430
431 if (SUCCEEDED(hrc))
432 {
433 // create an empty machine config
434 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
435
436 hrc = initDataAndChildObjects();
437 }
438
439 if (SUCCEEDED(hrc))
440 {
441#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
442 mSSData->strStateKeyId = strSsmKeyId;
443 mSSData->strStateKeyStore = strSsmKeyStore;
444#endif
445
446 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
447 mData->mAccessible = TRUE;
448
449 unconst(mData->mUuid) = aId;
450
451 mUserData->s.strName = strName;
452
453 if (llGroups.size())
454 mUserData->s.llGroups = llGroups;
455
456 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
457 // the "name sync" flag determines whether the machine directory gets renamed along
458 // with the machine file; say so if the settings file name is the same as the
459 // settings file parent directory (machine directory)
460 mUserData->s.fNameSync = i_isInOwnDir();
461
462 // initialize the default snapshots folder
463 hrc = COMSETTER(SnapshotFolder)(NULL);
464 AssertComRC(hrc);
465
466 /* Use the platform architecture which was handed-in by default. */
467 PlatformArchitecture_T enmPlatformArch = aArchitecture;
468
469 if (aOsType)
470 {
471 /* Store OS type */
472 mUserData->s.strOsType = aOsType->i_id();
473
474 /* Use the platform architecture of the found guest OS type. */
475 enmPlatformArch = aOsType->i_platformArchitecture();
476 }
477 else if (!strOsType.isEmpty())
478 {
479 /* Store OS type */
480 mUserData->s.strOsType = strOsType;
481 }
482
483 /* Set the platform architecture first before applying the defaults below. */
484 hrc = mPlatform->i_initArchitecture(enmPlatformArch);
485 if (FAILED(hrc)) return hrc;
486
487 /* Apply platform defaults. */
488 mPlatform->i_applyDefaults(aOsType);
489 i_platformPropertiesUpdate();
490
491 /* Apply firmware defaults. */
492 mFirmwareSettings->i_applyDefaults(aOsType);
493
494 /* Apply TPM defaults. */
495 mTrustedPlatformModule->i_applyDefaults(aOsType);
496
497 /* Apply recording defaults. */
498 mRecordingSettings->i_applyDefaults();
499
500 /* Apply network adapters defaults */
501 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
502 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
503
504 /* Apply serial port defaults */
505 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
506 mSerialPorts[slot]->i_applyDefaults(aOsType);
507
508 /* Apply parallel port defaults */
509 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
510 mParallelPorts[slot]->i_applyDefaults();
511
512 /* Enable the VMMDev testing feature for bootsector VMs: */
513 if (aOsType && aOsType->i_id() == GUEST_OS_ID_STR_X64("VBoxBS"))
514 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
515
516#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
517 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
518#endif
519 if (SUCCEEDED(hrc))
520 {
521 /* At this point the changing of the current state modification
522 * flag is allowed. */
523 i_allowStateModification();
524
525 /* commit all changes made during the initialization */
526 i_commit();
527 }
528 }
529
530 /* Confirm a successful initialization when it's the case */
531 if (SUCCEEDED(hrc))
532 {
533#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
534 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
535 {
536 size_t cbPassword = aPassword.length() + 1;
537 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
538 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
539 }
540#endif
541
542 if (mData->mAccessible)
543 autoInitSpan.setSucceeded();
544 else
545 autoInitSpan.setLimited();
546 }
547
548 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
549 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
550 mData->mRegistered,
551 mData->mAccessible,
552 hrc));
553
554 LogFlowThisFuncLeave();
555
556 return hrc;
557}
558
559/**
560 * Initializes a new instance with data from machine XML (formerly Init_Registered).
561 * Gets called in two modes:
562 *
563 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
564 * UUID is specified and we mark the machine as "registered";
565 *
566 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
567 * and the machine remains unregistered until RegisterMachine() is called.
568 *
569 * @param aParent Associated parent object
570 * @param strConfigFile Local file system path to the VM settings file (can
571 * be relative to the VirtualBox config directory).
572 * @param aId UUID of the machine or NULL (see above).
573 * @param strPassword Password for decrypting the config
574 *
575 * @return Success indicator. if not S_OK, the machine object is invalid
576 */
577HRESULT Machine::initFromSettings(VirtualBox *aParent,
578 const Utf8Str &strConfigFile,
579 const Guid *aId,
580 const com::Utf8Str &strPassword)
581{
582 LogFlowThisFuncEnter();
583 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
584
585 PCVBOXCRYPTOIF pCryptoIf = NULL;
586#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
587 if (strPassword.isNotEmpty())
588 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
589#else
590 if (strPassword.isNotEmpty())
591 {
592 /* Get at the crpytographic interface. */
593 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
594 if (FAILED(hrc))
595 return hrc; /* Error is set. */
596 }
597#endif
598
599 /* Enclose the state transition NotReady->InInit->Ready */
600 AutoInitSpan autoInitSpan(this);
601 AssertReturn(autoInitSpan.isOk(), E_FAIL);
602
603 HRESULT hrc = initImpl(aParent, strConfigFile);
604 if (FAILED(hrc)) return hrc;
605
606 if (aId)
607 {
608 // loading a registered VM:
609 unconst(mData->mUuid) = *aId;
610 mData->mRegistered = TRUE;
611 // now load the settings from XML:
612 hrc = i_registeredInit();
613 // this calls initDataAndChildObjects() and loadSettings()
614 }
615 else
616 {
617 // opening an unregistered VM (VirtualBox::OpenMachine()):
618 hrc = initDataAndChildObjects();
619 if (SUCCEEDED(hrc))
620 {
621 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
622 mData->mAccessible = TRUE;
623
624 try
625 {
626 // load and parse machine XML; this will throw on XML or logic errors
627 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
628 pCryptoIf,
629 strPassword.c_str());
630
631 // reject VM UUID duplicates, they can happen if someone
632 // tries to register an already known VM config again
633 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
634 true /* fPermitInaccessible */,
635 false /* aDoSetError */,
636 NULL) != VBOX_E_OBJECT_NOT_FOUND)
637 {
638 throw setError(E_FAIL,
639 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
640 mData->m_strConfigFile.c_str());
641 }
642
643 // use UUID from machine config
644 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
645
646#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
647 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
648 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
649 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
650 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
651#endif
652
653 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
654 {
655 // We just set the inaccessible state and fill the error info allowing the caller
656 // to register the machine with encrypted config even if the password is incorrect
657 mData->mAccessible = FALSE;
658
659 /* fetch the current error info */
660 mData->mAccessError = com::ErrorInfo();
661
662 setError(VBOX_E_PASSWORD_INCORRECT,
663 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
664 mData->pMachineConfigFile->uuid.raw());
665 }
666 else
667 {
668#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
669 if (strPassword.isNotEmpty())
670 {
671 size_t cbKey = strPassword.length() + 1; /* Include terminator */
672 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
673 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
674 }
675#endif
676
677 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
678 if (FAILED(hrc)) throw hrc;
679
680 /* At this point the changing of the current state modification
681 * flag is allowed. */
682 i_allowStateModification();
683
684 i_commit();
685 }
686 }
687 catch (HRESULT err)
688 {
689 /* we assume that error info is set by the thrower */
690 hrc = err;
691 }
692 catch (...)
693 {
694 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
695 }
696 }
697 }
698
699 /* Confirm a successful initialization when it's the case */
700 if (SUCCEEDED(hrc))
701 {
702 if (mData->mAccessible)
703 autoInitSpan.setSucceeded();
704 else
705 {
706 autoInitSpan.setLimited();
707
708 // uninit media from this machine's media registry, or else
709 // reloading the settings will fail
710 mParent->i_unregisterMachineMedia(i_getId());
711 }
712 }
713
714#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
715 if (pCryptoIf)
716 {
717 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
718 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
719 }
720#endif
721
722 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
723 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
724
725 LogFlowThisFuncLeave();
726
727 return hrc;
728}
729
730/**
731 * Initializes a new instance from a machine config that is already in memory
732 * (import OVF case). Since we are importing, the UUID in the machine
733 * config is ignored and we always generate a fresh one.
734 *
735 * @param aParent Associated parent object.
736 * @param strName Name for the new machine; this overrides what is specified in config.
737 * @param strSettingsFilename File name of .vbox file.
738 * @param config Machine configuration loaded and parsed from XML.
739 *
740 * @return Success indicator. if not S_OK, the machine object is invalid
741 */
742HRESULT Machine::init(VirtualBox *aParent,
743 const Utf8Str &strName,
744 const Utf8Str &strSettingsFilename,
745 const settings::MachineConfigFile &config)
746{
747 LogFlowThisFuncEnter();
748
749 /* Enclose the state transition NotReady->InInit->Ready */
750 AutoInitSpan autoInitSpan(this);
751 AssertReturn(autoInitSpan.isOk(), E_FAIL);
752
753 HRESULT hrc = initImpl(aParent, strSettingsFilename);
754 if (FAILED(hrc)) return hrc;
755
756 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
757 if (FAILED(hrc)) return hrc;
758
759 hrc = initDataAndChildObjects();
760 if (SUCCEEDED(hrc))
761 {
762 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
763 mData->mAccessible = TRUE;
764
765 // create empty machine config for instance data
766 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
767
768 // generate fresh UUID, ignore machine config
769 unconst(mData->mUuid).create();
770
771 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
772
773 // override VM name as well, it may be different
774 mUserData->s.strName = strName;
775
776 if (SUCCEEDED(hrc))
777 {
778 /* At this point the changing of the current state modification
779 * flag is allowed. */
780 i_allowStateModification();
781
782 /* commit all changes made during the initialization */
783 i_commit();
784 }
785 }
786
787 /* Confirm a successful initialization when it's the case */
788 if (SUCCEEDED(hrc))
789 {
790 if (mData->mAccessible)
791 autoInitSpan.setSucceeded();
792 else
793 {
794 /* Ignore all errors from unregistering, they would destroy
795- * the more interesting error information we already have,
796- * pinpointing the issue with the VM config. */
797 ErrorInfoKeeper eik;
798
799 autoInitSpan.setLimited();
800
801 // uninit media from this machine's media registry, or else
802 // reloading the settings will fail
803 mParent->i_unregisterMachineMedia(i_getId());
804 }
805 }
806
807 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
808 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
809
810 LogFlowThisFuncLeave();
811
812 return hrc;
813}
814
815/**
816 * Shared code between the various init() implementations.
817 *
818 * @returns HRESULT
819 * @param aParent The VirtualBox object.
820 * @param strConfigFile Settings file.
821 */
822HRESULT Machine::initImpl(VirtualBox *aParent,
823 const Utf8Str &strConfigFile)
824{
825 LogFlowThisFuncEnter();
826
827 AssertReturn(aParent, E_INVALIDARG);
828 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
829
830 HRESULT hrc = S_OK;
831
832 /* share the parent weakly */
833 unconst(mParent) = aParent;
834
835 /* allocate the essential machine data structure (the rest will be
836 * allocated later by initDataAndChildObjects() */
837 mData.allocate();
838
839 /* memorize the config file name (as provided) */
840 mData->m_strConfigFile = strConfigFile;
841
842 /* get the full file name */
843 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
844 if (RT_FAILURE(vrc1))
845 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
846 tr("Invalid machine settings file name '%s' (%Rrc)"),
847 strConfigFile.c_str(),
848 vrc1);
849
850#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
851 /** @todo Only create when the machine is going to be encrypted. */
852 /* Non-pageable memory is not accessible for non-VM process */
853 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
854 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
855#endif
856
857 LogFlowThisFuncLeave();
858
859 return hrc;
860}
861
862/**
863 * Tries to create a machine settings file in the path stored in the machine
864 * instance data. Used when a new machine is created to fail gracefully if
865 * the settings file could not be written (e.g. because machine dir is read-only).
866 * @return
867 */
868HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
869{
870 HRESULT hrc = S_OK;
871
872 // when we create a new machine, we must be able to create the settings file
873 RTFILE f = NIL_RTFILE;
874 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
875 if ( RT_SUCCESS(vrc)
876 || vrc == VERR_SHARING_VIOLATION
877 )
878 {
879 if (RT_SUCCESS(vrc))
880 RTFileClose(f);
881 if (!fForceOverwrite)
882 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
883 else
884 {
885 /* try to delete the config file, as otherwise the creation
886 * of a new settings file will fail. */
887 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
888 }
889 }
890 else if ( vrc != VERR_FILE_NOT_FOUND
891 && vrc != VERR_PATH_NOT_FOUND
892 )
893 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
894 mData->m_strConfigFileFull.c_str(), vrc);
895 return hrc;
896}
897
898/**
899 * Initializes the registered machine by loading the settings file.
900 * This method is separated from #init() in order to make it possible to
901 * retry the operation after VirtualBox startup instead of refusing to
902 * startup the whole VirtualBox server in case if the settings file of some
903 * registered VM is invalid or inaccessible.
904 *
905 * @note Must be always called from this object's write lock
906 * (unless called from #init() that doesn't need any locking).
907 * @note Locks the mUSBController method for writing.
908 * @note Subclasses must not call this method.
909 */
910HRESULT Machine::i_registeredInit()
911{
912 AssertReturn(!i_isSessionMachine(), E_FAIL);
913 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
914 AssertReturn(mData->mUuid.isValid(), E_FAIL);
915 AssertReturn(!mData->mAccessible, E_FAIL);
916
917 HRESULT hrc = initDataAndChildObjects();
918 if (SUCCEEDED(hrc))
919 {
920 /* Temporarily reset the registered flag in order to let setters
921 * potentially called from loadSettings() succeed (isMutable() used in
922 * all setters will return FALSE for a Machine instance if mRegistered
923 * is TRUE). */
924 mData->mRegistered = FALSE;
925
926 PCVBOXCRYPTOIF pCryptoIf = NULL;
927 SecretKey *pKey = NULL;
928 const char *pszPassword = NULL;
929#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
930 /* Resolve password and cryptographic support interface if machine is encrypted. */
931 if (mData->mstrKeyId.isNotEmpty())
932 {
933 /* Get at the crpytographic interface. */
934 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
935 if (SUCCEEDED(hrc))
936 {
937 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
938 if (RT_SUCCESS(vrc))
939 pszPassword = (const char *)pKey->getKeyBuffer();
940 else
941 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
942 mData->mstrKeyId.c_str(), vrc);
943 }
944 }
945#else
946 RT_NOREF(pKey);
947#endif
948
949 if (SUCCEEDED(hrc))
950 {
951 try
952 {
953 // load and parse machine XML; this will throw on XML or logic errors
954 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
955 pCryptoIf, pszPassword);
956
957 if (mData->mUuid != mData->pMachineConfigFile->uuid)
958 throw setError(E_FAIL,
959 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
960 mData->pMachineConfigFile->uuid.raw(),
961 mData->m_strConfigFileFull.c_str(),
962 mData->mUuid.toString().c_str(),
963 mParent->i_settingsFilePath().c_str());
964
965#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
966 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
967 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
968 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
969 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
970
971 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
972 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
973 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
974 mData->pMachineConfigFile->uuid.raw());
975 else
976#endif
977 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
978 if (FAILED(hrc)) throw hrc;
979 }
980 catch (HRESULT err)
981 {
982 /* we assume that error info is set by the thrower */
983 hrc = err;
984 }
985 catch (...)
986 {
987 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
988 }
989
990 /* Restore the registered flag (even on failure) */
991 mData->mRegistered = TRUE;
992 }
993
994#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
995 if (pCryptoIf)
996 mParent->i_releaseCryptoIf(pCryptoIf);
997 if (pKey)
998 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
999#endif
1000 }
1001
1002 if (SUCCEEDED(hrc))
1003 {
1004 /* Set mAccessible to TRUE only if we successfully locked and loaded
1005 * the settings file */
1006 mData->mAccessible = TRUE;
1007
1008 /* commit all changes made during loading the settings file */
1009 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1010 /// @todo r=klaus for some reason the settings loading logic backs up
1011 // the settings, and therefore a commit is needed. Should probably be changed.
1012 }
1013 else
1014 {
1015 /* If the machine is registered, then, instead of returning a
1016 * failure, we mark it as inaccessible and set the result to
1017 * success to give it a try later */
1018
1019 /* fetch the current error info */
1020 mData->mAccessError = com::ErrorInfo();
1021 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1022
1023 /* rollback all changes */
1024 i_rollback(false /* aNotify */);
1025
1026 // uninit media from this machine's media registry, or else
1027 // reloading the settings will fail
1028 mParent->i_unregisterMachineMedia(i_getId());
1029
1030 /* uninitialize the common part to make sure all data is reset to
1031 * default (null) values */
1032 uninitDataAndChildObjects();
1033
1034 hrc = S_OK;
1035 }
1036
1037 return hrc;
1038}
1039
1040/**
1041 * Uninitializes the instance.
1042 * Called either from FinalRelease() or by the parent when it gets destroyed.
1043 *
1044 * @note The caller of this method must make sure that this object
1045 * a) doesn't have active callers on the current thread and b) is not locked
1046 * by the current thread; otherwise uninit() will hang either a) due to
1047 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1048 * a dead-lock caused by this thread waiting for all callers on the other
1049 * threads are done but preventing them from doing so by holding a lock.
1050 */
1051void Machine::uninit()
1052{
1053 LogFlowThisFuncEnter();
1054
1055 Assert(!isWriteLockOnCurrentThread());
1056
1057 Assert(!uRegistryNeedsSaving);
1058 if (uRegistryNeedsSaving)
1059 {
1060 AutoCaller autoCaller(this);
1061 if (SUCCEEDED(autoCaller.hrc()))
1062 {
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1065 }
1066 }
1067
1068 /* Enclose the state transition Ready->InUninit->NotReady */
1069 AutoUninitSpan autoUninitSpan(this);
1070 if (autoUninitSpan.uninitDone())
1071 return;
1072
1073 Assert(!i_isSnapshotMachine());
1074 Assert(!i_isSessionMachine());
1075 Assert(!!mData);
1076
1077 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1078 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1079
1080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 if (!mData->mSession.mMachine.isNull())
1083 {
1084 /* Theoretically, this can only happen if the VirtualBox server has been
1085 * terminated while there were clients running that owned open direct
1086 * sessions. Since in this case we are definitely called by
1087 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1088 * won't happen on the client watcher thread (because it has a
1089 * VirtualBox caller for the duration of the
1090 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1091 * cannot happen until the VirtualBox caller is released). This is
1092 * important, because SessionMachine::uninit() cannot correctly operate
1093 * after we return from this method (it expects the Machine instance is
1094 * still valid). We'll call it ourselves below.
1095 */
1096 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1097 (SessionMachine*)mData->mSession.mMachine));
1098
1099 if (Global::IsOnlineOrTransient(mData->mMachineState))
1100 {
1101 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1102 /* set machine state using SessionMachine reimplementation */
1103 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1104 }
1105
1106 /*
1107 * Uninitialize SessionMachine using public uninit() to indicate
1108 * an unexpected uninitialization.
1109 */
1110 mData->mSession.mMachine->uninit();
1111 /* SessionMachine::uninit() must set mSession.mMachine to null */
1112 Assert(mData->mSession.mMachine.isNull());
1113 }
1114
1115 // uninit media from this machine's media registry, if they're still there
1116 Guid uuidMachine(i_getId());
1117
1118 /* the lock is no more necessary (SessionMachine is uninitialized) */
1119 alock.release();
1120
1121 /* XXX This will fail with
1122 * "cannot be closed because it is still attached to 1 virtual machines"
1123 * because at this point we did not call uninitDataAndChildObjects() yet
1124 * and therefore also removeBackReference() for all these media was not called! */
1125
1126 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1127 mParent->i_unregisterMachineMedia(uuidMachine);
1128
1129 // has machine been modified?
1130 if (mData->flModifications)
1131 {
1132 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1133 i_rollback(false /* aNotify */);
1134 }
1135
1136 if (mData->mAccessible)
1137 uninitDataAndChildObjects();
1138
1139#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1140 if (mData->mpKeyStore != NULL)
1141 delete mData->mpKeyStore;
1142#endif
1143
1144 /* free the essential data structure last */
1145 mData.free();
1146
1147 LogFlowThisFuncLeave();
1148}
1149
1150// Wrapped IMachine properties
1151/////////////////////////////////////////////////////////////////////////////
1152HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1153{
1154 /* mParent is constant during life time, no need to lock */
1155 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1156 aParent = pVirtualBox;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1162{
1163 /* mPlatform is constant during life time, no need to lock */
1164 ComObjPtr<Platform> pPlatform(mPlatform);
1165 aPlatform = pPlatform;
1166
1167 return S_OK;
1168}
1169
1170HRESULT Machine::getAccessible(BOOL *aAccessible)
1171{
1172 /* In some cases (medium registry related), it is necessary to be able to
1173 * go through the list of all machines. Happens when an inaccessible VM
1174 * has a sensible medium registry. */
1175 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT hrc = S_OK;
1179
1180 if (!mData->mAccessible)
1181 {
1182 /* try to initialize the VM once more if not accessible */
1183
1184 AutoReinitSpan autoReinitSpan(this);
1185 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1186
1187#ifdef DEBUG
1188 LogFlowThisFunc(("Dumping media backreferences\n"));
1189 mParent->i_dumpAllBackRefs();
1190#endif
1191
1192 if (mData->pMachineConfigFile)
1193 {
1194 // reset the XML file to force loadSettings() (called from i_registeredInit())
1195 // to parse it again; the file might have changed
1196 delete mData->pMachineConfigFile;
1197 mData->pMachineConfigFile = NULL;
1198 }
1199
1200 hrc = i_registeredInit();
1201
1202 if (SUCCEEDED(hrc) && mData->mAccessible)
1203 {
1204 autoReinitSpan.setSucceeded();
1205
1206 /* make sure interesting parties will notice the accessibility
1207 * state change */
1208 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1209 mParent->i_onMachineDataChanged(mData->mUuid);
1210 }
1211 }
1212
1213 if (SUCCEEDED(hrc))
1214 *aAccessible = mData->mAccessible;
1215
1216 LogFlowThisFuncLeave();
1217
1218 return hrc;
1219}
1220
1221HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1226 {
1227 /* return shortly */
1228 aAccessError = NULL;
1229 return S_OK;
1230 }
1231
1232 HRESULT hrc = S_OK;
1233
1234 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1235 hrc = errorInfo.createObject();
1236 if (SUCCEEDED(hrc))
1237 {
1238 errorInfo->init(mData->mAccessError.getResultCode(),
1239 mData->mAccessError.getInterfaceID().ref(),
1240 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1241 Utf8Str(mData->mAccessError.getText()));
1242 aAccessError = errorInfo;
1243 }
1244
1245 return hrc;
1246}
1247
1248HRESULT Machine::getName(com::Utf8Str &aName)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 aName = mUserData->s.strName;
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::setName(const com::Utf8Str &aName)
1258{
1259 // prohibit setting a UUID only as the machine name, or else it can
1260 // never be found by findMachine()
1261 Guid test(aName);
1262
1263 if (test.isValid())
1264 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1265
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1269 if (FAILED(hrc)) return hrc;
1270
1271 i_setModified(IsModified_MachineData);
1272 mUserData.backup();
1273 mUserData->s.strName = aName;
1274
1275 return S_OK;
1276}
1277
1278HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 aDescription = mUserData->s.strDescription;
1283
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1288{
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 // this can be done in principle in any state as it doesn't affect the VM
1292 // significantly, but play safe by not messing around while complex
1293 // activities are going on
1294 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1295 if (FAILED(hrc)) return hrc;
1296
1297 i_setModified(IsModified_MachineData);
1298 mUserData.backup();
1299 mUserData->s.strDescription = aDescription;
1300
1301 return S_OK;
1302}
1303
1304HRESULT Machine::getId(com::Guid &aId)
1305{
1306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 aId = mData->mUuid;
1309
1310 return S_OK;
1311}
1312
1313HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1314{
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316 aGroups.resize(mUserData->s.llGroups.size());
1317 size_t i = 0;
1318 for (StringsList::const_iterator
1319 it = mUserData->s.llGroups.begin();
1320 it != mUserData->s.llGroups.end();
1321 ++it, ++i)
1322 aGroups[i] = (*it);
1323
1324 return S_OK;
1325}
1326
1327HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1328{
1329 StringsList llGroups;
1330 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1331 if (FAILED(hrc))
1332 return hrc;
1333
1334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1335
1336 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1337 if (FAILED(hrc)) return hrc;
1338
1339 i_setModified(IsModified_MachineData);
1340 mUserData.backup();
1341 mUserData->s.llGroups = llGroups;
1342
1343 mParent->i_onMachineGroupsChanged(mData->mUuid);
1344 return S_OK;
1345}
1346
1347HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1348{
1349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1350
1351 aOSTypeId = mUserData->s.strOsType;
1352
1353 return S_OK;
1354}
1355
1356HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1357{
1358 /* look up the object by Id to check it is valid */
1359 ComObjPtr<GuestOSType> pGuestOSType;
1360 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1361
1362 /* when setting, always use the "etalon" value for consistency -- lookup
1363 * by ID is case-insensitive and the input value may have different case */
1364 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1369 if (FAILED(hrc)) return hrc;
1370
1371 i_setModified(IsModified_MachineData);
1372 mUserData.backup();
1373 mUserData->s.strOsType = osTypeId;
1374
1375 return S_OK;
1376}
1377
1378HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1379{
1380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1381
1382 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1383
1384 return S_OK;
1385}
1386
1387HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1388{
1389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1390
1391 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1392 if (FAILED(hrc)) return hrc;
1393
1394 i_setModified(IsModified_MachineData);
1395 mHWData.backup();
1396 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1397
1398 return S_OK;
1399}
1400
1401HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1402{
1403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1404
1405 *aPointingHIDType = mHWData->mPointingHIDType;
1406
1407 return S_OK;
1408}
1409
1410HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1411{
1412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1413
1414 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1415 if (FAILED(hrc)) return hrc;
1416
1417 i_setModified(IsModified_MachineData);
1418 mHWData.backup();
1419 mHWData->mPointingHIDType = aPointingHIDType;
1420
1421 return S_OK;
1422}
1423
1424HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1425{
1426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 aParavirtDebug = mHWData->mParavirtDebug;
1429 return S_OK;
1430}
1431
1432HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1433{
1434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1435
1436 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1437 if (FAILED(hrc)) return hrc;
1438
1439 /** @todo Parse/validate options? */
1440 if (aParavirtDebug != mHWData->mParavirtDebug)
1441 {
1442 i_setModified(IsModified_MachineData);
1443 mHWData.backup();
1444 mHWData->mParavirtDebug = aParavirtDebug;
1445 }
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aParavirtProvider = mHWData->mParavirtProvider;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1460{
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1464 if (FAILED(hrc)) return hrc;
1465
1466 if (aParavirtProvider != mHWData->mParavirtProvider)
1467 {
1468 i_setModified(IsModified_MachineData);
1469 mHWData.backup();
1470 mHWData->mParavirtProvider = aParavirtProvider;
1471 }
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1477{
1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 *aParavirtProvider = mHWData->mParavirtProvider;
1481 switch (mHWData->mParavirtProvider)
1482 {
1483 case ParavirtProvider_None:
1484 case ParavirtProvider_HyperV:
1485 case ParavirtProvider_KVM:
1486 case ParavirtProvider_Minimal:
1487 break;
1488
1489 /* Resolve dynamic provider types to the effective types. */
1490 default:
1491 {
1492 ComObjPtr<GuestOSType> pGuestOSType;
1493 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1494 pGuestOSType);
1495 if (FAILED(hrc2) || pGuestOSType.isNull())
1496 {
1497 *aParavirtProvider = ParavirtProvider_None;
1498 break;
1499 }
1500
1501 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1502 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1503
1504 switch (mHWData->mParavirtProvider)
1505 {
1506 case ParavirtProvider_Legacy:
1507 {
1508 if (fOsXGuest)
1509 *aParavirtProvider = ParavirtProvider_Minimal;
1510 else
1511 *aParavirtProvider = ParavirtProvider_None;
1512 break;
1513 }
1514
1515 case ParavirtProvider_Default:
1516 {
1517 Assert(strlen(GUEST_OS_ID_STR_X64("")) > 0);
1518 if (fOsXGuest)
1519 *aParavirtProvider = ParavirtProvider_Minimal;
1520 else if ( mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows11")
1521 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows10")
1522 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows10")
1523 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows81")
1524 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows81")
1525 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows8")
1526 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows8")
1527 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows7")
1528 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows7")
1529 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("WindowsVista")
1530 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("WindowsVista")
1531 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1532 || mUserData->s.strOsType.startsWith("Windows201"))
1533 && mUserData->s.strOsType.endsWith(GUEST_OS_ID_STR_X64("")))
1534 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2012")
1535 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2012")
1536 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2008")
1537 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2008"))
1538 *aParavirtProvider = ParavirtProvider_HyperV;
1539 else if ( guestTypeFamilyId == "Linux"
1540 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux22") // Linux22 and Linux24{_64} excluded as they're too old
1541 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux24") // to have any KVM paravirtualization support.
1542 && mUserData->s.strOsType != GUEST_OS_ID_STR_X64("Linux24"))
1543 *aParavirtProvider = ParavirtProvider_KVM;
1544 else
1545 *aParavirtProvider = ParavirtProvider_None;
1546 break;
1547 }
1548
1549 default: AssertFailedBreak(); /* Shut up MSC. */
1550 }
1551 break;
1552 }
1553 }
1554
1555 Assert( *aParavirtProvider == ParavirtProvider_None
1556 || *aParavirtProvider == ParavirtProvider_Minimal
1557 || *aParavirtProvider == ParavirtProvider_HyperV
1558 || *aParavirtProvider == ParavirtProvider_KVM);
1559 return S_OK;
1560}
1561
1562HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1563{
1564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1565
1566 aHardwareVersion = mHWData->mHWVersion;
1567
1568 return S_OK;
1569}
1570
1571HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1572{
1573 /* check known version */
1574 Utf8Str hwVersion = aHardwareVersion;
1575 if ( hwVersion.compare("1") != 0
1576 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1577 return setError(E_INVALIDARG,
1578 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1579
1580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1581
1582 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1583 if (FAILED(hrc)) return hrc;
1584
1585 i_setModified(IsModified_MachineData);
1586 mHWData.backup();
1587 mHWData->mHWVersion = aHardwareVersion;
1588
1589 return S_OK;
1590}
1591
1592HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1593{
1594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 if (!mHWData->mHardwareUUID.isZero())
1597 aHardwareUUID = mHWData->mHardwareUUID;
1598 else
1599 aHardwareUUID = mData->mUuid;
1600
1601 return S_OK;
1602}
1603
1604HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1605{
1606 if (!aHardwareUUID.isValid())
1607 return E_INVALIDARG;
1608
1609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1610
1611 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1612 if (FAILED(hrc)) return hrc;
1613
1614 i_setModified(IsModified_MachineData);
1615 mHWData.backup();
1616 if (aHardwareUUID == mData->mUuid)
1617 mHWData->mHardwareUUID.clear();
1618 else
1619 mHWData->mHardwareUUID = aHardwareUUID;
1620
1621 return S_OK;
1622}
1623
1624HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1625{
1626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 *aMemorySize = mHWData->mMemorySize;
1629
1630 return S_OK;
1631}
1632
1633HRESULT Machine::setMemorySize(ULONG aMemorySize)
1634{
1635 /* check RAM limits */
1636 if ( aMemorySize < MM_RAM_MIN_IN_MB
1637 || aMemorySize > MM_RAM_MAX_IN_MB
1638 )
1639 return setError(E_INVALIDARG,
1640 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1641 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1642
1643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1646 if (FAILED(hrc)) return hrc;
1647
1648 i_setModified(IsModified_MachineData);
1649 mHWData.backup();
1650 mHWData->mMemorySize = aMemorySize;
1651
1652 return S_OK;
1653}
1654
1655HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1656{
1657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1658
1659 *aCPUCount = mHWData->mCPUCount;
1660
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setCPUCount(ULONG aCPUCount)
1665{
1666 /* check CPU limits */
1667 if ( aCPUCount < SchemaDefs::MinCPUCount
1668 || aCPUCount > SchemaDefs::MaxCPUCount
1669 )
1670 return setError(E_INVALIDARG,
1671 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1672 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1673
1674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1675
1676 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1677 if (mHWData->mCPUHotPlugEnabled)
1678 {
1679 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1680 {
1681 if (mHWData->mCPUAttached[idx])
1682 return setError(E_INVALIDARG,
1683 tr("There is still a CPU attached to socket %lu."
1684 "Detach the CPU before removing the socket"),
1685 aCPUCount, idx+1);
1686 }
1687 }
1688
1689 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1690 if (FAILED(hrc)) return hrc;
1691
1692 i_setModified(IsModified_MachineData);
1693 mHWData.backup();
1694 mHWData->mCPUCount = aCPUCount;
1695
1696 return S_OK;
1697}
1698
1699HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1700{
1701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1704
1705 return S_OK;
1706}
1707
1708HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1709{
1710 /* check throttle limits */
1711 if ( aCPUExecutionCap < 1
1712 || aCPUExecutionCap > 100
1713 )
1714 return setError(E_INVALIDARG,
1715 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1716 aCPUExecutionCap, 1, 100);
1717
1718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1719
1720 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1721 if (FAILED(hrc)) return hrc;
1722
1723 alock.release();
1724 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1725 alock.acquire();
1726 if (FAILED(hrc)) return hrc;
1727
1728 i_setModified(IsModified_MachineData);
1729 mHWData.backup();
1730 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1731
1732 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1733 if (Global::IsOnline(mData->mMachineState))
1734 i_saveSettings(NULL, alock);
1735
1736 return S_OK;
1737}
1738
1739HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1740{
1741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1742
1743 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1744
1745 return S_OK;
1746}
1747
1748HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1749{
1750 HRESULT hrc = S_OK;
1751
1752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 hrc = i_checkStateDependency(MutableStateDep);
1755 if (FAILED(hrc)) return hrc;
1756
1757 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1758 {
1759 if (aCPUHotPlugEnabled)
1760 {
1761 i_setModified(IsModified_MachineData);
1762 mHWData.backup();
1763
1764 /* Add the amount of CPUs currently attached */
1765 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1766 mHWData->mCPUAttached[i] = true;
1767 }
1768 else
1769 {
1770 /*
1771 * We can disable hotplug only if the amount of maximum CPUs is equal
1772 * to the amount of attached CPUs
1773 */
1774 unsigned cCpusAttached = 0;
1775 unsigned iHighestId = 0;
1776
1777 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1778 {
1779 if (mHWData->mCPUAttached[i])
1780 {
1781 cCpusAttached++;
1782 iHighestId = i;
1783 }
1784 }
1785
1786 if ( (cCpusAttached != mHWData->mCPUCount)
1787 || (iHighestId >= mHWData->mCPUCount))
1788 return setError(E_INVALIDARG,
1789 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1790
1791 i_setModified(IsModified_MachineData);
1792 mHWData.backup();
1793 }
1794 }
1795
1796 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1797
1798 return hrc;
1799}
1800
1801HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1802{
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804
1805 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1806
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1811{
1812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1813
1814 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1815 if (SUCCEEDED(hrc))
1816 {
1817 i_setModified(IsModified_MachineData);
1818 mHWData.backup();
1819 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1820 }
1821 return hrc;
1822}
1823
1824HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1825{
1826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1827 aCPUProfile = mHWData->mCpuProfile;
1828 return S_OK;
1829}
1830
1831HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1832{
1833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1834 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1835 if (SUCCEEDED(hrc))
1836 {
1837 i_setModified(IsModified_MachineData);
1838 mHWData.backup();
1839 /* Empty equals 'host'. */
1840 if (aCPUProfile.isNotEmpty())
1841 mHWData->mCpuProfile = aCPUProfile;
1842 else
1843 mHWData->mCpuProfile = "host";
1844 }
1845 return hrc;
1846}
1847
1848HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1849{
1850#ifdef VBOX_WITH_USB_CARDREADER
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1854
1855 return S_OK;
1856#else
1857 NOREF(aEmulatedUSBCardReaderEnabled);
1858 return E_NOTIMPL;
1859#endif
1860}
1861
1862HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1863{
1864#ifdef VBOX_WITH_USB_CARDREADER
1865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1868 if (FAILED(hrc)) return hrc;
1869
1870 i_setModified(IsModified_MachineData);
1871 mHWData.backup();
1872 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1873
1874 return S_OK;
1875#else
1876 NOREF(aEmulatedUSBCardReaderEnabled);
1877 return E_NOTIMPL;
1878#endif
1879}
1880
1881/** @todo this method should not be public */
1882HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1887
1888 return S_OK;
1889}
1890
1891/**
1892 * Set the memory balloon size.
1893 *
1894 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1895 * we have to make sure that we never call IGuest from here.
1896 */
1897HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1898{
1899 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1900#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1901 /* check limits */
1902 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1903 return setError(E_INVALIDARG,
1904 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1905 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1906
1907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1910 if (FAILED(hrc)) return hrc;
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1915
1916 return S_OK;
1917#else
1918 NOREF(aMemoryBalloonSize);
1919 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1920#endif
1921}
1922
1923HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1928 return S_OK;
1929}
1930
1931HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1932{
1933#ifdef VBOX_WITH_PAGE_SHARING
1934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1937 if (FAILED(hrc)) return hrc;
1938
1939 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1943 return S_OK;
1944#else
1945 NOREF(aPageFusionEnabled);
1946 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1947#endif
1948}
1949
1950HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1951{
1952 /* mFirmwareSettings is constant during life time, no need to lock */
1953 aFirmwareSettings = mFirmwareSettings;
1954
1955 return S_OK;
1956}
1957
1958HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1959{
1960 /* mTrustedPlatformModule is constant during life time, no need to lock */
1961 aTrustedPlatformModule = mTrustedPlatformModule;
1962
1963 return S_OK;
1964}
1965
1966HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1967{
1968 /* mNvramStore is constant during life time, no need to lock */
1969 aNvramStore = mNvramStore;
1970
1971 return S_OK;
1972}
1973
1974HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1975{
1976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 aRecordingSettings = mRecordingSettings;
1979
1980 return S_OK;
1981}
1982
1983HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1984{
1985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1986
1987 aGraphicsAdapter = mGraphicsAdapter;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2002{
2003 /** @todo (r=dmik):
2004 * 1. Allow to change the name of the snapshot folder containing snapshots
2005 * 2. Rename the folder on disk instead of just changing the property
2006 * value (to be smart and not to leave garbage). Note that it cannot be
2007 * done here because the change may be rolled back. Thus, the right
2008 * place is #saveSettings().
2009 */
2010
2011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2014 if (FAILED(hrc)) return hrc;
2015
2016 if (!mData->mCurrentSnapshot.isNull())
2017 return setError(E_FAIL,
2018 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2019
2020 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2021
2022 if (strSnapshotFolder.isEmpty())
2023 strSnapshotFolder = "Snapshots";
2024 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2025 if (RT_FAILURE(vrc))
2026 return setErrorBoth(E_FAIL, vrc,
2027 tr("Invalid snapshot folder '%s' (%Rrc)"),
2028 strSnapshotFolder.c_str(), vrc);
2029
2030 i_setModified(IsModified_MachineData);
2031 mUserData.backup();
2032
2033 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2034
2035 return S_OK;
2036}
2037
2038HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2039{
2040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2041
2042 aMediumAttachments.resize(mMediumAttachments->size());
2043 size_t i = 0;
2044 for (MediumAttachmentList::const_iterator
2045 it = mMediumAttachments->begin();
2046 it != mMediumAttachments->end();
2047 ++it, ++i)
2048 aMediumAttachments[i] = *it;
2049
2050 return S_OK;
2051}
2052
2053HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2054{
2055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2056
2057 Assert(!!mVRDEServer);
2058
2059 aVRDEServer = mVRDEServer;
2060
2061 return S_OK;
2062}
2063
2064HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2065{
2066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2067
2068 aAudioSettings = mAudioSettings;
2069
2070 return S_OK;
2071}
2072
2073HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2074{
2075#ifdef VBOX_WITH_VUSB
2076 clearError();
2077 MultiResult hrcMult(S_OK);
2078
2079# ifdef VBOX_WITH_USB
2080 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2081 if (FAILED(hrcMult)) return hrcMult;
2082# endif
2083
2084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2085
2086 aUSBControllers.resize(mUSBControllers->size());
2087 size_t i = 0;
2088 for (USBControllerList::const_iterator
2089 it = mUSBControllers->begin();
2090 it != mUSBControllers->end();
2091 ++it, ++i)
2092 aUSBControllers[i] = *it;
2093
2094 return S_OK;
2095#else
2096 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2097 * extended error info to indicate that USB is simply not available
2098 * (w/o treating it as a failure), for example, as in OSE */
2099 NOREF(aUSBControllers);
2100 ReturnComNotImplemented();
2101#endif /* VBOX_WITH_VUSB */
2102}
2103
2104HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2105{
2106#ifdef VBOX_WITH_VUSB
2107 clearError();
2108 MultiResult hrcMult(S_OK);
2109
2110# ifdef VBOX_WITH_USB
2111 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2112 if (FAILED(hrcMult)) return hrcMult;
2113# endif
2114
2115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2116
2117 aUSBDeviceFilters = mUSBDeviceFilters;
2118 return hrcMult;
2119#else
2120 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2121 * extended error info to indicate that USB is simply not available
2122 * (w/o treating it as a failure), for example, as in OSE */
2123 NOREF(aUSBDeviceFilters);
2124 ReturnComNotImplemented();
2125#endif /* VBOX_WITH_VUSB */
2126}
2127
2128HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2129{
2130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 aSettingsFilePath = mData->m_strConfigFileFull;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2138{
2139 RT_NOREF(aSettingsFilePath);
2140 ReturnComNotImplemented();
2141}
2142
2143HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2144{
2145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2148 if (FAILED(hrc)) return hrc;
2149
2150 if (!mData->pMachineConfigFile->fileExists())
2151 // this is a new machine, and no config file exists yet:
2152 *aSettingsModified = TRUE;
2153 else
2154 *aSettingsModified = (mData->flModifications != 0);
2155
2156 return S_OK;
2157}
2158
2159HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2160{
2161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 *aSessionState = mData->mSession.mState;
2164
2165 return S_OK;
2166}
2167
2168HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2169{
2170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 aSessionName = mData->mSession.mName;
2173
2174 return S_OK;
2175}
2176
2177HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2178{
2179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2180
2181 *aSessionPID = mData->mSession.mPID;
2182
2183 return S_OK;
2184}
2185
2186HRESULT Machine::getState(MachineState_T *aState)
2187{
2188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 *aState = mData->mMachineState;
2191 Assert(mData->mMachineState != MachineState_Null);
2192
2193 return S_OK;
2194}
2195
2196HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2197{
2198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2201
2202 return S_OK;
2203}
2204
2205HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2206{
2207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2208
2209 aStateFilePath = mSSData->strStateFilePath;
2210
2211 return S_OK;
2212}
2213
2214HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2215{
2216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2217
2218 i_getLogFolder(aLogFolder);
2219
2220 return S_OK;
2221}
2222
2223HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2224{
2225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2226
2227 aCurrentSnapshot = mData->mCurrentSnapshot;
2228
2229 return S_OK;
2230}
2231
2232HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2233{
2234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2235
2236 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2237 ? 0
2238 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2239
2240 return S_OK;
2241}
2242
2243HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2244{
2245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2246
2247 /* Note: for machines with no snapshots, we always return FALSE
2248 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2249 * reasons :) */
2250
2251 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2252 ? FALSE
2253 : mData->mCurrentStateModified;
2254
2255 return S_OK;
2256}
2257
2258HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2259{
2260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2261
2262 aSharedFolders.resize(mHWData->mSharedFolders.size());
2263 size_t i = 0;
2264 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2265 it = mHWData->mSharedFolders.begin();
2266 it != mHWData->mSharedFolders.end();
2267 ++it, ++i)
2268 aSharedFolders[i] = *it;
2269
2270 return S_OK;
2271}
2272
2273HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2274{
2275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2276
2277 *aClipboardMode = mHWData->mClipboardMode;
2278
2279 return S_OK;
2280}
2281
2282HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2283{
2284 HRESULT hrc = S_OK;
2285
2286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2287
2288 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2289 if (FAILED(hrc)) return hrc;
2290
2291 alock.release();
2292 hrc = i_onClipboardModeChange(aClipboardMode);
2293 alock.acquire();
2294 if (FAILED(hrc)) return hrc;
2295
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mClipboardMode = aClipboardMode;
2299
2300 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2301 if (Global::IsOnline(mData->mMachineState))
2302 i_saveSettings(NULL, alock);
2303
2304 return S_OK;
2305}
2306
2307HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2308{
2309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
2311 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2312
2313 return S_OK;
2314}
2315
2316HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2317{
2318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2321 if (FAILED(hrc)) return hrc;
2322
2323 alock.release();
2324 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2325 alock.acquire();
2326 if (FAILED(hrc)) return hrc;
2327
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2331
2332 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2333 if (Global::IsOnline(mData->mMachineState))
2334 i_saveSettings(NULL, alock);
2335
2336 return S_OK;
2337}
2338
2339HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2340{
2341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 *aDnDMode = mHWData->mDnDMode;
2344
2345 return S_OK;
2346}
2347
2348HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2349{
2350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2351
2352 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2353 if (FAILED(hrc)) return hrc;
2354
2355 alock.release();
2356 hrc = i_onDnDModeChange(aDnDMode);
2357
2358 alock.acquire();
2359 if (FAILED(hrc)) return hrc;
2360
2361 i_setModified(IsModified_MachineData);
2362 mHWData.backup();
2363 mHWData->mDnDMode = aDnDMode;
2364
2365 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2366 if (Global::IsOnline(mData->mMachineState))
2367 i_saveSettings(NULL, alock);
2368
2369 return S_OK;
2370}
2371
2372HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2373{
2374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2375
2376 aStorageControllers.resize(mStorageControllers->size());
2377 size_t i = 0;
2378 for (StorageControllerList::const_iterator
2379 it = mStorageControllers->begin();
2380 it != mStorageControllers->end();
2381 ++it, ++i)
2382 aStorageControllers[i] = *it;
2383
2384 return S_OK;
2385}
2386
2387HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2388{
2389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2390
2391 *aEnabled = mUserData->s.fTeleporterEnabled;
2392
2393 return S_OK;
2394}
2395
2396HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2397{
2398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2399
2400 /* Only allow it to be set to true when PoweredOff or Aborted.
2401 (Clearing it is always permitted.) */
2402 if ( aTeleporterEnabled
2403 && mData->mRegistered
2404 && ( !i_isSessionMachine()
2405 || ( mData->mMachineState != MachineState_PoweredOff
2406 && mData->mMachineState != MachineState_Teleported
2407 && mData->mMachineState != MachineState_Aborted
2408 )
2409 )
2410 )
2411 return setError(VBOX_E_INVALID_VM_STATE,
2412 tr("The machine is not powered off (state is %s)"),
2413 Global::stringifyMachineState(mData->mMachineState));
2414
2415 i_setModified(IsModified_MachineData);
2416 mUserData.backup();
2417 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2418
2419 return S_OK;
2420}
2421
2422HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2423{
2424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2425
2426 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2427
2428 return S_OK;
2429}
2430
2431HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2432{
2433 if (aTeleporterPort >= _64K)
2434 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2435
2436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2437
2438 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2439 if (FAILED(hrc)) return hrc;
2440
2441 i_setModified(IsModified_MachineData);
2442 mUserData.backup();
2443 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2444
2445 return S_OK;
2446}
2447
2448HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2449{
2450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2451
2452 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2453
2454 return S_OK;
2455}
2456
2457HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2458{
2459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2462 if (FAILED(hrc)) return hrc;
2463
2464 i_setModified(IsModified_MachineData);
2465 mUserData.backup();
2466 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2467
2468 return S_OK;
2469}
2470
2471HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2472{
2473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2474 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2475
2476 return S_OK;
2477}
2478
2479HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2480{
2481 /*
2482 * Hash the password first.
2483 */
2484 com::Utf8Str aT = aTeleporterPassword;
2485
2486 if (!aT.isEmpty())
2487 {
2488 if (VBoxIsPasswordHashed(&aT))
2489 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2490 VBoxHashPassword(&aT);
2491 }
2492
2493 /*
2494 * Do the update.
2495 */
2496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2497 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2498 if (SUCCEEDED(hrc))
2499 {
2500 i_setModified(IsModified_MachineData);
2501 mUserData.backup();
2502 mUserData->s.strTeleporterPassword = aT;
2503 }
2504
2505 return hrc;
2506}
2507
2508HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2509{
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2513
2514 return S_OK;
2515}
2516
2517HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2518{
2519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2522 if (FAILED(hrc)) return hrc;
2523
2524 i_setModified(IsModified_MachineData);
2525 mHWData.backup();
2526 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2527
2528 return S_OK;
2529}
2530
2531HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 *aIOCacheSize = mHWData->mIOCacheSize;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2541{
2542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2545 if (FAILED(hrc)) return hrc;
2546
2547 i_setModified(IsModified_MachineData);
2548 mHWData.backup();
2549 mHWData->mIOCacheSize = aIOCacheSize;
2550
2551 return S_OK;
2552}
2553
2554HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2555{
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2559 aKeyId = mSSData->strStateKeyId;
2560#else
2561 aKeyId = com::Utf8Str::Empty;
2562#endif
2563
2564 return S_OK;
2565}
2566
2567HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2568{
2569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2570
2571#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2572 aKeyStore = mSSData->strStateKeyStore;
2573#else
2574 aKeyStore = com::Utf8Str::Empty;
2575#endif
2576
2577 return S_OK;
2578}
2579
2580HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2581{
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2585 aKeyId = mData->mstrLogKeyId;
2586#else
2587 aKeyId = com::Utf8Str::Empty;
2588#endif
2589
2590 return S_OK;
2591}
2592
2593HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2594{
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2598 aKeyStore = mData->mstrLogKeyStore;
2599#else
2600 aKeyStore = com::Utf8Str::Empty;
2601#endif
2602
2603 return S_OK;
2604}
2605
2606HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2607{
2608 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2609
2610 return S_OK;
2611}
2612
2613
2614/**
2615 * @note Locks objects!
2616 */
2617HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2618 LockType_T aLockType)
2619{
2620 /* check the session state */
2621 SessionState_T state;
2622 HRESULT hrc = aSession->COMGETTER(State)(&state);
2623 if (FAILED(hrc)) return hrc;
2624
2625 if (state != SessionState_Unlocked)
2626 return setError(VBOX_E_INVALID_OBJECT_STATE,
2627 tr("The given session is busy"));
2628
2629 // get the client's IInternalSessionControl interface
2630 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2631 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2632 E_INVALIDARG);
2633
2634 // session name (only used in some code paths)
2635 Utf8Str strSessionName;
2636
2637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2638
2639 if (!mData->mRegistered)
2640 return setError(E_UNEXPECTED,
2641 tr("The machine '%s' is not registered"),
2642 mUserData->s.strName.c_str());
2643
2644 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2645
2646 SessionState_T oldState = mData->mSession.mState;
2647 /* Hack: in case the session is closing and there is a progress object
2648 * which allows waiting for the session to be closed, take the opportunity
2649 * and do a limited wait (max. 1 second). This helps a lot when the system
2650 * is busy and thus session closing can take a little while. */
2651 if ( mData->mSession.mState == SessionState_Unlocking
2652 && mData->mSession.mProgress)
2653 {
2654 alock.release();
2655 mData->mSession.mProgress->WaitForCompletion(1000);
2656 alock.acquire();
2657 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2658 }
2659
2660 // try again now
2661 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2662 // (i.e. session machine exists)
2663 && (aLockType == LockType_Shared) // caller wants a shared link to the
2664 // existing session that holds the write lock:
2665 )
2666 {
2667 // OK, share the session... we are now dealing with three processes:
2668 // 1) VBoxSVC (where this code runs);
2669 // 2) process C: the caller's client process (who wants a shared session);
2670 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2671
2672 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2673 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2674 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2675 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2676 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2677
2678 /*
2679 * Release the lock before calling the client process. It's safe here
2680 * since the only thing to do after we get the lock again is to add
2681 * the remote control to the list (which doesn't directly influence
2682 * anything).
2683 */
2684 alock.release();
2685
2686 // get the console of the session holding the write lock (this is a remote call)
2687 ComPtr<IConsole> pConsoleW;
2688 if (mData->mSession.mLockType == LockType_VM)
2689 {
2690 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2691 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2692 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2693 if (FAILED(hrc))
2694 // the failure may occur w/o any error info (from RPC), so provide one
2695 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2696 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2697 }
2698
2699 // share the session machine and W's console with the caller's session
2700 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2701 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2702 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2703
2704 if (FAILED(hrc))
2705 // the failure may occur w/o any error info (from RPC), so provide one
2706 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2707 alock.acquire();
2708
2709 // need to revalidate the state after acquiring the lock again
2710 if (mData->mSession.mState != SessionState_Locked)
2711 {
2712 pSessionControl->Uninitialize();
2713 return setError(VBOX_E_INVALID_SESSION_STATE,
2714 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2715 mUserData->s.strName.c_str());
2716 }
2717
2718 // add the caller's session to the list
2719 mData->mSession.mRemoteControls.push_back(pSessionControl);
2720 }
2721 else if ( mData->mSession.mState == SessionState_Locked
2722 || mData->mSession.mState == SessionState_Unlocking
2723 )
2724 {
2725 // sharing not permitted, or machine still unlocking:
2726 return setError(VBOX_E_INVALID_OBJECT_STATE,
2727 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2728 mUserData->s.strName.c_str());
2729 }
2730 else
2731 {
2732 // machine is not locked: then write-lock the machine (create the session machine)
2733
2734 // must not be busy
2735 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2736
2737 // get the caller's session PID
2738 RTPROCESS pid = NIL_RTPROCESS;
2739 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2740 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2741 Assert(pid != NIL_RTPROCESS);
2742
2743 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2744
2745 if (fLaunchingVMProcess)
2746 {
2747 if (mData->mSession.mPID == NIL_RTPROCESS)
2748 {
2749 // two or more clients racing for a lock, the one which set the
2750 // session state to Spawning will win, the others will get an
2751 // error as we can't decide here if waiting a little would help
2752 // (only for shared locks this would avoid an error)
2753 return setError(VBOX_E_INVALID_OBJECT_STATE,
2754 tr("The machine '%s' already has a lock request pending"),
2755 mUserData->s.strName.c_str());
2756 }
2757
2758 // this machine is awaiting for a spawning session to be opened:
2759 // then the calling process must be the one that got started by
2760 // LaunchVMProcess()
2761
2762 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2763 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2764
2765#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2766 /* Hardened windows builds spawns three processes when a VM is
2767 launched, the 3rd one is the one that will end up here. */
2768 RTPROCESS pidParent;
2769 int vrc = RTProcQueryParent(pid, &pidParent);
2770 if (RT_SUCCESS(vrc))
2771 vrc = RTProcQueryParent(pidParent, &pidParent);
2772 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2773 || vrc == VERR_ACCESS_DENIED)
2774 {
2775 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2776 mData->mSession.mPID = pid;
2777 }
2778#endif
2779
2780 if (mData->mSession.mPID != pid)
2781 return setError(E_ACCESSDENIED,
2782 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2783 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2784 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2785 }
2786
2787 // create the mutable SessionMachine from the current machine
2788 ComObjPtr<SessionMachine> sessionMachine;
2789 sessionMachine.createObject();
2790 hrc = sessionMachine->init(this);
2791 AssertComRC(hrc);
2792
2793 /* NOTE: doing return from this function after this point but
2794 * before the end is forbidden since it may call SessionMachine::uninit()
2795 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2796 * lock while still holding the Machine lock in alock so that a deadlock
2797 * is possible due to the wrong lock order. */
2798
2799 if (SUCCEEDED(hrc))
2800 {
2801 /*
2802 * Set the session state to Spawning to protect against subsequent
2803 * attempts to open a session and to unregister the machine after
2804 * we release the lock.
2805 */
2806 SessionState_T origState = mData->mSession.mState;
2807 mData->mSession.mState = SessionState_Spawning;
2808
2809#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2810 /* Get the client token ID to be passed to the client process */
2811 Utf8Str strTokenId;
2812 sessionMachine->i_getTokenId(strTokenId);
2813 Assert(!strTokenId.isEmpty());
2814#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2815 /* Get the client token to be passed to the client process */
2816 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2817 /* The token is now "owned" by pToken, fix refcount */
2818 if (!pToken.isNull())
2819 pToken->Release();
2820#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2821
2822 /*
2823 * Release the lock before calling the client process -- it will call
2824 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2825 * because the state is Spawning, so that LaunchVMProcess() and
2826 * LockMachine() calls will fail. This method, called before we
2827 * acquire the lock again, will fail because of the wrong PID.
2828 *
2829 * Note that mData->mSession.mRemoteControls accessed outside
2830 * the lock may not be modified when state is Spawning, so it's safe.
2831 */
2832 alock.release();
2833
2834 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2835#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2836 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2837#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2838 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2839 /* Now the token is owned by the client process. */
2840 pToken.setNull();
2841#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2842 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2843
2844 /* The failure may occur w/o any error info (from RPC), so provide one */
2845 if (FAILED(hrc))
2846 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2847
2848 // get session name, either to remember or to compare against
2849 // the already known session name.
2850 {
2851 Bstr bstrSessionName;
2852 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2853 if (SUCCEEDED(hrc2))
2854 strSessionName = bstrSessionName;
2855 }
2856
2857 if ( SUCCEEDED(hrc)
2858 && fLaunchingVMProcess
2859 )
2860 {
2861 /* complete the remote session initialization */
2862
2863 /* get the console from the direct session */
2864 ComPtr<IConsole> console;
2865 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2866 ComAssertComRC(hrc);
2867
2868 if (SUCCEEDED(hrc) && !console)
2869 {
2870 ComAssert(!!console);
2871 hrc = E_FAIL;
2872 }
2873
2874 /* assign machine & console to the remote session */
2875 if (SUCCEEDED(hrc))
2876 {
2877 /*
2878 * after LaunchVMProcess(), the first and the only
2879 * entry in remoteControls is that remote session
2880 */
2881 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2882 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2883 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2884
2885 /* The failure may occur w/o any error info (from RPC), so provide one */
2886 if (FAILED(hrc))
2887 setError(VBOX_E_VM_ERROR,
2888 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2889 }
2890
2891 if (FAILED(hrc))
2892 pSessionControl->Uninitialize();
2893 }
2894
2895 /* acquire the lock again */
2896 alock.acquire();
2897
2898 /* Restore the session state */
2899 mData->mSession.mState = origState;
2900 }
2901
2902 // finalize spawning anyway (this is why we don't return on errors above)
2903 if (fLaunchingVMProcess)
2904 {
2905 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2906 /* Note that the progress object is finalized later */
2907 /** @todo Consider checking mData->mSession.mProgress for cancellation
2908 * around here. */
2909
2910 /* We don't reset mSession.mPID here because it is necessary for
2911 * SessionMachine::uninit() to reap the child process later. */
2912
2913 if (FAILED(hrc))
2914 {
2915 /* Close the remote session, remove the remote control from the list
2916 * and reset session state to Closed (@note keep the code in sync
2917 * with the relevant part in checkForSpawnFailure()). */
2918
2919 Assert(mData->mSession.mRemoteControls.size() == 1);
2920 if (mData->mSession.mRemoteControls.size() == 1)
2921 {
2922 ErrorInfoKeeper eik;
2923 mData->mSession.mRemoteControls.front()->Uninitialize();
2924 }
2925
2926 mData->mSession.mRemoteControls.clear();
2927 mData->mSession.mState = SessionState_Unlocked;
2928 }
2929 }
2930 else
2931 {
2932 /* memorize PID of the directly opened session */
2933 if (SUCCEEDED(hrc))
2934 mData->mSession.mPID = pid;
2935 }
2936
2937 if (SUCCEEDED(hrc))
2938 {
2939 mData->mSession.mLockType = aLockType;
2940 /* memorize the direct session control and cache IUnknown for it */
2941 mData->mSession.mDirectControl = pSessionControl;
2942 mData->mSession.mState = SessionState_Locked;
2943 if (!fLaunchingVMProcess)
2944 mData->mSession.mName = strSessionName;
2945 /* associate the SessionMachine with this Machine */
2946 mData->mSession.mMachine = sessionMachine;
2947
2948 /* request an IUnknown pointer early from the remote party for later
2949 * identity checks (it will be internally cached within mDirectControl
2950 * at least on XPCOM) */
2951 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2952 NOREF(unk);
2953
2954#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2955 if (aLockType == LockType_VM)
2956 {
2957 /* get the console from the direct session */
2958 ComPtr<IConsole> console;
2959 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2960 ComAssertComRC(hrc);
2961 /* send passswords to console */
2962 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2963 it != mData->mpKeyStore->end();
2964 ++it)
2965 {
2966 SecretKey *pKey = it->second;
2967 pKey->retain();
2968 console->AddEncryptionPassword(Bstr(it->first).raw(),
2969 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2970 TRUE);
2971 pKey->release();
2972 }
2973
2974 }
2975#endif
2976 }
2977
2978 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2979 * would break the lock order */
2980 alock.release();
2981
2982 /* uninitialize the created session machine on failure */
2983 if (FAILED(hrc))
2984 sessionMachine->uninit();
2985 }
2986
2987 if (SUCCEEDED(hrc))
2988 {
2989 /*
2990 * tell the client watcher thread to update the set of
2991 * machines that have open sessions
2992 */
2993 mParent->i_updateClientWatcher();
2994
2995 if (oldState != SessionState_Locked)
2996 /* fire an event */
2997 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
2998 }
2999
3000 return hrc;
3001}
3002
3003/**
3004 * @note Locks objects!
3005 */
3006HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3007 const com::Utf8Str &aName,
3008 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3009 ComPtr<IProgress> &aProgress)
3010{
3011 Utf8Str strFrontend(aName);
3012 /* "emergencystop" doesn't need the session, so skip the checks/interface
3013 * retrieval. This code doesn't quite fit in here, but introducing a
3014 * special API method would be even more effort, and would require explicit
3015 * support by every API client. It's better to hide the feature a bit. */
3016 if (strFrontend != "emergencystop")
3017 CheckComArgNotNull(aSession);
3018
3019 HRESULT hrc = S_OK;
3020 if (strFrontend.isEmpty())
3021 {
3022 Bstr bstrFrontend;
3023 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3024 if (FAILED(hrc))
3025 return hrc;
3026 strFrontend = bstrFrontend;
3027 if (strFrontend.isEmpty())
3028 {
3029 ComPtr<ISystemProperties> systemProperties;
3030 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3031 if (FAILED(hrc))
3032 return hrc;
3033 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3034 if (FAILED(hrc))
3035 return hrc;
3036 strFrontend = bstrFrontend;
3037 }
3038 /* paranoia - emergencystop is not a valid default */
3039 if (strFrontend == "emergencystop")
3040 strFrontend = Utf8Str::Empty;
3041 }
3042 /* default frontend: Qt GUI */
3043 if (strFrontend.isEmpty())
3044 strFrontend = "GUI/Qt";
3045
3046 if (strFrontend != "emergencystop")
3047 {
3048 /* check the session state */
3049 SessionState_T state;
3050 hrc = aSession->COMGETTER(State)(&state);
3051 if (FAILED(hrc))
3052 return hrc;
3053
3054 if (state != SessionState_Unlocked)
3055 return setError(VBOX_E_INVALID_OBJECT_STATE,
3056 tr("The given session is busy"));
3057
3058 /* get the IInternalSessionControl interface */
3059 ComPtr<IInternalSessionControl> control(aSession);
3060 ComAssertMsgRet(!control.isNull(),
3061 ("No IInternalSessionControl interface"),
3062 E_INVALIDARG);
3063
3064 /* get the teleporter enable state for the progress object init. */
3065 BOOL fTeleporterEnabled;
3066 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3067 if (FAILED(hrc))
3068 return hrc;
3069
3070 /* create a progress object */
3071 ComObjPtr<ProgressProxy> progress;
3072 progress.createObject();
3073 hrc = progress->init(mParent,
3074 static_cast<IMachine*>(this),
3075 Bstr(tr("Starting VM")).raw(),
3076 TRUE /* aCancelable */,
3077 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3078 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3079 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3080 2 /* uFirstOperationWeight */,
3081 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3082 if (SUCCEEDED(hrc))
3083 {
3084 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3085 if (SUCCEEDED(hrc))
3086 {
3087 aProgress = progress;
3088
3089 /* signal the client watcher thread */
3090 mParent->i_updateClientWatcher();
3091
3092 /* fire an event */
3093 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3094 }
3095 }
3096 }
3097 else
3098 {
3099 /* no progress object - either instant success or failure */
3100 aProgress = NULL;
3101
3102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 if (mData->mSession.mState != SessionState_Locked)
3105 return setError(VBOX_E_INVALID_OBJECT_STATE,
3106 tr("The machine '%s' is not locked by a session"),
3107 mUserData->s.strName.c_str());
3108
3109 /* must have a VM process associated - do not kill normal API clients
3110 * with an open session */
3111 if (!Global::IsOnline(mData->mMachineState))
3112 return setError(VBOX_E_INVALID_OBJECT_STATE,
3113 tr("The machine '%s' does not have a VM process"),
3114 mUserData->s.strName.c_str());
3115
3116 /* forcibly terminate the VM process */
3117 if (mData->mSession.mPID != NIL_RTPROCESS)
3118 RTProcTerminate(mData->mSession.mPID);
3119
3120 /* signal the client watcher thread, as most likely the client has
3121 * been terminated */
3122 mParent->i_updateClientWatcher();
3123 }
3124
3125 return hrc;
3126}
3127
3128HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3129{
3130 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3131 return setError(E_INVALIDARG,
3132 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3133 aPosition, SchemaDefs::MaxBootPosition);
3134
3135 if (aDevice == DeviceType_USB)
3136 return setError(E_NOTIMPL,
3137 tr("Booting from USB device is currently not supported"));
3138
3139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3142 if (FAILED(hrc)) return hrc;
3143
3144 i_setModified(IsModified_MachineData);
3145 mHWData.backup();
3146 mHWData->mBootOrder[aPosition - 1] = aDevice;
3147
3148 return S_OK;
3149}
3150
3151HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3152{
3153 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3154 return setError(E_INVALIDARG,
3155 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3156 aPosition, SchemaDefs::MaxBootPosition);
3157
3158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3159
3160 *aDevice = mHWData->mBootOrder[aPosition - 1];
3161
3162 return S_OK;
3163}
3164
3165HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3166 LONG aControllerPort,
3167 LONG aDevice,
3168 DeviceType_T aType,
3169 const ComPtr<IMedium> &aMedium)
3170{
3171 IMedium *aM = aMedium;
3172 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3173 aName.c_str(), aControllerPort, aDevice, aType, aM));
3174
3175 // request the host lock first, since might be calling Host methods for getting host drives;
3176 // next, protect the media tree all the while we're in here, as well as our member variables
3177 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3178 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3179
3180 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3181 if (FAILED(hrc)) return hrc;
3182
3183 /// @todo NEWMEDIA implicit machine registration
3184 if (!mData->mRegistered)
3185 return setError(VBOX_E_INVALID_OBJECT_STATE,
3186 tr("Cannot attach storage devices to an unregistered machine"));
3187
3188 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3189
3190 /* Check for an existing controller. */
3191 ComObjPtr<StorageController> ctl;
3192 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3193 if (FAILED(hrc)) return hrc;
3194
3195 StorageControllerType_T ctrlType;
3196 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3197 if (FAILED(hrc))
3198 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3199
3200 bool fSilent = false;
3201
3202 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3203 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3204 if ( mData->mMachineState == MachineState_Paused
3205 && strReconfig == "1")
3206 fSilent = true;
3207
3208 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3209 bool fHotplug = false;
3210 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3211 fHotplug = true;
3212
3213 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3214 return setError(VBOX_E_INVALID_VM_STATE,
3215 tr("Controller '%s' does not support hot-plugging"),
3216 aName.c_str());
3217
3218 // check that the port and device are not out of range
3219 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3220 if (FAILED(hrc)) return hrc;
3221
3222 /* check if the device slot is already busy */
3223 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3224 aName,
3225 aControllerPort,
3226 aDevice);
3227 if (pAttachTemp)
3228 {
3229 Medium *pMedium = pAttachTemp->i_getMedium();
3230 if (pMedium)
3231 {
3232 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3233 return setError(VBOX_E_OBJECT_IN_USE,
3234 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3235 pMedium->i_getLocationFull().c_str(),
3236 aControllerPort,
3237 aDevice,
3238 aName.c_str());
3239 }
3240 else
3241 return setError(VBOX_E_OBJECT_IN_USE,
3242 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3243 aControllerPort, aDevice, aName.c_str());
3244 }
3245
3246 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3247 if (aMedium && medium.isNull())
3248 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3249
3250 AutoCaller mediumCaller(medium);
3251 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3252
3253 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3254
3255 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3256 if ( pAttachTemp
3257 && !medium.isNull()
3258 && ( medium->i_getType() != MediumType_Readonly
3259 || medium->i_getDeviceType() != DeviceType_DVD)
3260 )
3261 return setError(VBOX_E_OBJECT_IN_USE,
3262 tr("Medium '%s' is already attached to this virtual machine"),
3263 medium->i_getLocationFull().c_str());
3264
3265 if (!medium.isNull())
3266 {
3267 MediumType_T mtype = medium->i_getType();
3268 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3269 // For DVDs it's not written to the config file, so needs no global config
3270 // version bump. For floppies it's a new attribute "type", which is ignored
3271 // by older VirtualBox version, so needs no global config version bump either.
3272 // For hard disks this type is not accepted.
3273 if (mtype == MediumType_MultiAttach)
3274 {
3275 // This type is new with VirtualBox 4.0 and therefore requires settings
3276 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3277 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3278 // two reasons: The medium type is a property of the media registry tree, which
3279 // can reside in the global config file (for pre-4.0 media); we would therefore
3280 // possibly need to bump the global config version. We don't want to do that though
3281 // because that might make downgrading to pre-4.0 impossible.
3282 // As a result, we can only use these two new types if the medium is NOT in the
3283 // global registry:
3284 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3285 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3286 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3287 )
3288 return setError(VBOX_E_INVALID_OBJECT_STATE,
3289 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3290 "to machines that were created with VirtualBox 4.0 or later"),
3291 medium->i_getLocationFull().c_str());
3292 }
3293 }
3294
3295 bool fIndirect = false;
3296 if (!medium.isNull())
3297 fIndirect = medium->i_isReadOnly();
3298 bool associate = true;
3299
3300 do
3301 {
3302 if ( aType == DeviceType_HardDisk
3303 && mMediumAttachments.isBackedUp())
3304 {
3305 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3306
3307 /* check if the medium was attached to the VM before we started
3308 * changing attachments in which case the attachment just needs to
3309 * be restored */
3310 pAttachTemp = i_findAttachment(oldAtts, medium);
3311 if (pAttachTemp)
3312 {
3313 AssertReturn(!fIndirect, E_FAIL);
3314
3315 /* see if it's the same bus/channel/device */
3316 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3317 {
3318 /* the simplest case: restore the whole attachment
3319 * and return, nothing else to do */
3320 mMediumAttachments->push_back(pAttachTemp);
3321
3322 /* Reattach the medium to the VM. */
3323 if (fHotplug || fSilent)
3324 {
3325 mediumLock.release();
3326 treeLock.release();
3327 alock.release();
3328
3329 MediumLockList *pMediumLockList(new MediumLockList());
3330
3331 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3332 medium /* pToLockWrite */,
3333 false /* fMediumLockWriteAll */,
3334 NULL,
3335 *pMediumLockList);
3336 alock.acquire();
3337 if (FAILED(hrc))
3338 delete pMediumLockList;
3339 else
3340 {
3341 Assert(mData->mSession.mLockedMedia.IsLocked());
3342 mData->mSession.mLockedMedia.Unlock();
3343 alock.release();
3344 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3345 mData->mSession.mLockedMedia.Lock();
3346 alock.acquire();
3347 }
3348 alock.release();
3349
3350 if (SUCCEEDED(hrc))
3351 {
3352 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3353 /* Remove lock list in case of error. */
3354 if (FAILED(hrc))
3355 {
3356 mData->mSession.mLockedMedia.Unlock();
3357 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3358 mData->mSession.mLockedMedia.Lock();
3359 }
3360 }
3361 }
3362
3363 return S_OK;
3364 }
3365
3366 /* bus/channel/device differ; we need a new attachment object,
3367 * but don't try to associate it again */
3368 associate = false;
3369 break;
3370 }
3371 }
3372
3373 /* go further only if the attachment is to be indirect */
3374 if (!fIndirect)
3375 break;
3376
3377 /* perform the so called smart attachment logic for indirect
3378 * attachments. Note that smart attachment is only applicable to base
3379 * hard disks. */
3380
3381 if (medium->i_getParent().isNull())
3382 {
3383 /* first, investigate the backup copy of the current hard disk
3384 * attachments to make it possible to re-attach existing diffs to
3385 * another device slot w/o losing their contents */
3386 if (mMediumAttachments.isBackedUp())
3387 {
3388 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3389
3390 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3391 uint32_t foundLevel = 0;
3392
3393 for (MediumAttachmentList::const_iterator
3394 it = oldAtts.begin();
3395 it != oldAtts.end();
3396 ++it)
3397 {
3398 uint32_t level = 0;
3399 MediumAttachment *pAttach = *it;
3400 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3401 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3402 if (pMedium.isNull())
3403 continue;
3404
3405 if (pMedium->i_getBase(&level) == medium)
3406 {
3407 /* skip the hard disk if its currently attached (we
3408 * cannot attach the same hard disk twice) */
3409 if (i_findAttachment(*mMediumAttachments.data(),
3410 pMedium))
3411 continue;
3412
3413 /* matched device, channel and bus (i.e. attached to the
3414 * same place) will win and immediately stop the search;
3415 * otherwise the attachment that has the youngest
3416 * descendant of medium will be used
3417 */
3418 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3419 {
3420 /* the simplest case: restore the whole attachment
3421 * and return, nothing else to do */
3422 mMediumAttachments->push_back(*it);
3423
3424 /* Reattach the medium to the VM. */
3425 if (fHotplug || fSilent)
3426 {
3427 mediumLock.release();
3428 treeLock.release();
3429 alock.release();
3430
3431 MediumLockList *pMediumLockList(new MediumLockList());
3432
3433 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3434 medium /* pToLockWrite */,
3435 false /* fMediumLockWriteAll */,
3436 NULL,
3437 *pMediumLockList);
3438 alock.acquire();
3439 if (FAILED(hrc))
3440 delete pMediumLockList;
3441 else
3442 {
3443 Assert(mData->mSession.mLockedMedia.IsLocked());
3444 mData->mSession.mLockedMedia.Unlock();
3445 alock.release();
3446 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3447 mData->mSession.mLockedMedia.Lock();
3448 alock.acquire();
3449 }
3450 alock.release();
3451
3452 if (SUCCEEDED(hrc))
3453 {
3454 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3455 /* Remove lock list in case of error. */
3456 if (FAILED(hrc))
3457 {
3458 mData->mSession.mLockedMedia.Unlock();
3459 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3460 mData->mSession.mLockedMedia.Lock();
3461 }
3462 }
3463 }
3464
3465 return S_OK;
3466 }
3467 else if ( foundIt == oldAtts.end()
3468 || level > foundLevel /* prefer younger */
3469 )
3470 {
3471 foundIt = it;
3472 foundLevel = level;
3473 }
3474 }
3475 }
3476
3477 if (foundIt != oldAtts.end())
3478 {
3479 /* use the previously attached hard disk */
3480 medium = (*foundIt)->i_getMedium();
3481 mediumCaller.attach(medium);
3482 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3483 mediumLock.attach(medium);
3484 /* not implicit, doesn't require association with this VM */
3485 fIndirect = false;
3486 associate = false;
3487 /* go right to the MediumAttachment creation */
3488 break;
3489 }
3490 }
3491
3492 /* must give up the medium lock and medium tree lock as below we
3493 * go over snapshots, which needs a lock with higher lock order. */
3494 mediumLock.release();
3495 treeLock.release();
3496
3497 /* then, search through snapshots for the best diff in the given
3498 * hard disk's chain to base the new diff on */
3499
3500 ComObjPtr<Medium> base;
3501 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3502 while (snap)
3503 {
3504 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3505
3506 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3507
3508 MediumAttachment *pAttachFound = NULL;
3509 uint32_t foundLevel = 0;
3510
3511 for (MediumAttachmentList::const_iterator
3512 it = snapAtts.begin();
3513 it != snapAtts.end();
3514 ++it)
3515 {
3516 MediumAttachment *pAttach = *it;
3517 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3518 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3519 if (pMedium.isNull())
3520 continue;
3521
3522 uint32_t level = 0;
3523 if (pMedium->i_getBase(&level) == medium)
3524 {
3525 /* matched device, channel and bus (i.e. attached to the
3526 * same place) will win and immediately stop the search;
3527 * otherwise the attachment that has the youngest
3528 * descendant of medium will be used
3529 */
3530 if ( pAttach->i_getDevice() == aDevice
3531 && pAttach->i_getPort() == aControllerPort
3532 && pAttach->i_getControllerName() == aName
3533 )
3534 {
3535 pAttachFound = pAttach;
3536 break;
3537 }
3538 else if ( !pAttachFound
3539 || level > foundLevel /* prefer younger */
3540 )
3541 {
3542 pAttachFound = pAttach;
3543 foundLevel = level;
3544 }
3545 }
3546 }
3547
3548 if (pAttachFound)
3549 {
3550 base = pAttachFound->i_getMedium();
3551 break;
3552 }
3553
3554 snap = snap->i_getParent();
3555 }
3556
3557 /* re-lock medium tree and the medium, as we need it below */
3558 treeLock.acquire();
3559 mediumLock.acquire();
3560
3561 /* found a suitable diff, use it as a base */
3562 if (!base.isNull())
3563 {
3564 medium = base;
3565 mediumCaller.attach(medium);
3566 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3567 mediumLock.attach(medium);
3568 }
3569 }
3570
3571 Utf8Str strFullSnapshotFolder;
3572 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3573
3574 ComObjPtr<Medium> diff;
3575 diff.createObject();
3576 // store this diff in the same registry as the parent
3577 Guid uuidRegistryParent;
3578 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3579 {
3580 // parent image has no registry: this can happen if we're attaching a new immutable
3581 // image that has not yet been attached (medium then points to the base and we're
3582 // creating the diff image for the immutable, and the parent is not yet registered);
3583 // put the parent in the machine registry then
3584 mediumLock.release();
3585 treeLock.release();
3586 alock.release();
3587 i_addMediumToRegistry(medium);
3588 alock.acquire();
3589 treeLock.acquire();
3590 mediumLock.acquire();
3591 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3592 }
3593 hrc = diff->init(mParent,
3594 medium->i_getPreferredDiffFormat(),
3595 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3596 uuidRegistryParent,
3597 DeviceType_HardDisk);
3598 if (FAILED(hrc)) return hrc;
3599
3600 /* Apply the normal locking logic to the entire chain. */
3601 MediumLockList *pMediumLockList(new MediumLockList());
3602 mediumLock.release();
3603 treeLock.release();
3604 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3605 diff /* pToLockWrite */,
3606 false /* fMediumLockWriteAll */,
3607 medium,
3608 *pMediumLockList);
3609 treeLock.acquire();
3610 mediumLock.acquire();
3611 if (SUCCEEDED(hrc))
3612 {
3613 mediumLock.release();
3614 treeLock.release();
3615 hrc = pMediumLockList->Lock();
3616 treeLock.acquire();
3617 mediumLock.acquire();
3618 if (FAILED(hrc))
3619 setError(hrc,
3620 tr("Could not lock medium when creating diff '%s'"),
3621 diff->i_getLocationFull().c_str());
3622 else
3623 {
3624 /* will release the lock before the potentially lengthy
3625 * operation, so protect with the special state */
3626 MachineState_T oldState = mData->mMachineState;
3627 i_setMachineState(MachineState_SettingUp);
3628
3629 mediumLock.release();
3630 treeLock.release();
3631 alock.release();
3632
3633 hrc = medium->i_createDiffStorage(diff,
3634 medium->i_getPreferredDiffVariant(),
3635 pMediumLockList,
3636 NULL /* aProgress */,
3637 true /* aWait */,
3638 false /* aNotify */);
3639
3640 alock.acquire();
3641 treeLock.acquire();
3642 mediumLock.acquire();
3643
3644 i_setMachineState(oldState);
3645 }
3646 }
3647
3648 /* Unlock the media and free the associated memory. */
3649 delete pMediumLockList;
3650
3651 if (FAILED(hrc)) return hrc;
3652
3653 /* use the created diff for the actual attachment */
3654 medium = diff;
3655 mediumCaller.attach(medium);
3656 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3657 mediumLock.attach(medium);
3658 }
3659 while (0);
3660
3661 ComObjPtr<MediumAttachment> attachment;
3662 attachment.createObject();
3663 hrc = attachment->init(this,
3664 medium,
3665 aName,
3666 aControllerPort,
3667 aDevice,
3668 aType,
3669 fIndirect,
3670 false /* fPassthrough */,
3671 false /* fTempEject */,
3672 false /* fNonRotational */,
3673 false /* fDiscard */,
3674 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3675 Utf8Str::Empty);
3676 if (FAILED(hrc)) return hrc;
3677
3678 if (associate && !medium.isNull())
3679 {
3680 // as the last step, associate the medium to the VM
3681 hrc = medium->i_addBackReference(mData->mUuid);
3682 // here we can fail because of Deleting, or being in process of creating a Diff
3683 if (FAILED(hrc)) return hrc;
3684
3685 mediumLock.release();
3686 treeLock.release();
3687 alock.release();
3688 i_addMediumToRegistry(medium);
3689 alock.acquire();
3690 treeLock.acquire();
3691 mediumLock.acquire();
3692 }
3693
3694 /* success: finally remember the attachment */
3695 i_setModified(IsModified_Storage);
3696 mMediumAttachments.backup();
3697 mMediumAttachments->push_back(attachment);
3698
3699 mediumLock.release();
3700 treeLock.release();
3701 alock.release();
3702
3703 if (fHotplug || fSilent)
3704 {
3705 if (!medium.isNull())
3706 {
3707 MediumLockList *pMediumLockList(new MediumLockList());
3708
3709 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3710 medium /* pToLockWrite */,
3711 false /* fMediumLockWriteAll */,
3712 NULL,
3713 *pMediumLockList);
3714 alock.acquire();
3715 if (FAILED(hrc))
3716 delete pMediumLockList;
3717 else
3718 {
3719 Assert(mData->mSession.mLockedMedia.IsLocked());
3720 mData->mSession.mLockedMedia.Unlock();
3721 alock.release();
3722 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3723 mData->mSession.mLockedMedia.Lock();
3724 alock.acquire();
3725 }
3726 alock.release();
3727 }
3728
3729 if (SUCCEEDED(hrc))
3730 {
3731 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3732 /* Remove lock list in case of error. */
3733 if (FAILED(hrc))
3734 {
3735 mData->mSession.mLockedMedia.Unlock();
3736 mData->mSession.mLockedMedia.Remove(attachment);
3737 mData->mSession.mLockedMedia.Lock();
3738 }
3739 }
3740 }
3741
3742 /* Save modified registries, but skip this machine as it's the caller's
3743 * job to save its settings like all other settings changes. */
3744 mParent->i_unmarkRegistryModified(i_getId());
3745 mParent->i_saveModifiedRegistries();
3746
3747 if (SUCCEEDED(hrc))
3748 {
3749 if (fIndirect && medium != aM)
3750 mParent->i_onMediumConfigChanged(medium);
3751 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3752 }
3753
3754 return hrc;
3755}
3756
3757HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3758 LONG aDevice)
3759{
3760 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3761
3762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3763
3764 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3765 if (FAILED(hrc)) return hrc;
3766
3767 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3768
3769 /* Check for an existing controller. */
3770 ComObjPtr<StorageController> ctl;
3771 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3772 if (FAILED(hrc)) return hrc;
3773
3774 StorageControllerType_T ctrlType;
3775 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3776 if (FAILED(hrc))
3777 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3778
3779 bool fSilent = false;
3780 Utf8Str strReconfig;
3781
3782 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3783 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3784 if ( mData->mMachineState == MachineState_Paused
3785 && strReconfig == "1")
3786 fSilent = true;
3787
3788 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3789 bool fHotplug = false;
3790 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3791 fHotplug = true;
3792
3793 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3794 return setError(VBOX_E_INVALID_VM_STATE,
3795 tr("Controller '%s' does not support hot-plugging"),
3796 aName.c_str());
3797
3798 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3799 aName,
3800 aControllerPort,
3801 aDevice);
3802 if (!pAttach)
3803 return setError(VBOX_E_OBJECT_NOT_FOUND,
3804 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3805 aDevice, aControllerPort, aName.c_str());
3806
3807 if (fHotplug && !pAttach->i_getHotPluggable())
3808 return setError(VBOX_E_NOT_SUPPORTED,
3809 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3810 aDevice, aControllerPort, aName.c_str());
3811
3812 /*
3813 * The VM has to detach the device before we delete any implicit diffs.
3814 * If this fails we can roll back without loosing data.
3815 */
3816 if (fHotplug || fSilent)
3817 {
3818 alock.release();
3819 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3820 alock.acquire();
3821 }
3822 if (FAILED(hrc)) return hrc;
3823
3824 /* If we are here everything went well and we can delete the implicit now. */
3825 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3826
3827 alock.release();
3828
3829 /* Save modified registries, but skip this machine as it's the caller's
3830 * job to save its settings like all other settings changes. */
3831 mParent->i_unmarkRegistryModified(i_getId());
3832 mParent->i_saveModifiedRegistries();
3833
3834 if (SUCCEEDED(hrc))
3835 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3836
3837 return hrc;
3838}
3839
3840HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3841 LONG aDevice, BOOL aPassthrough)
3842{
3843 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3844 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3845
3846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3847
3848 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3849 if (FAILED(hrc)) return hrc;
3850
3851 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3852
3853 /* Check for an existing controller. */
3854 ComObjPtr<StorageController> ctl;
3855 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3856 if (FAILED(hrc)) return hrc;
3857
3858 StorageControllerType_T ctrlType;
3859 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3860 if (FAILED(hrc))
3861 return setError(E_FAIL,
3862 tr("Could not get type of controller '%s'"),
3863 aName.c_str());
3864
3865 bool fSilent = false;
3866 Utf8Str strReconfig;
3867
3868 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3869 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3870 if ( mData->mMachineState == MachineState_Paused
3871 && strReconfig == "1")
3872 fSilent = true;
3873
3874 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3875 bool fHotplug = false;
3876 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3877 fHotplug = true;
3878
3879 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3880 return setError(VBOX_E_INVALID_VM_STATE,
3881 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3882 aName.c_str());
3883
3884 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3885 aName,
3886 aControllerPort,
3887 aDevice);
3888 if (!pAttach)
3889 return setError(VBOX_E_OBJECT_NOT_FOUND,
3890 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3891 aDevice, aControllerPort, aName.c_str());
3892
3893
3894 i_setModified(IsModified_Storage);
3895 mMediumAttachments.backup();
3896
3897 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3898
3899 if (pAttach->i_getType() != DeviceType_DVD)
3900 return setError(E_INVALIDARG,
3901 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3902 aDevice, aControllerPort, aName.c_str());
3903
3904 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3905
3906 pAttach->i_updatePassthrough(!!aPassthrough);
3907
3908 attLock.release();
3909 alock.release();
3910 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3911 if (SUCCEEDED(hrc) && fValueChanged)
3912 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3913
3914 return hrc;
3915}
3916
3917HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3918 LONG aDevice, BOOL aTemporaryEject)
3919{
3920
3921 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3922 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3923
3924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3925
3926 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3927 if (FAILED(hrc)) return hrc;
3928
3929 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3930 aName,
3931 aControllerPort,
3932 aDevice);
3933 if (!pAttach)
3934 return setError(VBOX_E_OBJECT_NOT_FOUND,
3935 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3936 aDevice, aControllerPort, aName.c_str());
3937
3938
3939 i_setModified(IsModified_Storage);
3940 mMediumAttachments.backup();
3941
3942 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3943
3944 if (pAttach->i_getType() != DeviceType_DVD)
3945 return setError(E_INVALIDARG,
3946 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3947 aDevice, aControllerPort, aName.c_str());
3948 pAttach->i_updateTempEject(!!aTemporaryEject);
3949
3950 return S_OK;
3951}
3952
3953HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3954 LONG aDevice, BOOL aNonRotational)
3955{
3956
3957 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3958 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3959
3960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3961
3962 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3963 if (FAILED(hrc)) return hrc;
3964
3965 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3966
3967 if (Global::IsOnlineOrTransient(mData->mMachineState))
3968 return setError(VBOX_E_INVALID_VM_STATE,
3969 tr("Invalid machine state: %s"),
3970 Global::stringifyMachineState(mData->mMachineState));
3971
3972 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3973 aName,
3974 aControllerPort,
3975 aDevice);
3976 if (!pAttach)
3977 return setError(VBOX_E_OBJECT_NOT_FOUND,
3978 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3979 aDevice, aControllerPort, aName.c_str());
3980
3981
3982 i_setModified(IsModified_Storage);
3983 mMediumAttachments.backup();
3984
3985 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3986
3987 if (pAttach->i_getType() != DeviceType_HardDisk)
3988 return setError(E_INVALIDARG,
3989 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"),
3990 aDevice, aControllerPort, aName.c_str());
3991 pAttach->i_updateNonRotational(!!aNonRotational);
3992
3993 return S_OK;
3994}
3995
3996HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
3997 LONG aDevice, BOOL aDiscard)
3998{
3999
4000 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4001 aName.c_str(), aControllerPort, aDevice, aDiscard));
4002
4003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4004
4005 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4006 if (FAILED(hrc)) return hrc;
4007
4008 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4009
4010 if (Global::IsOnlineOrTransient(mData->mMachineState))
4011 return setError(VBOX_E_INVALID_VM_STATE,
4012 tr("Invalid machine state: %s"),
4013 Global::stringifyMachineState(mData->mMachineState));
4014
4015 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4016 aName,
4017 aControllerPort,
4018 aDevice);
4019 if (!pAttach)
4020 return setError(VBOX_E_OBJECT_NOT_FOUND,
4021 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4022 aDevice, aControllerPort, aName.c_str());
4023
4024
4025 i_setModified(IsModified_Storage);
4026 mMediumAttachments.backup();
4027
4028 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4029
4030 if (pAttach->i_getType() != DeviceType_HardDisk)
4031 return setError(E_INVALIDARG,
4032 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"),
4033 aDevice, aControllerPort, aName.c_str());
4034 pAttach->i_updateDiscard(!!aDiscard);
4035
4036 return S_OK;
4037}
4038
4039HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4040 LONG aDevice, BOOL aHotPluggable)
4041{
4042 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4043 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4044
4045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4046
4047 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4048 if (FAILED(hrc)) return hrc;
4049
4050 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4051
4052 if (Global::IsOnlineOrTransient(mData->mMachineState))
4053 return setError(VBOX_E_INVALID_VM_STATE,
4054 tr("Invalid machine state: %s"),
4055 Global::stringifyMachineState(mData->mMachineState));
4056
4057 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4058 aName,
4059 aControllerPort,
4060 aDevice);
4061 if (!pAttach)
4062 return setError(VBOX_E_OBJECT_NOT_FOUND,
4063 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4064 aDevice, aControllerPort, aName.c_str());
4065
4066 /* Check for an existing controller. */
4067 ComObjPtr<StorageController> ctl;
4068 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4069 if (FAILED(hrc)) return hrc;
4070
4071 StorageControllerType_T ctrlType;
4072 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4073 if (FAILED(hrc))
4074 return setError(E_FAIL,
4075 tr("Could not get type of controller '%s'"),
4076 aName.c_str());
4077
4078 if (!i_isControllerHotplugCapable(ctrlType))
4079 return setError(VBOX_E_NOT_SUPPORTED,
4080 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4081 aName.c_str());
4082
4083 /* silently ignore attempts to modify the hot-plug status of USB devices */
4084 if (ctrlType == StorageControllerType_USB)
4085 return S_OK;
4086
4087 i_setModified(IsModified_Storage);
4088 mMediumAttachments.backup();
4089
4090 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4091
4092 if (pAttach->i_getType() == DeviceType_Floppy)
4093 return setError(E_INVALIDARG,
4094 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"),
4095 aDevice, aControllerPort, aName.c_str());
4096 pAttach->i_updateHotPluggable(!!aHotPluggable);
4097
4098 return S_OK;
4099}
4100
4101HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4102 LONG aDevice)
4103{
4104 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4105 aName.c_str(), aControllerPort, aDevice));
4106
4107 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4108}
4109
4110HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4111 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4112{
4113 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4114 aName.c_str(), aControllerPort, aDevice));
4115
4116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4117
4118 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4119 if (FAILED(hrc)) return hrc;
4120
4121 if (Global::IsOnlineOrTransient(mData->mMachineState))
4122 return setError(VBOX_E_INVALID_VM_STATE,
4123 tr("Invalid machine state: %s"),
4124 Global::stringifyMachineState(mData->mMachineState));
4125
4126 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4127 aName,
4128 aControllerPort,
4129 aDevice);
4130 if (!pAttach)
4131 return setError(VBOX_E_OBJECT_NOT_FOUND,
4132 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4133 aDevice, aControllerPort, aName.c_str());
4134
4135
4136 i_setModified(IsModified_Storage);
4137 mMediumAttachments.backup();
4138
4139 IBandwidthGroup *iB = aBandwidthGroup;
4140 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4141 if (aBandwidthGroup && group.isNull())
4142 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4143
4144 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4145
4146 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4147 if (strBandwidthGroupOld.isNotEmpty())
4148 {
4149 /* Get the bandwidth group object and release it - this must not fail. */
4150 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4151 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4152 Assert(SUCCEEDED(hrc));
4153
4154 pBandwidthGroupOld->i_release();
4155 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4156 }
4157
4158 if (!group.isNull())
4159 {
4160 group->i_reference();
4161 pAttach->i_updateBandwidthGroup(group->i_getName());
4162 }
4163
4164 return S_OK;
4165}
4166
4167HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4168 LONG aControllerPort,
4169 LONG aDevice,
4170 DeviceType_T aType)
4171{
4172 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4173 aName.c_str(), aControllerPort, aDevice, aType));
4174
4175 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4176}
4177
4178
4179HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4180 LONG aControllerPort,
4181 LONG aDevice,
4182 BOOL aForce)
4183{
4184 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4185 aName.c_str(), aControllerPort, aForce));
4186
4187 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4188}
4189
4190HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4191 LONG aControllerPort,
4192 LONG aDevice,
4193 const ComPtr<IMedium> &aMedium,
4194 BOOL aForce)
4195{
4196 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4197 aName.c_str(), aControllerPort, aDevice, aForce));
4198
4199 // request the host lock first, since might be calling Host methods for getting host drives;
4200 // next, protect the media tree all the while we're in here, as well as our member variables
4201 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4202 this->lockHandle(),
4203 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4204
4205 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4206 if (FAILED(hrc)) return hrc;
4207
4208 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4209 aName,
4210 aControllerPort,
4211 aDevice);
4212 if (pAttach.isNull())
4213 return setError(VBOX_E_OBJECT_NOT_FOUND,
4214 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4215 aDevice, aControllerPort, aName.c_str());
4216
4217 /* Remember previously mounted medium. The medium before taking the
4218 * backup is not necessarily the same thing. */
4219 ComObjPtr<Medium> oldmedium;
4220 oldmedium = pAttach->i_getMedium();
4221
4222 IMedium *iM = aMedium;
4223 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4224 if (aMedium && pMedium.isNull())
4225 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4226
4227 /* Check if potential medium is already mounted */
4228 if (pMedium == oldmedium)
4229 return S_OK;
4230
4231 AutoCaller mediumCaller(pMedium);
4232 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4233
4234 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4235 if (pMedium)
4236 {
4237 DeviceType_T mediumType = pAttach->i_getType();
4238 switch (mediumType)
4239 {
4240 case DeviceType_DVD:
4241 case DeviceType_Floppy:
4242 break;
4243
4244 default:
4245 return setError(VBOX_E_INVALID_OBJECT_STATE,
4246 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4247 aControllerPort,
4248 aDevice,
4249 aName.c_str());
4250 }
4251 }
4252
4253 i_setModified(IsModified_Storage);
4254 mMediumAttachments.backup();
4255
4256 {
4257 // The backup operation makes the pAttach reference point to the
4258 // old settings. Re-get the correct reference.
4259 pAttach = i_findAttachment(*mMediumAttachments.data(),
4260 aName,
4261 aControllerPort,
4262 aDevice);
4263 if (!oldmedium.isNull())
4264 oldmedium->i_removeBackReference(mData->mUuid);
4265 if (!pMedium.isNull())
4266 {
4267 pMedium->i_addBackReference(mData->mUuid);
4268
4269 mediumLock.release();
4270 multiLock.release();
4271 i_addMediumToRegistry(pMedium);
4272 multiLock.acquire();
4273 mediumLock.acquire();
4274 }
4275
4276 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4277 pAttach->i_updateMedium(pMedium);
4278 }
4279
4280 i_setModified(IsModified_Storage);
4281
4282 mediumLock.release();
4283 multiLock.release();
4284 hrc = i_onMediumChange(pAttach, aForce);
4285 multiLock.acquire();
4286 mediumLock.acquire();
4287
4288 /* On error roll back this change only. */
4289 if (FAILED(hrc))
4290 {
4291 if (!pMedium.isNull())
4292 pMedium->i_removeBackReference(mData->mUuid);
4293 pAttach = i_findAttachment(*mMediumAttachments.data(),
4294 aName,
4295 aControllerPort,
4296 aDevice);
4297 /* If the attachment is gone in the meantime, bail out. */
4298 if (pAttach.isNull())
4299 return hrc;
4300 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4301 if (!oldmedium.isNull())
4302 oldmedium->i_addBackReference(mData->mUuid);
4303 pAttach->i_updateMedium(oldmedium);
4304 }
4305
4306 mediumLock.release();
4307 multiLock.release();
4308
4309 /* Save modified registries, but skip this machine as it's the caller's
4310 * job to save its settings like all other settings changes. */
4311 mParent->i_unmarkRegistryModified(i_getId());
4312 mParent->i_saveModifiedRegistries();
4313
4314 return hrc;
4315}
4316HRESULT Machine::getMedium(const com::Utf8Str &aName,
4317 LONG aControllerPort,
4318 LONG aDevice,
4319 ComPtr<IMedium> &aMedium)
4320{
4321 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4322 aName.c_str(), aControllerPort, aDevice));
4323
4324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4325
4326 aMedium = NULL;
4327
4328 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4329 aName,
4330 aControllerPort,
4331 aDevice);
4332 if (pAttach.isNull())
4333 return setError(VBOX_E_OBJECT_NOT_FOUND,
4334 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4335 aDevice, aControllerPort, aName.c_str());
4336
4337 aMedium = pAttach->i_getMedium();
4338
4339 return S_OK;
4340}
4341
4342HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4343{
4344 if (aSlot < RT_ELEMENTS(mSerialPorts))
4345 {
4346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4347 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4348 return S_OK;
4349 }
4350 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4351}
4352
4353HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4354{
4355 if (aSlot < RT_ELEMENTS(mParallelPorts))
4356 {
4357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4358 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4359 return S_OK;
4360 }
4361 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4362}
4363
4364
4365HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4366{
4367 /* Do not assert if slot is out of range, just return the advertised
4368 status. testdriver/vbox.py triggers this in logVmInfo. */
4369 if (aSlot >= mNetworkAdapters.size())
4370 return setError(E_INVALIDARG,
4371 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4372 aSlot, mNetworkAdapters.size());
4373
4374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4375
4376 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4377
4378 return S_OK;
4379}
4380
4381HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4382{
4383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4384
4385 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4386 size_t i = 0;
4387 for (settings::StringsMap::const_iterator
4388 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4389 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4390 ++it, ++i)
4391 aKeys[i] = it->first;
4392
4393 return S_OK;
4394}
4395
4396 /**
4397 * @note Locks this object for reading.
4398 */
4399HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4400 com::Utf8Str &aValue)
4401{
4402 /* start with nothing found */
4403 aValue = "";
4404
4405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4406
4407 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4408 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4409 // found:
4410 aValue = it->second; // source is a Utf8Str
4411
4412 /* return the result to caller (may be empty) */
4413 return S_OK;
4414}
4415
4416 /**
4417 * @note Locks mParent for writing + this object for writing.
4418 */
4419HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4420{
4421 /* Because control characters in aKey have caused problems in the settings
4422 * they are rejected unless the key should be deleted. */
4423 if (!aValue.isEmpty())
4424 {
4425 for (size_t i = 0; i < aKey.length(); ++i)
4426 {
4427 char ch = aKey[i];
4428 if (RTLocCIsCntrl(ch))
4429 return E_INVALIDARG;
4430 }
4431 }
4432
4433 Utf8Str strOldValue; // empty
4434
4435 // locking note: we only hold the read lock briefly to look up the old value,
4436 // then release it and call the onExtraCanChange callbacks. There is a small
4437 // chance of a race insofar as the callback might be called twice if two callers
4438 // change the same key at the same time, but that's a much better solution
4439 // than the deadlock we had here before. The actual changing of the extradata
4440 // is then performed under the write lock and race-free.
4441
4442 // look up the old value first; if nothing has changed then we need not do anything
4443 {
4444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4445
4446 // For snapshots don't even think about allowing changes, extradata
4447 // is global for a machine, so there is nothing snapshot specific.
4448 if (i_isSnapshotMachine())
4449 return setError(VBOX_E_INVALID_VM_STATE,
4450 tr("Cannot set extradata for a snapshot"));
4451
4452 // check if the right IMachine instance is used
4453 if (mData->mRegistered && !i_isSessionMachine())
4454 return setError(VBOX_E_INVALID_VM_STATE,
4455 tr("Cannot set extradata for an immutable machine"));
4456
4457 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4458 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4459 strOldValue = it->second;
4460 }
4461
4462 bool fChanged;
4463 if ((fChanged = (strOldValue != aValue)))
4464 {
4465 // ask for permission from all listeners outside the locks;
4466 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4467 // lock to copy the list of callbacks to invoke
4468 Bstr bstrError;
4469 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4470 {
4471 const char *sep = bstrError.isEmpty() ? "" : ": ";
4472 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4473 return setError(E_ACCESSDENIED,
4474 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4475 aKey.c_str(),
4476 aValue.c_str(),
4477 sep,
4478 bstrError.raw());
4479 }
4480
4481 // data is changing and change not vetoed: then write it out under the lock
4482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4483
4484 if (aValue.isEmpty())
4485 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4486 else
4487 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4488 // creates a new key if needed
4489
4490 bool fNeedsGlobalSaveSettings = false;
4491 // This saving of settings is tricky: there is no "old state" for the
4492 // extradata items at all (unlike all other settings), so the old/new
4493 // settings comparison would give a wrong result!
4494 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4495
4496 if (fNeedsGlobalSaveSettings)
4497 {
4498 // save the global settings; for that we should hold only the VirtualBox lock
4499 alock.release();
4500 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4501 mParent->i_saveSettings();
4502 }
4503 }
4504
4505 // fire notification outside the lock
4506 if (fChanged)
4507 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4508
4509 return S_OK;
4510}
4511
4512HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4513{
4514 aProgress = NULL;
4515 NOREF(aSettingsFilePath);
4516 ReturnComNotImplemented();
4517}
4518
4519HRESULT Machine::saveSettings()
4520{
4521 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4522
4523 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4524 if (FAILED(hrc)) return hrc;
4525
4526 /* the settings file path may never be null */
4527 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4528
4529 /* save all VM data excluding snapshots */
4530 bool fNeedsGlobalSaveSettings = false;
4531 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4532 mlock.release();
4533
4534 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4535 {
4536 // save the global settings; for that we should hold only the VirtualBox lock
4537 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4538 hrc = mParent->i_saveSettings();
4539 }
4540
4541 return hrc;
4542}
4543
4544
4545HRESULT Machine::discardSettings()
4546{
4547 /*
4548 * We need to take the machine list lock here as well as the machine one
4549 * or we'll get into trouble should any media stuff require rolling back.
4550 *
4551 * Details:
4552 *
4553 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4554 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4555 * 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]
4556 * 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
4557 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4558 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4559 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4560 * 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
4561 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4562 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4563 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4564 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4565 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4566 * 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]
4567 * 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] (*)
4568 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4569 * 0:005> k
4570 * # Child-SP RetAddr Call Site
4571 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4572 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4573 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4574 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4575 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4576 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4577 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4578 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4579 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4580 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4581 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4582 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4583 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4584 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4585 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4586 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4587 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4588 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4589 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4590 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4591 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4592 *
4593 */
4594 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4596
4597 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4598 if (FAILED(hrc)) return hrc;
4599
4600 /*
4601 * during this rollback, the session will be notified if data has
4602 * been actually changed
4603 */
4604 i_rollback(true /* aNotify */);
4605
4606 return S_OK;
4607}
4608
4609/** @note Locks objects! */
4610HRESULT Machine::unregister(AutoCaller &autoCaller,
4611 CleanupMode_T aCleanupMode,
4612 std::vector<ComPtr<IMedium> > &aMedia)
4613{
4614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4615
4616 Guid id(i_getId());
4617
4618 if (mData->mSession.mState != SessionState_Unlocked)
4619 return setError(VBOX_E_INVALID_OBJECT_STATE,
4620 tr("Cannot unregister the machine '%s' while it is locked"),
4621 mUserData->s.strName.c_str());
4622
4623 // wait for state dependents to drop to zero
4624 i_ensureNoStateDependencies(alock);
4625
4626 if (!mData->mAccessible)
4627 {
4628 // inaccessible machines can only be unregistered; uninitialize ourselves
4629 // here because currently there may be no unregistered that are inaccessible
4630 // (this state combination is not supported). Note releasing the caller and
4631 // leaving the lock before calling uninit()
4632 alock.release();
4633 autoCaller.release();
4634
4635 uninit();
4636
4637 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4638 // calls VirtualBox::i_saveSettings()
4639
4640 return S_OK;
4641 }
4642
4643 HRESULT hrc = S_OK;
4644 mData->llFilesToDelete.clear();
4645
4646 if (!mSSData->strStateFilePath.isEmpty())
4647 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4648
4649 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4650 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4651 mData->llFilesToDelete.push_back(strNVRAMFile);
4652
4653 // This list collects the medium objects from all medium attachments
4654 // which we will detach from the machine and its snapshots, in a specific
4655 // order which allows for closing all media without getting "media in use"
4656 // errors, simply by going through the list from the front to the back:
4657 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4658 // and must be closed before the parent media from the snapshots, or closing the parents
4659 // will fail because they still have children);
4660 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4661 // the root ("first") snapshot of the machine.
4662 MediaList llMedia;
4663
4664 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4665 && mMediumAttachments->size()
4666 )
4667 {
4668 // we have media attachments: detach them all and add the Medium objects to our list
4669 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4670 }
4671
4672 if (mData->mFirstSnapshot)
4673 {
4674 // add the media from the medium attachments of the snapshots to
4675 // llMedia as well, after the "main" machine media;
4676 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4677 // snapshot machine, depth first.
4678
4679 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4680 MachineState_T oldState = mData->mMachineState;
4681 mData->mMachineState = MachineState_DeletingSnapshot;
4682
4683 // make a copy of the first snapshot reference so the refcount does not
4684 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4685 // (would hang due to the AutoCaller voodoo)
4686 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4687
4688 // GO!
4689 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4690
4691 mData->mMachineState = oldState;
4692 }
4693
4694 if (FAILED(hrc))
4695 {
4696 i_rollbackMedia();
4697 return hrc;
4698 }
4699
4700 // commit all the media changes made above
4701 i_commitMedia();
4702
4703 mData->mRegistered = false;
4704
4705 // machine lock no longer needed
4706 alock.release();
4707
4708 /* Make sure that the settings of the current VM are not saved, because
4709 * they are rather crippled at this point to meet the cleanup expectations
4710 * and there's no point destroying the VM config on disk just because. */
4711 mParent->i_unmarkRegistryModified(id);
4712
4713 // return media to caller
4714 aMedia.resize(llMedia.size());
4715 size_t i = 0;
4716 for (MediaList::const_iterator
4717 it = llMedia.begin();
4718 it != llMedia.end();
4719 ++it, ++i)
4720 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4721
4722 mParent->i_unregisterMachine(this, aCleanupMode, id);
4723 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4724
4725 return S_OK;
4726}
4727
4728/**
4729 * Task record for deleting a machine config.
4730 */
4731class Machine::DeleteConfigTask
4732 : public Machine::Task
4733{
4734public:
4735 DeleteConfigTask(Machine *m,
4736 Progress *p,
4737 const Utf8Str &t,
4738 const RTCList<ComPtr<IMedium> > &llMedia,
4739 const StringsList &llFilesToDelete)
4740 : Task(m, p, t),
4741 m_llMedia(llMedia),
4742 m_llFilesToDelete(llFilesToDelete)
4743 {}
4744
4745private:
4746 void handler()
4747 {
4748 try
4749 {
4750 m_pMachine->i_deleteConfigHandler(*this);
4751 }
4752 catch (...)
4753 {
4754 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4755 }
4756 }
4757
4758 RTCList<ComPtr<IMedium> > m_llMedia;
4759 StringsList m_llFilesToDelete;
4760
4761 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4762};
4763
4764/**
4765 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4766 * SessionMachine::taskHandler().
4767 *
4768 * @note Locks this object for writing.
4769 *
4770 * @param task
4771 */
4772void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4773{
4774 LogFlowThisFuncEnter();
4775
4776 AutoCaller autoCaller(this);
4777 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4778 if (FAILED(autoCaller.hrc()))
4779 {
4780 /* we might have been uninitialized because the session was accidentally
4781 * closed by the client, so don't assert */
4782 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4783 task.m_pProgress->i_notifyComplete(hrc);
4784 LogFlowThisFuncLeave();
4785 return;
4786 }
4787
4788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4789
4790 HRESULT hrc;
4791 try
4792 {
4793 ULONG uLogHistoryCount = 3;
4794 ComPtr<ISystemProperties> systemProperties;
4795 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4796 if (FAILED(hrc)) throw hrc;
4797
4798 if (!systemProperties.isNull())
4799 {
4800 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4801 if (FAILED(hrc)) throw hrc;
4802 }
4803
4804 MachineState_T oldState = mData->mMachineState;
4805 i_setMachineState(MachineState_SettingUp);
4806 alock.release();
4807 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4808 {
4809 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4810 {
4811 AutoCaller mac(pMedium);
4812 if (FAILED(mac.hrc())) throw mac.hrc();
4813 Utf8Str strLocation = pMedium->i_getLocationFull();
4814 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4815 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4816 if (FAILED(hrc)) throw hrc;
4817 }
4818 if (pMedium->i_isMediumFormatFile())
4819 {
4820 ComPtr<IProgress> pProgress2;
4821 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4822 if (FAILED(hrc)) throw hrc;
4823 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4824 if (FAILED(hrc)) throw hrc;
4825 }
4826
4827 /* Close the medium, deliberately without checking the return
4828 * code, and without leaving any trace in the error info, as
4829 * a failure here is a very minor issue, which shouldn't happen
4830 * as above we even managed to delete the medium. */
4831 {
4832 ErrorInfoKeeper eik;
4833 pMedium->Close();
4834 }
4835 }
4836 i_setMachineState(oldState);
4837 alock.acquire();
4838
4839 // delete the files pushed on the task list by Machine::Delete()
4840 // (this includes saved states of the machine and snapshots and
4841 // medium storage files from the IMedium list passed in, and the
4842 // machine XML file)
4843 for (StringsList::const_iterator
4844 it = task.m_llFilesToDelete.begin();
4845 it != task.m_llFilesToDelete.end();
4846 ++it)
4847 {
4848 const Utf8Str &strFile = *it;
4849 LogFunc(("Deleting file %s\n", strFile.c_str()));
4850 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4851 if (FAILED(hrc)) throw hrc;
4852 i_deleteFile(strFile);
4853 }
4854
4855 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4856 if (FAILED(hrc)) throw hrc;
4857
4858 /* delete the settings only when the file actually exists */
4859 if (mData->pMachineConfigFile->fileExists())
4860 {
4861 /* Delete any backup or uncommitted XML files. Ignore failures.
4862 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4863 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4864 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4865 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4866 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4867 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4868
4869 /* delete the Logs folder, nothing important should be left
4870 * there (we don't check for errors because the user might have
4871 * some private files there that we don't want to delete) */
4872 Utf8Str logFolder;
4873 getLogFolder(logFolder);
4874 Assert(logFolder.length());
4875 if (RTDirExists(logFolder.c_str()))
4876 {
4877 /* Delete all VBox.log[.N] files from the Logs folder
4878 * (this must be in sync with the rotation logic in
4879 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4880 * files that may have been created by the GUI. */
4881 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4882 i_deleteFile(log, true /* fIgnoreFailures */);
4883 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4884 i_deleteFile(log, true /* fIgnoreFailures */);
4885 for (ULONG i = uLogHistoryCount; i > 0; i--)
4886 {
4887 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4888 i_deleteFile(log, true /* fIgnoreFailures */);
4889 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4890 i_deleteFile(log, true /* fIgnoreFailures */);
4891 }
4892 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4893 i_deleteFile(log, true /* fIgnoreFailures */);
4894#if defined(RT_OS_WINDOWS)
4895 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4896 i_deleteFile(log, true /* fIgnoreFailures */);
4897 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4898 i_deleteFile(log, true /* fIgnoreFailures */);
4899#endif
4900
4901 RTDirRemove(logFolder.c_str());
4902 }
4903
4904 /* delete the Snapshots folder, nothing important should be left
4905 * there (we don't check for errors because the user might have
4906 * some private files there that we don't want to delete) */
4907 Utf8Str strFullSnapshotFolder;
4908 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4909 Assert(!strFullSnapshotFolder.isEmpty());
4910 if (RTDirExists(strFullSnapshotFolder.c_str()))
4911 RTDirRemove(strFullSnapshotFolder.c_str());
4912
4913 // delete the directory that contains the settings file, but only
4914 // if it matches the VM name
4915 Utf8Str settingsDir;
4916 if (i_isInOwnDir(&settingsDir))
4917 RTDirRemove(settingsDir.c_str());
4918 }
4919
4920 alock.release();
4921
4922 mParent->i_saveModifiedRegistries();
4923 }
4924 catch (HRESULT hrcXcpt)
4925 {
4926 hrc = hrcXcpt;
4927 }
4928
4929 task.m_pProgress->i_notifyComplete(hrc);
4930
4931 LogFlowThisFuncLeave();
4932}
4933
4934HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4935{
4936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4937
4938 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4939 if (FAILED(hrc)) return hrc;
4940
4941 if (mData->mRegistered)
4942 return setError(VBOX_E_INVALID_VM_STATE,
4943 tr("Cannot delete settings of a registered machine"));
4944
4945 // collect files to delete
4946 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4947 // machine config file
4948 if (mData->pMachineConfigFile->fileExists())
4949 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4950 // backup of machine config file
4951 Utf8Str strTmp(mData->m_strConfigFileFull);
4952 strTmp.append("-prev");
4953 if (RTFileExists(strTmp.c_str()))
4954 llFilesToDelete.push_back(strTmp);
4955
4956 RTCList<ComPtr<IMedium> > llMedia;
4957 for (size_t i = 0; i < aMedia.size(); ++i)
4958 {
4959 IMedium *pIMedium(aMedia[i]);
4960 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4961 if (pMedium.isNull())
4962 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4963 SafeArray<BSTR> ids;
4964 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4965 if (FAILED(hrc)) return hrc;
4966 /* At this point the medium should not have any back references
4967 * anymore. If it has it is attached to another VM and *must* not
4968 * deleted. */
4969 if (ids.size() < 1)
4970 llMedia.append(pMedium);
4971 }
4972
4973 ComObjPtr<Progress> pProgress;
4974 pProgress.createObject();
4975 hrc = pProgress->init(i_getVirtualBox(),
4976 static_cast<IMachine*>(this) /* aInitiator */,
4977 tr("Deleting files"),
4978 true /* fCancellable */,
4979 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4980 tr("Collecting file inventory"));
4981 if (FAILED(hrc))
4982 return hrc;
4983
4984 /* create and start the task on a separate thread (note that it will not
4985 * start working until we release alock) */
4986 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
4987 hrc = pTask->createThread();
4988 pTask = NULL;
4989 if (FAILED(hrc))
4990 return hrc;
4991
4992 pProgress.queryInterfaceTo(aProgress.asOutParam());
4993
4994 LogFlowFuncLeave();
4995
4996 return S_OK;
4997}
4998
4999HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5000{
5001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 ComObjPtr<Snapshot> pSnapshot;
5004 HRESULT hrc;
5005
5006 if (aNameOrId.isEmpty())
5007 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5008 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5009 else
5010 {
5011 Guid uuid(aNameOrId);
5012 if (uuid.isValid())
5013 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5014 else
5015 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5016 }
5017 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5018
5019 return hrc;
5020}
5021
5022HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5023 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5024{
5025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5026
5027 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5028 if (FAILED(hrc)) return hrc;
5029
5030 ComObjPtr<SharedFolder> sharedFolder;
5031 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5032 if (SUCCEEDED(hrc))
5033 return setError(VBOX_E_OBJECT_IN_USE,
5034 tr("Shared folder named '%s' already exists"),
5035 aName.c_str());
5036
5037 SymlinkPolicy_T enmSymlinkPolicy = SymlinkPolicy_None;
5038 sharedFolder.createObject();
5039 hrc = sharedFolder->init(i_getMachine(),
5040 aName,
5041 aHostPath,
5042 !!aWritable,
5043 !!aAutomount,
5044 aAutoMountPoint,
5045 true /* fFailOnError */,
5046 enmSymlinkPolicy);
5047 if (FAILED(hrc)) return hrc;
5048
5049 i_setModified(IsModified_SharedFolders);
5050 mHWData.backup();
5051 mHWData->mSharedFolders.push_back(sharedFolder);
5052
5053 /* inform the direct session if any */
5054 alock.release();
5055 i_onSharedFolderChange();
5056
5057 return S_OK;
5058}
5059
5060HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5061{
5062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5063
5064 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5065 if (FAILED(hrc)) return hrc;
5066
5067 ComObjPtr<SharedFolder> sharedFolder;
5068 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5069 if (FAILED(hrc)) return hrc;
5070
5071 i_setModified(IsModified_SharedFolders);
5072 mHWData.backup();
5073 mHWData->mSharedFolders.remove(sharedFolder);
5074
5075 /* inform the direct session if any */
5076 alock.release();
5077 i_onSharedFolderChange();
5078
5079 return S_OK;
5080}
5081
5082HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5083{
5084 /* start with No */
5085 *aCanShow = FALSE;
5086
5087 ComPtr<IInternalSessionControl> directControl;
5088 {
5089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5090
5091 if (mData->mSession.mState != SessionState_Locked)
5092 return setError(VBOX_E_INVALID_VM_STATE,
5093 tr("Machine is not locked for session (session state: %s)"),
5094 Global::stringifySessionState(mData->mSession.mState));
5095
5096 if (mData->mSession.mLockType == LockType_VM)
5097 directControl = mData->mSession.mDirectControl;
5098 }
5099
5100 /* ignore calls made after #OnSessionEnd() is called */
5101 if (!directControl)
5102 return S_OK;
5103
5104 LONG64 dummy;
5105 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5106}
5107
5108HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5109{
5110 ComPtr<IInternalSessionControl> directControl;
5111 {
5112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5113
5114 if (mData->mSession.mState != SessionState_Locked)
5115 return setError(E_FAIL,
5116 tr("Machine is not locked for session (session state: %s)"),
5117 Global::stringifySessionState(mData->mSession.mState));
5118
5119 if (mData->mSession.mLockType == LockType_VM)
5120 directControl = mData->mSession.mDirectControl;
5121 }
5122
5123 /* ignore calls made after #OnSessionEnd() is called */
5124 if (!directControl)
5125 return S_OK;
5126
5127 BOOL dummy;
5128 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5129}
5130
5131#ifdef VBOX_WITH_GUEST_PROPS
5132/**
5133 * Look up a guest property in VBoxSVC's internal structures.
5134 */
5135HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5136 com::Utf8Str &aValue,
5137 LONG64 *aTimestamp,
5138 com::Utf8Str &aFlags) const
5139{
5140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5141
5142 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5143 if (it != mHWData->mGuestProperties.end())
5144 {
5145 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5146 aValue = it->second.strValue;
5147 *aTimestamp = it->second.mTimestamp;
5148 GuestPropWriteFlags(it->second.mFlags, szFlags);
5149 aFlags = Utf8Str(szFlags);
5150 }
5151
5152 return S_OK;
5153}
5154
5155/**
5156 * Query the VM that a guest property belongs to for the property.
5157 * @returns E_ACCESSDENIED if the VM process is not available or not
5158 * currently handling queries and the lookup should then be done in
5159 * VBoxSVC.
5160 */
5161HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5162 com::Utf8Str &aValue,
5163 LONG64 *aTimestamp,
5164 com::Utf8Str &aFlags) const
5165{
5166 HRESULT hrc = S_OK;
5167 Bstr bstrValue;
5168 Bstr bstrFlags;
5169
5170 ComPtr<IInternalSessionControl> directControl;
5171 {
5172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5173 if (mData->mSession.mLockType == LockType_VM)
5174 directControl = mData->mSession.mDirectControl;
5175 }
5176
5177 /* ignore calls made after #OnSessionEnd() is called */
5178 if (!directControl)
5179 hrc = E_ACCESSDENIED;
5180 else
5181 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5182 0 /* accessMode */,
5183 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5184
5185 aValue = bstrValue;
5186 aFlags = bstrFlags;
5187
5188 return hrc;
5189}
5190#endif // VBOX_WITH_GUEST_PROPS
5191
5192HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5193 com::Utf8Str &aValue,
5194 LONG64 *aTimestamp,
5195 com::Utf8Str &aFlags)
5196{
5197#ifndef VBOX_WITH_GUEST_PROPS
5198 ReturnComNotImplemented();
5199#else // VBOX_WITH_GUEST_PROPS
5200
5201 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5202
5203 if (hrc == E_ACCESSDENIED)
5204 /* The VM is not running or the service is not (yet) accessible */
5205 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5206 return hrc;
5207#endif // VBOX_WITH_GUEST_PROPS
5208}
5209
5210HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5211{
5212 LONG64 dummyTimestamp;
5213 com::Utf8Str dummyFlags;
5214 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5215
5216}
5217HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5218{
5219 com::Utf8Str dummyFlags;
5220 com::Utf8Str dummyValue;
5221 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5222}
5223
5224#ifdef VBOX_WITH_GUEST_PROPS
5225/**
5226 * Set a guest property in VBoxSVC's internal structures.
5227 */
5228HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5229 const com::Utf8Str &aFlags, bool fDelete)
5230{
5231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5232 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5233 if (FAILED(hrc)) return hrc;
5234
5235 try
5236 {
5237 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5238 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5239 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5240
5241 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5242 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5243
5244 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5245 if (it == mHWData->mGuestProperties.end())
5246 {
5247 if (!fDelete)
5248 {
5249 i_setModified(IsModified_MachineData);
5250 mHWData.backupEx();
5251
5252 RTTIMESPEC time;
5253 HWData::GuestProperty prop;
5254 prop.strValue = aValue;
5255 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5256 prop.mFlags = fFlags;
5257 mHWData->mGuestProperties[aName] = prop;
5258 }
5259 }
5260 else
5261 {
5262 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5263 {
5264 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5265 }
5266 else
5267 {
5268 i_setModified(IsModified_MachineData);
5269 mHWData.backupEx();
5270
5271 /* The backupEx() operation invalidates our iterator,
5272 * so get a new one. */
5273 it = mHWData->mGuestProperties.find(aName);
5274 Assert(it != mHWData->mGuestProperties.end());
5275
5276 if (!fDelete)
5277 {
5278 RTTIMESPEC time;
5279 it->second.strValue = aValue;
5280 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5281 it->second.mFlags = fFlags;
5282 }
5283 else
5284 mHWData->mGuestProperties.erase(it);
5285 }
5286 }
5287
5288 if (SUCCEEDED(hrc))
5289 {
5290 alock.release();
5291
5292 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5293 }
5294 }
5295 catch (std::bad_alloc &)
5296 {
5297 hrc = E_OUTOFMEMORY;
5298 }
5299
5300 return hrc;
5301}
5302
5303/**
5304 * Set a property on the VM that that property belongs to.
5305 * @returns E_ACCESSDENIED if the VM process is not available or not
5306 * currently handling queries and the setting should then be done in
5307 * VBoxSVC.
5308 */
5309HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5310 const com::Utf8Str &aFlags, bool fDelete)
5311{
5312 HRESULT hrc;
5313
5314 try
5315 {
5316 ComPtr<IInternalSessionControl> directControl;
5317 {
5318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5319 if (mData->mSession.mLockType == LockType_VM)
5320 directControl = mData->mSession.mDirectControl;
5321 }
5322
5323 Bstr dummy1; /* will not be changed (setter) */
5324 Bstr dummy2; /* will not be changed (setter) */
5325 LONG64 dummy64;
5326 if (!directControl)
5327 hrc = E_ACCESSDENIED;
5328 else
5329 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5330 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5331 fDelete ? 2 : 1 /* accessMode */,
5332 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5333 }
5334 catch (std::bad_alloc &)
5335 {
5336 hrc = E_OUTOFMEMORY;
5337 }
5338
5339 return hrc;
5340}
5341#endif // VBOX_WITH_GUEST_PROPS
5342
5343HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5344 const com::Utf8Str &aFlags)
5345{
5346#ifndef VBOX_WITH_GUEST_PROPS
5347 ReturnComNotImplemented();
5348#else // VBOX_WITH_GUEST_PROPS
5349
5350 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5351 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5352
5353 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5354 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5355
5356 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5357 if (hrc == E_ACCESSDENIED)
5358 /* The VM is not running or the service is not (yet) accessible */
5359 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5360 return hrc;
5361#endif // VBOX_WITH_GUEST_PROPS
5362}
5363
5364HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5365{
5366 return setGuestProperty(aProperty, aValue, "");
5367}
5368
5369HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5370{
5371#ifndef VBOX_WITH_GUEST_PROPS
5372 ReturnComNotImplemented();
5373#else // VBOX_WITH_GUEST_PROPS
5374 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5375 if (hrc == E_ACCESSDENIED)
5376 /* The VM is not running or the service is not (yet) accessible */
5377 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5378 return hrc;
5379#endif // VBOX_WITH_GUEST_PROPS
5380}
5381
5382#ifdef VBOX_WITH_GUEST_PROPS
5383/**
5384 * Enumerate the guest properties in VBoxSVC's internal structures.
5385 */
5386HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5387 std::vector<com::Utf8Str> &aNames,
5388 std::vector<com::Utf8Str> &aValues,
5389 std::vector<LONG64> &aTimestamps,
5390 std::vector<com::Utf8Str> &aFlags)
5391{
5392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5393 Utf8Str strPatterns(aPatterns);
5394
5395 /*
5396 * Look for matching patterns and build up a list.
5397 */
5398 HWData::GuestPropertyMap propMap;
5399 for (HWData::GuestPropertyMap::const_iterator
5400 it = mHWData->mGuestProperties.begin();
5401 it != mHWData->mGuestProperties.end();
5402 ++it)
5403 {
5404 if ( strPatterns.isEmpty()
5405 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5406 RTSTR_MAX,
5407 it->first.c_str(),
5408 RTSTR_MAX,
5409 NULL)
5410 )
5411 propMap.insert(*it);
5412 }
5413
5414 alock.release();
5415
5416 /*
5417 * And build up the arrays for returning the property information.
5418 */
5419 size_t cEntries = propMap.size();
5420
5421 aNames.resize(cEntries);
5422 aValues.resize(cEntries);
5423 aTimestamps.resize(cEntries);
5424 aFlags.resize(cEntries);
5425
5426 size_t i = 0;
5427 for (HWData::GuestPropertyMap::const_iterator
5428 it = propMap.begin();
5429 it != propMap.end();
5430 ++it, ++i)
5431 {
5432 aNames[i] = it->first;
5433 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5434 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5435
5436 aValues[i] = it->second.strValue;
5437 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5438 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5439
5440 aTimestamps[i] = it->second.mTimestamp;
5441
5442 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5443 GuestPropWriteFlags(it->second.mFlags, szFlags);
5444 aFlags[i] = szFlags;
5445 }
5446
5447 return S_OK;
5448}
5449
5450/**
5451 * Enumerate the properties managed by a VM.
5452 * @returns E_ACCESSDENIED if the VM process is not available or not
5453 * currently handling queries and the setting should then be done in
5454 * VBoxSVC.
5455 */
5456HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5457 std::vector<com::Utf8Str> &aNames,
5458 std::vector<com::Utf8Str> &aValues,
5459 std::vector<LONG64> &aTimestamps,
5460 std::vector<com::Utf8Str> &aFlags)
5461{
5462 HRESULT hrc;
5463 ComPtr<IInternalSessionControl> directControl;
5464 {
5465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5466 if (mData->mSession.mLockType == LockType_VM)
5467 directControl = mData->mSession.mDirectControl;
5468 }
5469
5470 com::SafeArray<BSTR> bNames;
5471 com::SafeArray<BSTR> bValues;
5472 com::SafeArray<LONG64> bTimestamps;
5473 com::SafeArray<BSTR> bFlags;
5474
5475 if (!directControl)
5476 hrc = E_ACCESSDENIED;
5477 else
5478 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5479 ComSafeArrayAsOutParam(bNames),
5480 ComSafeArrayAsOutParam(bValues),
5481 ComSafeArrayAsOutParam(bTimestamps),
5482 ComSafeArrayAsOutParam(bFlags));
5483 size_t i;
5484 aNames.resize(bNames.size());
5485 for (i = 0; i < bNames.size(); ++i)
5486 aNames[i] = Utf8Str(bNames[i]);
5487 aValues.resize(bValues.size());
5488 for (i = 0; i < bValues.size(); ++i)
5489 aValues[i] = Utf8Str(bValues[i]);
5490 aTimestamps.resize(bTimestamps.size());
5491 for (i = 0; i < bTimestamps.size(); ++i)
5492 aTimestamps[i] = bTimestamps[i];
5493 aFlags.resize(bFlags.size());
5494 for (i = 0; i < bFlags.size(); ++i)
5495 aFlags[i] = Utf8Str(bFlags[i]);
5496
5497 return hrc;
5498}
5499#endif // VBOX_WITH_GUEST_PROPS
5500HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5501 std::vector<com::Utf8Str> &aNames,
5502 std::vector<com::Utf8Str> &aValues,
5503 std::vector<LONG64> &aTimestamps,
5504 std::vector<com::Utf8Str> &aFlags)
5505{
5506#ifndef VBOX_WITH_GUEST_PROPS
5507 ReturnComNotImplemented();
5508#else // VBOX_WITH_GUEST_PROPS
5509
5510 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5511
5512 if (hrc == E_ACCESSDENIED)
5513 /* The VM is not running or the service is not (yet) accessible */
5514 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5515 return hrc;
5516#endif // VBOX_WITH_GUEST_PROPS
5517}
5518
5519HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5520 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5521{
5522 MediumAttachmentList atts;
5523
5524 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5525 if (FAILED(hrc)) return hrc;
5526
5527 aMediumAttachments.resize(atts.size());
5528 size_t i = 0;
5529 for (MediumAttachmentList::const_iterator
5530 it = atts.begin();
5531 it != atts.end();
5532 ++it, ++i)
5533 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5534
5535 return S_OK;
5536}
5537
5538HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5539 LONG aControllerPort,
5540 LONG aDevice,
5541 ComPtr<IMediumAttachment> &aAttachment)
5542{
5543 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5544 aName.c_str(), aControllerPort, aDevice));
5545
5546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5547
5548 aAttachment = NULL;
5549
5550 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5551 aName,
5552 aControllerPort,
5553 aDevice);
5554 if (pAttach.isNull())
5555 return setError(VBOX_E_OBJECT_NOT_FOUND,
5556 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5557 aDevice, aControllerPort, aName.c_str());
5558
5559 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5560
5561 return S_OK;
5562}
5563
5564
5565HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5566 StorageBus_T aConnectionType,
5567 ComPtr<IStorageController> &aController)
5568{
5569 if ( (aConnectionType <= StorageBus_Null)
5570 || (aConnectionType > StorageBus_VirtioSCSI))
5571 return setError(E_INVALIDARG,
5572 tr("Invalid connection type: %d"),
5573 aConnectionType);
5574
5575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5576
5577 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5578 if (FAILED(hrc)) return hrc;
5579
5580 /* try to find one with the name first. */
5581 ComObjPtr<StorageController> ctrl;
5582
5583 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5584 if (SUCCEEDED(hrc))
5585 return setError(VBOX_E_OBJECT_IN_USE,
5586 tr("Storage controller named '%s' already exists"),
5587 aName.c_str());
5588
5589 ctrl.createObject();
5590
5591 /* get a new instance number for the storage controller */
5592 ULONG ulInstance = 0;
5593 bool fBootable = true;
5594 for (StorageControllerList::const_iterator
5595 it = mStorageControllers->begin();
5596 it != mStorageControllers->end();
5597 ++it)
5598 {
5599 if ((*it)->i_getStorageBus() == aConnectionType)
5600 {
5601 ULONG ulCurInst = (*it)->i_getInstance();
5602
5603 if (ulCurInst >= ulInstance)
5604 ulInstance = ulCurInst + 1;
5605
5606 /* Only one controller of each type can be marked as bootable. */
5607 if ((*it)->i_getBootable())
5608 fBootable = false;
5609 }
5610 }
5611
5612 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5613 if (FAILED(hrc)) return hrc;
5614
5615 i_setModified(IsModified_Storage);
5616 mStorageControllers.backup();
5617 mStorageControllers->push_back(ctrl);
5618
5619 ctrl.queryInterfaceTo(aController.asOutParam());
5620
5621 /* inform the direct session if any */
5622 alock.release();
5623 i_onStorageControllerChange(i_getId(), aName);
5624
5625 return S_OK;
5626}
5627
5628HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5629 ComPtr<IStorageController> &aStorageController)
5630{
5631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5632
5633 ComObjPtr<StorageController> ctrl;
5634
5635 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5636 if (SUCCEEDED(hrc))
5637 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5638
5639 return hrc;
5640}
5641
5642HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5643 ULONG aInstance,
5644 ComPtr<IStorageController> &aStorageController)
5645{
5646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5647
5648 for (StorageControllerList::const_iterator
5649 it = mStorageControllers->begin();
5650 it != mStorageControllers->end();
5651 ++it)
5652 {
5653 if ( (*it)->i_getStorageBus() == aConnectionType
5654 && (*it)->i_getInstance() == aInstance)
5655 {
5656 (*it).queryInterfaceTo(aStorageController.asOutParam());
5657 return S_OK;
5658 }
5659 }
5660
5661 return setError(VBOX_E_OBJECT_NOT_FOUND,
5662 tr("Could not find a storage controller with instance number '%lu'"),
5663 aInstance);
5664}
5665
5666HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5667{
5668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5669
5670 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5671 if (FAILED(hrc)) return hrc;
5672
5673 ComObjPtr<StorageController> ctrl;
5674
5675 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5676 if (SUCCEEDED(hrc))
5677 {
5678 /* Ensure that only one controller of each type is marked as bootable. */
5679 if (aBootable == TRUE)
5680 {
5681 for (StorageControllerList::const_iterator
5682 it = mStorageControllers->begin();
5683 it != mStorageControllers->end();
5684 ++it)
5685 {
5686 ComObjPtr<StorageController> aCtrl = (*it);
5687
5688 if ( (aCtrl->i_getName() != aName)
5689 && aCtrl->i_getBootable() == TRUE
5690 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5691 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5692 {
5693 aCtrl->i_setBootable(FALSE);
5694 break;
5695 }
5696 }
5697 }
5698
5699 if (SUCCEEDED(hrc))
5700 {
5701 ctrl->i_setBootable(aBootable);
5702 i_setModified(IsModified_Storage);
5703 }
5704 }
5705
5706 if (SUCCEEDED(hrc))
5707 {
5708 /* inform the direct session if any */
5709 alock.release();
5710 i_onStorageControllerChange(i_getId(), aName);
5711 }
5712
5713 return hrc;
5714}
5715
5716HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5717{
5718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5719
5720 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5721 if (FAILED(hrc)) return hrc;
5722
5723 ComObjPtr<StorageController> ctrl;
5724 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5725 if (FAILED(hrc)) return hrc;
5726
5727 MediumAttachmentList llDetachedAttachments;
5728 {
5729 /* find all attached devices to the appropriate storage controller and detach them all */
5730 // make a temporary list because detachDevice invalidates iterators into
5731 // mMediumAttachments
5732 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5733
5734 for (MediumAttachmentList::const_iterator
5735 it = llAttachments2.begin();
5736 it != llAttachments2.end();
5737 ++it)
5738 {
5739 MediumAttachment *pAttachTemp = *it;
5740
5741 AutoCaller localAutoCaller(pAttachTemp);
5742 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
5743
5744 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5745
5746 if (pAttachTemp->i_getControllerName() == aName)
5747 {
5748 llDetachedAttachments.push_back(pAttachTemp);
5749 hrc = i_detachDevice(pAttachTemp, alock, NULL);
5750 if (FAILED(hrc)) return hrc;
5751 }
5752 }
5753 }
5754
5755 /* send event about detached devices before removing parent controller */
5756 for (MediumAttachmentList::const_iterator
5757 it = llDetachedAttachments.begin();
5758 it != llDetachedAttachments.end();
5759 ++it)
5760 {
5761 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5762 }
5763
5764 /* We can remove it now. */
5765 i_setModified(IsModified_Storage);
5766 mStorageControllers.backup();
5767
5768 ctrl->i_unshare();
5769
5770 mStorageControllers->remove(ctrl);
5771
5772 /* inform the direct session if any */
5773 alock.release();
5774 i_onStorageControllerChange(i_getId(), aName);
5775
5776 return S_OK;
5777}
5778
5779HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5780 ComPtr<IUSBController> &aController)
5781{
5782 if ( (aType <= USBControllerType_Null)
5783 || (aType >= USBControllerType_Last))
5784 return setError(E_INVALIDARG,
5785 tr("Invalid USB controller type: %d"),
5786 aType);
5787
5788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5789
5790 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5791 if (FAILED(hrc)) return hrc;
5792
5793 /* try to find one with the same type first. */
5794 ComObjPtr<USBController> ctrl;
5795
5796 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5797 if (SUCCEEDED(hrc))
5798 return setError(VBOX_E_OBJECT_IN_USE,
5799 tr("USB controller named '%s' already exists"),
5800 aName.c_str());
5801
5802 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5803 ChipsetType_T enmChipsetType;
5804 hrc = mPlatform->getChipsetType(&enmChipsetType);
5805 if (FAILED(hrc))
5806 return hrc;
5807
5808 ULONG maxInstances;
5809 hrc = mPlatformProperties->GetMaxInstancesOfUSBControllerType(enmChipsetType, aType, &maxInstances);
5810 if (FAILED(hrc))
5811 return hrc;
5812
5813 ULONG cInstances = i_getUSBControllerCountByType(aType);
5814 if (cInstances >= maxInstances)
5815 return setError(E_INVALIDARG,
5816 tr("Too many USB controllers of this type"));
5817
5818 ctrl.createObject();
5819
5820 hrc = ctrl->init(this, aName, aType);
5821 if (FAILED(hrc)) return hrc;
5822
5823 i_setModified(IsModified_USB);
5824 mUSBControllers.backup();
5825 mUSBControllers->push_back(ctrl);
5826
5827 ctrl.queryInterfaceTo(aController.asOutParam());
5828
5829 /* inform the direct session if any */
5830 alock.release();
5831 i_onUSBControllerChange();
5832
5833 return S_OK;
5834}
5835
5836HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
5837{
5838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5839
5840 ComObjPtr<USBController> ctrl;
5841
5842 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5843 if (SUCCEEDED(hrc))
5844 ctrl.queryInterfaceTo(aController.asOutParam());
5845
5846 return hrc;
5847}
5848
5849HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
5850 ULONG *aControllers)
5851{
5852 if ( (aType <= USBControllerType_Null)
5853 || (aType >= USBControllerType_Last))
5854 return setError(E_INVALIDARG,
5855 tr("Invalid USB controller type: %d"),
5856 aType);
5857
5858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5859
5860 ComObjPtr<USBController> ctrl;
5861
5862 *aControllers = i_getUSBControllerCountByType(aType);
5863
5864 return S_OK;
5865}
5866
5867HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
5868{
5869
5870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5871
5872 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5873 if (FAILED(hrc)) return hrc;
5874
5875 ComObjPtr<USBController> ctrl;
5876 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5877 if (FAILED(hrc)) return hrc;
5878
5879 i_setModified(IsModified_USB);
5880 mUSBControllers.backup();
5881
5882 ctrl->i_unshare();
5883
5884 mUSBControllers->remove(ctrl);
5885
5886 /* inform the direct session if any */
5887 alock.release();
5888 i_onUSBControllerChange();
5889
5890 return S_OK;
5891}
5892
5893HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
5894 ULONG *aOriginX,
5895 ULONG *aOriginY,
5896 ULONG *aWidth,
5897 ULONG *aHeight,
5898 BOOL *aEnabled)
5899{
5900 uint32_t u32OriginX= 0;
5901 uint32_t u32OriginY= 0;
5902 uint32_t u32Width = 0;
5903 uint32_t u32Height = 0;
5904 uint16_t u16Flags = 0;
5905
5906#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5907 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5908#else
5909 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5910#endif
5911 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
5912 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5913 if (RT_FAILURE(vrc))
5914 {
5915#ifdef RT_OS_WINDOWS
5916 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5917 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5918 * So just assign fEnable to TRUE again.
5919 * The right fix would be to change GUI API wrappers to make sure that parameters
5920 * are changed only if API succeeds.
5921 */
5922 *aEnabled = TRUE;
5923#endif
5924 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5925 tr("Saved guest size is not available (%Rrc)"),
5926 vrc);
5927 }
5928
5929 *aOriginX = u32OriginX;
5930 *aOriginY = u32OriginY;
5931 *aWidth = u32Width;
5932 *aHeight = u32Height;
5933 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5934
5935 return S_OK;
5936}
5937
5938HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
5939 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
5940{
5941 if (aScreenId != 0)
5942 return E_NOTIMPL;
5943
5944 if ( aBitmapFormat != BitmapFormat_BGR0
5945 && aBitmapFormat != BitmapFormat_BGRA
5946 && aBitmapFormat != BitmapFormat_RGBA
5947 && aBitmapFormat != BitmapFormat_PNG)
5948 return setError(E_NOTIMPL,
5949 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
5950
5951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5952
5953 uint8_t *pu8Data = NULL;
5954 uint32_t cbData = 0;
5955 uint32_t u32Width = 0;
5956 uint32_t u32Height = 0;
5957
5958#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5959 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5960#else
5961 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5962#endif
5963 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
5964 &pu8Data, &cbData, &u32Width, &u32Height);
5965 if (RT_FAILURE(vrc))
5966 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5967 tr("Saved thumbnail data is not available (%Rrc)"),
5968 vrc);
5969
5970 HRESULT hrc = S_OK;
5971
5972 *aWidth = u32Width;
5973 *aHeight = u32Height;
5974
5975 if (cbData > 0)
5976 {
5977 /* Convert pixels to the format expected by the API caller. */
5978 if (aBitmapFormat == BitmapFormat_BGR0)
5979 {
5980 /* [0] B, [1] G, [2] R, [3] 0. */
5981 aData.resize(cbData);
5982 memcpy(&aData.front(), pu8Data, cbData);
5983 }
5984 else if (aBitmapFormat == BitmapFormat_BGRA)
5985 {
5986 /* [0] B, [1] G, [2] R, [3] A. */
5987 aData.resize(cbData);
5988 for (uint32_t i = 0; i < cbData; i += 4)
5989 {
5990 aData[i] = pu8Data[i];
5991 aData[i + 1] = pu8Data[i + 1];
5992 aData[i + 2] = pu8Data[i + 2];
5993 aData[i + 3] = 0xff;
5994 }
5995 }
5996 else if (aBitmapFormat == BitmapFormat_RGBA)
5997 {
5998 /* [0] R, [1] G, [2] B, [3] A. */
5999 aData.resize(cbData);
6000 for (uint32_t i = 0; i < cbData; i += 4)
6001 {
6002 aData[i] = pu8Data[i + 2];
6003 aData[i + 1] = pu8Data[i + 1];
6004 aData[i + 2] = pu8Data[i];
6005 aData[i + 3] = 0xff;
6006 }
6007 }
6008 else if (aBitmapFormat == BitmapFormat_PNG)
6009 {
6010 uint8_t *pu8PNG = NULL;
6011 uint32_t cbPNG = 0;
6012 uint32_t cxPNG = 0;
6013 uint32_t cyPNG = 0;
6014
6015 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6016
6017 if (RT_SUCCESS(vrc))
6018 {
6019 aData.resize(cbPNG);
6020 if (cbPNG)
6021 memcpy(&aData.front(), pu8PNG, cbPNG);
6022 }
6023 else
6024 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6025
6026 RTMemFree(pu8PNG);
6027 }
6028 }
6029
6030 freeSavedDisplayScreenshot(pu8Data);
6031
6032 return hrc;
6033}
6034
6035HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6036 ULONG *aWidth,
6037 ULONG *aHeight,
6038 std::vector<BitmapFormat_T> &aBitmapFormats)
6039{
6040 if (aScreenId != 0)
6041 return E_NOTIMPL;
6042
6043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6044
6045 uint8_t *pu8Data = NULL;
6046 uint32_t cbData = 0;
6047 uint32_t u32Width = 0;
6048 uint32_t u32Height = 0;
6049
6050#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6051 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6052#else
6053 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6054#endif
6055 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6056 &pu8Data, &cbData, &u32Width, &u32Height);
6057
6058 if (RT_FAILURE(vrc))
6059 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6060 tr("Saved screenshot data is not available (%Rrc)"),
6061 vrc);
6062
6063 *aWidth = u32Width;
6064 *aHeight = u32Height;
6065 aBitmapFormats.resize(1);
6066 aBitmapFormats[0] = BitmapFormat_PNG;
6067
6068 freeSavedDisplayScreenshot(pu8Data);
6069
6070 return S_OK;
6071}
6072
6073HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6074 BitmapFormat_T aBitmapFormat,
6075 ULONG *aWidth,
6076 ULONG *aHeight,
6077 std::vector<BYTE> &aData)
6078{
6079 if (aScreenId != 0)
6080 return E_NOTIMPL;
6081
6082 if (aBitmapFormat != BitmapFormat_PNG)
6083 return E_NOTIMPL;
6084
6085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6086
6087 uint8_t *pu8Data = NULL;
6088 uint32_t cbData = 0;
6089 uint32_t u32Width = 0;
6090 uint32_t u32Height = 0;
6091
6092#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6093 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6094#else
6095 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6096#endif
6097 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6098 &pu8Data, &cbData, &u32Width, &u32Height);
6099
6100 if (RT_FAILURE(vrc))
6101 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6102 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6103 vrc);
6104
6105 *aWidth = u32Width;
6106 *aHeight = u32Height;
6107
6108 aData.resize(cbData);
6109 if (cbData)
6110 memcpy(&aData.front(), pu8Data, cbData);
6111
6112 freeSavedDisplayScreenshot(pu8Data);
6113
6114 return S_OK;
6115}
6116
6117HRESULT Machine::hotPlugCPU(ULONG aCpu)
6118{
6119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6120
6121 if (!mHWData->mCPUHotPlugEnabled)
6122 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6123
6124 if (aCpu >= mHWData->mCPUCount)
6125 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6126
6127 if (mHWData->mCPUAttached[aCpu])
6128 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6129
6130 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6131 if (FAILED(hrc)) return hrc;
6132
6133 alock.release();
6134 hrc = i_onCPUChange(aCpu, false);
6135 alock.acquire();
6136 if (FAILED(hrc)) return hrc;
6137
6138 i_setModified(IsModified_MachineData);
6139 mHWData.backup();
6140 mHWData->mCPUAttached[aCpu] = true;
6141
6142 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6143 if (Global::IsOnline(mData->mMachineState))
6144 i_saveSettings(NULL, alock);
6145
6146 return S_OK;
6147}
6148
6149HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6150{
6151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6152
6153 if (!mHWData->mCPUHotPlugEnabled)
6154 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6155
6156 if (aCpu >= SchemaDefs::MaxCPUCount)
6157 return setError(E_INVALIDARG,
6158 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6159 SchemaDefs::MaxCPUCount);
6160
6161 if (!mHWData->mCPUAttached[aCpu])
6162 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6163
6164 /* CPU 0 can't be detached */
6165 if (aCpu == 0)
6166 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6167
6168 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6169 if (FAILED(hrc)) return hrc;
6170
6171 alock.release();
6172 hrc = i_onCPUChange(aCpu, true);
6173 alock.acquire();
6174 if (FAILED(hrc)) return hrc;
6175
6176 i_setModified(IsModified_MachineData);
6177 mHWData.backup();
6178 mHWData->mCPUAttached[aCpu] = false;
6179
6180 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6181 if (Global::IsOnline(mData->mMachineState))
6182 i_saveSettings(NULL, alock);
6183
6184 return S_OK;
6185}
6186
6187HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6188{
6189 *aAttached = false;
6190
6191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6192
6193 /* If hotplug is enabled the CPU is always enabled. */
6194 if (!mHWData->mCPUHotPlugEnabled)
6195 {
6196 if (aCpu < mHWData->mCPUCount)
6197 *aAttached = true;
6198 }
6199 else
6200 {
6201 if (aCpu < SchemaDefs::MaxCPUCount)
6202 *aAttached = mHWData->mCPUAttached[aCpu];
6203 }
6204
6205 return S_OK;
6206}
6207
6208HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6209{
6210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6211
6212 Utf8Str log = i_getLogFilename(aIdx);
6213 if (!RTFileExists(log.c_str()))
6214 log.setNull();
6215 aFilename = log;
6216
6217 return S_OK;
6218}
6219
6220HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6221{
6222 if (aSize < 0)
6223 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6224
6225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6226
6227 HRESULT hrc = S_OK;
6228 Utf8Str log = i_getLogFilename(aIdx);
6229
6230 /* do not unnecessarily hold the lock while doing something which does
6231 * not need the lock and potentially takes a long time. */
6232 alock.release();
6233
6234 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6235 * keeps the SOAP reply size under 1M for the webservice (we're using
6236 * base64 encoded strings for binary data for years now, avoiding the
6237 * expansion of each byte array element to approx. 25 bytes of XML. */
6238 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6239 aData.resize(cbData);
6240
6241 int vrc = VINF_SUCCESS;
6242 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6243
6244#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6245 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6246 {
6247 PCVBOXCRYPTOIF pCryptoIf = NULL;
6248 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6249 if (SUCCEEDED(hrc))
6250 {
6251 alock.acquire();
6252
6253 SecretKey *pKey = NULL;
6254 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6255 alock.release();
6256
6257 if (RT_SUCCESS(vrc))
6258 {
6259 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6260 if (RT_SUCCESS(vrc))
6261 {
6262 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6263 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6264 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6265 if (RT_SUCCESS(vrc))
6266 {
6267 RTVfsIoStrmRelease(hVfsIosLog);
6268 hVfsIosLog = hVfsIosLogDec;
6269 }
6270 }
6271
6272 pKey->release();
6273 }
6274
6275 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6276 }
6277 }
6278 else
6279 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6280#else
6281 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6282#endif
6283 if (RT_SUCCESS(vrc))
6284 {
6285 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6286 cbData ? &aData.front() : NULL, cbData,
6287 true /*fBlocking*/, &cbData);
6288 if (RT_SUCCESS(vrc))
6289 aData.resize(cbData);
6290 else
6291 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6292
6293 RTVfsIoStrmRelease(hVfsIosLog);
6294 }
6295 else
6296 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6297
6298 if (FAILED(hrc))
6299 aData.resize(0);
6300
6301 return hrc;
6302}
6303
6304
6305/**
6306 * Currently this method doesn't attach device to the running VM,
6307 * just makes sure it's plugged on next VM start.
6308 */
6309HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6310{
6311 // lock scope
6312 {
6313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6314
6315 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6316 if (FAILED(hrc)) return hrc;
6317
6318 ChipsetType_T aChipset = ChipsetType_PIIX3;
6319 hrc = mPlatform->COMGETTER(ChipsetType)(&aChipset);
6320 if (FAILED(hrc)) return hrc;
6321
6322 if (aChipset != ChipsetType_ICH9) /** @todo BUGBUG ASSUMES x86! */
6323 {
6324 return setError(E_INVALIDARG,
6325 tr("Host PCI attachment only supported with ICH9 chipset"));
6326 }
6327
6328 // check if device with this host PCI address already attached
6329 for (HWData::PCIDeviceAssignmentList::const_iterator
6330 it = mHWData->mPCIDeviceAssignments.begin();
6331 it != mHWData->mPCIDeviceAssignments.end();
6332 ++it)
6333 {
6334 LONG iHostAddress = -1;
6335 ComPtr<PCIDeviceAttachment> pAttach;
6336 pAttach = *it;
6337 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6338 if (iHostAddress == aHostAddress)
6339 return setError(E_INVALIDARG,
6340 tr("Device with host PCI address already attached to this VM"));
6341 }
6342
6343 ComObjPtr<PCIDeviceAttachment> pda;
6344 char name[32];
6345
6346 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6347 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6348 pda.createObject();
6349 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6350 i_setModified(IsModified_MachineData);
6351 mHWData.backup();
6352 mHWData->mPCIDeviceAssignments.push_back(pda);
6353 }
6354
6355 return S_OK;
6356}
6357
6358/**
6359 * Currently this method doesn't detach device from the running VM,
6360 * just makes sure it's not plugged on next VM start.
6361 */
6362HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6363{
6364 ComObjPtr<PCIDeviceAttachment> pAttach;
6365 bool fRemoved = false;
6366 HRESULT hrc;
6367
6368 // lock scope
6369 {
6370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6371
6372 hrc = i_checkStateDependency(MutableStateDep);
6373 if (FAILED(hrc)) return hrc;
6374
6375 for (HWData::PCIDeviceAssignmentList::const_iterator
6376 it = mHWData->mPCIDeviceAssignments.begin();
6377 it != mHWData->mPCIDeviceAssignments.end();
6378 ++it)
6379 {
6380 LONG iHostAddress = -1;
6381 pAttach = *it;
6382 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6383 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6384 {
6385 i_setModified(IsModified_MachineData);
6386 mHWData.backup();
6387 mHWData->mPCIDeviceAssignments.remove(pAttach);
6388 fRemoved = true;
6389 break;
6390 }
6391 }
6392 }
6393
6394
6395 /* Fire event outside of the lock */
6396 if (fRemoved)
6397 {
6398 Assert(!pAttach.isNull());
6399 ComPtr<IEventSource> es;
6400 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
6401 Assert(SUCCEEDED(hrc));
6402 Bstr mid;
6403 hrc = this->COMGETTER(Id)(mid.asOutParam());
6404 Assert(SUCCEEDED(hrc));
6405 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6406 }
6407
6408 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6409 tr("No host PCI device %08x attached"),
6410 aHostAddress
6411 );
6412}
6413
6414HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6415{
6416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6419 size_t i = 0;
6420 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6421 it = mHWData->mPCIDeviceAssignments.begin();
6422 it != mHWData->mPCIDeviceAssignments.end();
6423 ++it, ++i)
6424 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6425
6426 return S_OK;
6427}
6428
6429HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6430{
6431 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6432
6433 return S_OK;
6434}
6435
6436HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6437{
6438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6439
6440 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6441
6442 return S_OK;
6443}
6444
6445HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6446{
6447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6448 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6449 if (SUCCEEDED(hrc))
6450 {
6451 hrc = mHWData.backupEx();
6452 if (SUCCEEDED(hrc))
6453 {
6454 i_setModified(IsModified_MachineData);
6455 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6456 }
6457 }
6458 return hrc;
6459}
6460
6461HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6462{
6463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6464 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6465 return S_OK;
6466}
6467
6468HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6469{
6470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6471 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6472 if (SUCCEEDED(hrc))
6473 {
6474 hrc = mHWData.backupEx();
6475 if (SUCCEEDED(hrc))
6476 {
6477 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6478 if (SUCCEEDED(hrc))
6479 i_setModified(IsModified_MachineData);
6480 }
6481 }
6482 return hrc;
6483}
6484
6485HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6486{
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6490
6491 return S_OK;
6492}
6493
6494HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6495{
6496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6497 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6498 if (SUCCEEDED(hrc))
6499 {
6500 hrc = mHWData.backupEx();
6501 if (SUCCEEDED(hrc))
6502 {
6503 i_setModified(IsModified_MachineData);
6504 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6505 }
6506 }
6507 return hrc;
6508}
6509
6510HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6511{
6512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6513
6514 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6515
6516 return S_OK;
6517}
6518
6519HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6520{
6521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6522
6523 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6524 if ( SUCCEEDED(hrc)
6525 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6526 {
6527 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6528 int vrc;
6529
6530 if (aAutostartEnabled)
6531 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6532 else
6533 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6534
6535 if (RT_SUCCESS(vrc))
6536 {
6537 hrc = mHWData.backupEx();
6538 if (SUCCEEDED(hrc))
6539 {
6540 i_setModified(IsModified_MachineData);
6541 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6542 }
6543 }
6544 else if (vrc == VERR_NOT_SUPPORTED)
6545 hrc = setError(VBOX_E_NOT_SUPPORTED,
6546 tr("The VM autostart feature is not supported on this platform"));
6547 else if (vrc == VERR_PATH_NOT_FOUND)
6548 hrc = setError(E_FAIL,
6549 tr("The path to the autostart database is not set"));
6550 else
6551 hrc = setError(E_UNEXPECTED,
6552 aAutostartEnabled ?
6553 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6554 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6555 mUserData->s.strName.c_str(), vrc);
6556 }
6557 return hrc;
6558}
6559
6560HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6561{
6562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6563
6564 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6565
6566 return S_OK;
6567}
6568
6569HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6570{
6571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6572 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6573 if (SUCCEEDED(hrc))
6574 {
6575 hrc = mHWData.backupEx();
6576 if (SUCCEEDED(hrc))
6577 {
6578 i_setModified(IsModified_MachineData);
6579 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6580 }
6581 }
6582 return hrc;
6583}
6584
6585HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6586{
6587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6588
6589 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6590
6591 return S_OK;
6592}
6593
6594HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6595{
6596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6597 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6598 if ( SUCCEEDED(hrc)
6599 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6600 {
6601 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6602 int vrc;
6603
6604 if (aAutostopType != AutostopType_Disabled)
6605 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6606 else
6607 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6608
6609 if (RT_SUCCESS(vrc))
6610 {
6611 hrc = mHWData.backupEx();
6612 if (SUCCEEDED(hrc))
6613 {
6614 i_setModified(IsModified_MachineData);
6615 mHWData->mAutostart.enmAutostopType = aAutostopType;
6616 }
6617 }
6618 else if (vrc == VERR_NOT_SUPPORTED)
6619 hrc = setError(VBOX_E_NOT_SUPPORTED,
6620 tr("The VM autostop feature is not supported on this platform"));
6621 else if (vrc == VERR_PATH_NOT_FOUND)
6622 hrc = setError(E_FAIL,
6623 tr("The path to the autostart database is not set"));
6624 else
6625 hrc = setError(E_UNEXPECTED,
6626 aAutostopType != AutostopType_Disabled ?
6627 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6628 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6629 mUserData->s.strName.c_str(), vrc);
6630 }
6631 return hrc;
6632}
6633
6634HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6635{
6636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6637
6638 aDefaultFrontend = mHWData->mDefaultFrontend;
6639
6640 return S_OK;
6641}
6642
6643HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6644{
6645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6646 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6647 if (SUCCEEDED(hrc))
6648 {
6649 hrc = mHWData.backupEx();
6650 if (SUCCEEDED(hrc))
6651 {
6652 i_setModified(IsModified_MachineData);
6653 mHWData->mDefaultFrontend = aDefaultFrontend;
6654 }
6655 }
6656 return hrc;
6657}
6658
6659HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6660{
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662 size_t cbIcon = mUserData->s.ovIcon.size();
6663 aIcon.resize(cbIcon);
6664 if (cbIcon)
6665 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6666 return S_OK;
6667}
6668
6669HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6670{
6671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6672 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6673 if (SUCCEEDED(hrc))
6674 {
6675 i_setModified(IsModified_MachineData);
6676 mUserData.backup();
6677 size_t cbIcon = aIcon.size();
6678 mUserData->s.ovIcon.resize(cbIcon);
6679 if (cbIcon)
6680 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6681 }
6682 return hrc;
6683}
6684
6685HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6686{
6687#ifdef VBOX_WITH_USB
6688 *aUSBProxyAvailable = true;
6689#else
6690 *aUSBProxyAvailable = false;
6691#endif
6692 return S_OK;
6693}
6694
6695HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6696{
6697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6698
6699 *aVMProcessPriority = mUserData->s.enmVMPriority;
6700
6701 return S_OK;
6702}
6703
6704HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6705{
6706 RT_NOREF(aVMProcessPriority);
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6709 if (SUCCEEDED(hrc))
6710 {
6711 hrc = mUserData.backupEx();
6712 if (SUCCEEDED(hrc))
6713 {
6714 i_setModified(IsModified_MachineData);
6715 mUserData->s.enmVMPriority = aVMProcessPriority;
6716 }
6717 }
6718 alock.release();
6719 if (SUCCEEDED(hrc))
6720 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6721 return hrc;
6722}
6723
6724HRESULT Machine::getVMExecutionEngine(VMExecutionEngine_T *aVMExecutionEngine)
6725{
6726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6727
6728 *aVMExecutionEngine = mUserData->s.enmExecEngine;
6729
6730 return S_OK;
6731}
6732
6733HRESULT Machine::setVMExecutionEngine(VMExecutionEngine_T aVMExecutionEngine)
6734{
6735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6736 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6737 if (SUCCEEDED(hrc))
6738 {
6739 hrc = mUserData.backupEx();
6740 if (SUCCEEDED(hrc))
6741 {
6742 i_setModified(IsModified_MachineData);
6743 mUserData->s.enmExecEngine = aVMExecutionEngine;
6744 }
6745 }
6746 return hrc;
6747}
6748
6749HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6750 ComPtr<IProgress> &aProgress)
6751{
6752 ComObjPtr<Progress> pP;
6753 Progress *ppP = pP;
6754 IProgress *iP = static_cast<IProgress *>(ppP);
6755 IProgress **pProgress = &iP;
6756
6757 IMachine *pTarget = aTarget;
6758
6759 /* Convert the options. */
6760 RTCList<CloneOptions_T> optList;
6761 if (aOptions.size())
6762 for (size_t i = 0; i < aOptions.size(); ++i)
6763 optList.append(aOptions[i]);
6764
6765 if (optList.contains(CloneOptions_Link))
6766 {
6767 if (!i_isSnapshotMachine())
6768 return setError(E_INVALIDARG,
6769 tr("Linked clone can only be created from a snapshot"));
6770 if (aMode != CloneMode_MachineState)
6771 return setError(E_INVALIDARG,
6772 tr("Linked clone can only be created for a single machine state"));
6773 }
6774 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6775
6776 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6777
6778 HRESULT hrc = pWorker->start(pProgress);
6779
6780 pP = static_cast<Progress *>(*pProgress);
6781 pP.queryInterfaceTo(aProgress.asOutParam());
6782
6783 return hrc;
6784
6785}
6786
6787HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6788 const com::Utf8Str &aType,
6789 ComPtr<IProgress> &aProgress)
6790{
6791 LogFlowThisFuncEnter();
6792
6793 ComObjPtr<Progress> ptrProgress;
6794 HRESULT hrc = ptrProgress.createObject();
6795 if (SUCCEEDED(hrc))
6796 {
6797 com::Utf8Str strDefaultPath;
6798 if (aTargetPath.isEmpty())
6799 i_calculateFullPath(".", strDefaultPath);
6800
6801 /* Initialize our worker task */
6802 MachineMoveVM *pTask = NULL;
6803 try
6804 {
6805 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6806 }
6807 catch (std::bad_alloc &)
6808 {
6809 return E_OUTOFMEMORY;
6810 }
6811
6812 hrc = pTask->init();//no exceptions are thrown
6813
6814 if (SUCCEEDED(hrc))
6815 {
6816 hrc = pTask->createThread();
6817 pTask = NULL; /* Consumed by createThread(). */
6818 if (SUCCEEDED(hrc))
6819 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6820 else
6821 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6822 }
6823 else
6824 delete pTask;
6825 }
6826
6827 LogFlowThisFuncLeave();
6828 return hrc;
6829
6830}
6831
6832HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6833{
6834 NOREF(aProgress);
6835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6836
6837 // This check should always fail.
6838 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6839 if (FAILED(hrc)) return hrc;
6840
6841 AssertFailedReturn(E_NOTIMPL);
6842}
6843
6844HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6845{
6846 NOREF(aSavedStateFile);
6847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6848
6849 // This check should always fail.
6850 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6851 if (FAILED(hrc)) return hrc;
6852
6853 AssertFailedReturn(E_NOTIMPL);
6854}
6855
6856HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6857{
6858 NOREF(aFRemoveFile);
6859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6860
6861 // This check should always fail.
6862 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6863 if (FAILED(hrc)) return hrc;
6864
6865 AssertFailedReturn(E_NOTIMPL);
6866}
6867
6868// public methods for internal purposes
6869/////////////////////////////////////////////////////////////////////////////
6870
6871/**
6872 * Adds the given IsModified_* flag to the dirty flags of the machine.
6873 * This must be called either during i_loadSettings or under the machine write lock.
6874 * @param fl Flag
6875 * @param fAllowStateModification If state modifications are allowed.
6876 */
6877void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6878{
6879 mData->flModifications |= fl;
6880 if (fAllowStateModification && i_isStateModificationAllowed())
6881 mData->mCurrentStateModified = true;
6882}
6883
6884/**
6885 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6886 * care of the write locking.
6887 *
6888 * @param fModification The flag to add.
6889 * @param fAllowStateModification If state modifications are allowed.
6890 */
6891void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6892{
6893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6894 i_setModified(fModification, fAllowStateModification);
6895}
6896
6897/**
6898 * Saves the registry entry of this machine to the given configuration node.
6899 *
6900 * @param data Machine registry data.
6901 *
6902 * @note locks this object for reading.
6903 */
6904HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6905{
6906 AutoLimitedCaller autoCaller(this);
6907 AssertComRCReturnRC(autoCaller.hrc());
6908
6909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6910
6911 data.uuid = mData->mUuid;
6912 data.strSettingsFile = mData->m_strConfigFile;
6913
6914 return S_OK;
6915}
6916
6917/**
6918 * Calculates the absolute path of the given path taking the directory of the
6919 * machine settings file as the current directory.
6920 *
6921 * @param strPath Path to calculate the absolute path for.
6922 * @param aResult Where to put the result (used only on success, can be the
6923 * same Utf8Str instance as passed in @a aPath).
6924 * @return IPRT result.
6925 *
6926 * @note Locks this object for reading.
6927 */
6928int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6929{
6930 AutoCaller autoCaller(this);
6931 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
6932
6933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6934
6935 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6936
6937 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6938
6939 strSettingsDir.stripFilename();
6940 char szFolder[RTPATH_MAX];
6941 size_t cbFolder = sizeof(szFolder);
6942 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
6943 if (RT_SUCCESS(vrc))
6944 aResult = szFolder;
6945
6946 return vrc;
6947}
6948
6949/**
6950 * Copies strSource to strTarget, making it relative to the machine folder
6951 * if it is a subdirectory thereof, or simply copying it otherwise.
6952 *
6953 * @param strSource Path to evaluate and copy.
6954 * @param strTarget Buffer to receive target path.
6955 *
6956 * @note Locks this object for reading.
6957 */
6958void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6959 Utf8Str &strTarget)
6960{
6961 AutoCaller autoCaller(this);
6962 AssertComRCReturn(autoCaller.hrc(), (void)0);
6963
6964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6965
6966 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6967 // use strTarget as a temporary buffer to hold the machine settings dir
6968 strTarget = mData->m_strConfigFileFull;
6969 strTarget.stripFilename();
6970 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6971 {
6972 // is relative: then append what's left
6973 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6974 // for empty paths (only possible for subdirs) use "." to avoid
6975 // triggering default settings for not present config attributes.
6976 if (strTarget.isEmpty())
6977 strTarget = ".";
6978 }
6979 else
6980 // is not relative: then overwrite
6981 strTarget = strSource;
6982}
6983
6984/**
6985 * Returns the full path to the machine's log folder in the
6986 * \a aLogFolder argument.
6987 */
6988void Machine::i_getLogFolder(Utf8Str &aLogFolder)
6989{
6990 AutoCaller autoCaller(this);
6991 AssertComRCReturnVoid(autoCaller.hrc());
6992
6993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6994
6995 char szTmp[RTPATH_MAX];
6996 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6997 if (RT_SUCCESS(vrc))
6998 {
6999 if (szTmp[0] && !mUserData.isNull())
7000 {
7001 char szTmp2[RTPATH_MAX];
7002 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7003 if (RT_SUCCESS(vrc))
7004 aLogFolder.printf("%s%c%s",
7005 szTmp2,
7006 RTPATH_DELIMITER,
7007 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7008 }
7009 else
7010 vrc = VERR_PATH_IS_RELATIVE;
7011 }
7012
7013 if (RT_FAILURE(vrc))
7014 {
7015 // fallback if VBOX_USER_LOGHOME is not set or invalid
7016 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7017 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7018 aLogFolder.append(RTPATH_DELIMITER);
7019 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7020 }
7021}
7022
7023/**
7024 * Returns the full path to the machine's log file for an given index.
7025 */
7026Utf8Str Machine::i_getLogFilename(ULONG idx)
7027{
7028 Utf8Str logFolder;
7029 getLogFolder(logFolder);
7030 Assert(logFolder.length());
7031
7032 Utf8Str log;
7033 if (idx == 0)
7034 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7035#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7036 else if (idx == 1)
7037 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7038 else
7039 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7040#else
7041 else
7042 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7043#endif
7044 return log;
7045}
7046
7047/**
7048 * Returns the full path to the machine's hardened log file.
7049 */
7050Utf8Str Machine::i_getHardeningLogFilename(void)
7051{
7052 Utf8Str strFilename;
7053 getLogFolder(strFilename);
7054 Assert(strFilename.length());
7055 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7056 return strFilename;
7057}
7058
7059/**
7060 * Returns the default NVRAM filename based on the location of the VM config.
7061 * Note that this is a relative path.
7062 */
7063Utf8Str Machine::i_getDefaultNVRAMFilename()
7064{
7065 AutoCaller autoCaller(this);
7066 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7067
7068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7069
7070 if (i_isSnapshotMachine())
7071 return Utf8Str::Empty;
7072
7073 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7074 strNVRAMFilePath.stripPath();
7075 strNVRAMFilePath.stripSuffix();
7076 strNVRAMFilePath += ".nvram";
7077
7078 return strNVRAMFilePath;
7079}
7080
7081/**
7082 * Returns the NVRAM filename for a new snapshot. This intentionally works
7083 * similarly to the saved state file naming. Note that this is usually
7084 * a relative path, unless the snapshot folder is absolute.
7085 */
7086Utf8Str Machine::i_getSnapshotNVRAMFilename()
7087{
7088 AutoCaller autoCaller(this);
7089 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7090
7091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7092
7093 RTTIMESPEC ts;
7094 RTTimeNow(&ts);
7095 RTTIME time;
7096 RTTimeExplode(&time, &ts);
7097
7098 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7099 strNVRAMFilePath += RTPATH_DELIMITER;
7100 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7101 time.i32Year, time.u8Month, time.u8MonthDay,
7102 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7103
7104 return strNVRAMFilePath;
7105}
7106
7107/**
7108 * Returns the version of the settings file.
7109 */
7110SettingsVersion_T Machine::i_getSettingsVersion(void)
7111{
7112 AutoCaller autoCaller(this);
7113 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7114
7115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7116
7117 return mData->pMachineConfigFile->getSettingsVersion();
7118}
7119
7120/**
7121 * Composes a unique saved state filename based on the current system time. The filename is
7122 * granular to the second so this will work so long as no more than one snapshot is taken on
7123 * a machine per second.
7124 *
7125 * Before version 4.1, we used this formula for saved state files:
7126 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7127 * which no longer works because saved state files can now be shared between the saved state of the
7128 * "saved" machine and an online snapshot, and the following would cause problems:
7129 * 1) save machine
7130 * 2) create online snapshot from that machine state --> reusing saved state file
7131 * 3) save machine again --> filename would be reused, breaking the online snapshot
7132 *
7133 * So instead we now use a timestamp.
7134 *
7135 * @param strStateFilePath
7136 */
7137
7138void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7139{
7140 AutoCaller autoCaller(this);
7141 AssertComRCReturnVoid(autoCaller.hrc());
7142
7143 {
7144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7145 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7146 }
7147
7148 RTTIMESPEC ts;
7149 RTTimeNow(&ts);
7150 RTTIME time;
7151 RTTimeExplode(&time, &ts);
7152
7153 strStateFilePath += RTPATH_DELIMITER;
7154 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7155 time.i32Year, time.u8Month, time.u8MonthDay,
7156 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7157}
7158
7159/**
7160 * Returns whether at least one USB controller is present for the VM.
7161 */
7162bool Machine::i_isUSBControllerPresent()
7163{
7164 AutoCaller autoCaller(this);
7165 AssertComRCReturn(autoCaller.hrc(), false);
7166
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168
7169 return (mUSBControllers->size() > 0);
7170}
7171
7172
7173/**
7174 * @note Locks this object for writing, calls the client process
7175 * (inside the lock).
7176 */
7177HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7178 const Utf8Str &strFrontend,
7179 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7180 ProgressProxy *aProgress)
7181{
7182 LogFlowThisFuncEnter();
7183
7184 AssertReturn(aControl, E_FAIL);
7185 AssertReturn(aProgress, E_FAIL);
7186 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7187
7188 AutoCaller autoCaller(this);
7189 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7190
7191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7192
7193 if (!mData->mRegistered)
7194 return setError(E_UNEXPECTED,
7195 tr("The machine '%s' is not registered"),
7196 mUserData->s.strName.c_str());
7197
7198 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7199
7200 /* The process started when launching a VM with separate UI/VM processes is always
7201 * the UI process, i.e. needs special handling as it won't claim the session. */
7202 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7203
7204 if (fSeparate)
7205 {
7206 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7207 return setError(VBOX_E_INVALID_OBJECT_STATE,
7208 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7209 mUserData->s.strName.c_str());
7210 }
7211 else
7212 {
7213 if ( mData->mSession.mState == SessionState_Locked
7214 || mData->mSession.mState == SessionState_Spawning
7215 || mData->mSession.mState == SessionState_Unlocking)
7216 return setError(VBOX_E_INVALID_OBJECT_STATE,
7217 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7218 mUserData->s.strName.c_str());
7219
7220 /* may not be busy */
7221 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7222 }
7223
7224 /* Hardening logging */
7225#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7226 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7227 {
7228 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7229 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7230 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7231 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7232 {
7233 Utf8Str strStartupLogDir = strHardeningLogFile;
7234 strStartupLogDir.stripFilename();
7235 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7236 file without stripping the file. */
7237 }
7238 strSupHardeningLogArg.append(strHardeningLogFile);
7239
7240 /* Remove legacy log filename to avoid confusion. */
7241 Utf8Str strOldStartupLogFile;
7242 getLogFolder(strOldStartupLogFile);
7243 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7244 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7245 }
7246#else
7247 Utf8Str strSupHardeningLogArg;
7248#endif
7249
7250 Utf8Str strAppOverride;
7251#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7252 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7253#endif
7254
7255 bool fUseVBoxSDS = false;
7256 Utf8Str strCanonicalName;
7257 if (false)
7258 { }
7259#ifdef VBOX_WITH_QTGUI
7260 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7261 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7262 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7263 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7264 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7265 {
7266 strCanonicalName = "GUI/Qt";
7267 fUseVBoxSDS = true;
7268 }
7269#endif
7270#ifdef VBOX_WITH_VBOXSDL
7271 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7272 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7273 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7274 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7275 {
7276 strCanonicalName = "GUI/SDL";
7277 fUseVBoxSDS = true;
7278 }
7279#endif
7280#ifdef VBOX_WITH_HEADLESS
7281 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7282 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7283 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7284 {
7285 strCanonicalName = "headless";
7286 }
7287#endif
7288 else
7289 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7290
7291 Utf8Str idStr = mData->mUuid.toString();
7292 Utf8Str const &strMachineName = mUserData->s.strName;
7293 RTPROCESS pid = NIL_RTPROCESS;
7294
7295#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7296 RT_NOREF(fUseVBoxSDS);
7297#else
7298 DWORD idCallerSession = ~(DWORD)0;
7299 if (fUseVBoxSDS)
7300 {
7301 /*
7302 * The VBoxSDS should be used for process launching the VM with
7303 * GUI only if the caller and the VBoxSDS are in different Windows
7304 * sessions and the caller in the interactive one.
7305 */
7306 fUseVBoxSDS = false;
7307
7308 /* Get windows session of the current process. The process token used
7309 due to several reasons:
7310 1. The token is absent for the current thread except someone set it
7311 for us.
7312 2. Needs to get the id of the session where the process is started.
7313 We only need to do this once, though. */
7314 static DWORD s_idCurrentSession = ~(DWORD)0;
7315 DWORD idCurrentSession = s_idCurrentSession;
7316 if (idCurrentSession == ~(DWORD)0)
7317 {
7318 HANDLE hCurrentProcessToken = NULL;
7319 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7320 {
7321 DWORD cbIgn = 0;
7322 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7323 s_idCurrentSession = idCurrentSession;
7324 else
7325 {
7326 idCurrentSession = ~(DWORD)0;
7327 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7328 }
7329 CloseHandle(hCurrentProcessToken);
7330 }
7331 else
7332 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7333 }
7334
7335 /* get the caller's session */
7336 HRESULT hrc = CoImpersonateClient();
7337 if (SUCCEEDED(hrc))
7338 {
7339 HANDLE hCallerThreadToken;
7340 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7341 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7342 &hCallerThreadToken))
7343 {
7344 SetLastError(NO_ERROR);
7345 DWORD cbIgn = 0;
7346 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7347 {
7348 /* Only need to use SDS if the session ID differs: */
7349 if (idCurrentSession != idCallerSession)
7350 {
7351 fUseVBoxSDS = false;
7352
7353 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7354 DWORD cbTokenGroups = 0;
7355 PTOKEN_GROUPS pTokenGroups = NULL;
7356 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7357 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7358 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7359 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7360 {
7361 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7362 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7363 PSID pInteractiveSid = NULL;
7364 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7365 {
7366 /* Iterate over the groups looking for the interactive SID: */
7367 fUseVBoxSDS = false;
7368 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7369 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7370 {
7371 fUseVBoxSDS = true;
7372 break;
7373 }
7374 FreeSid(pInteractiveSid);
7375 }
7376 }
7377 else
7378 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7379 RTMemTmpFree(pTokenGroups);
7380 }
7381 }
7382 else
7383 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7384 CloseHandle(hCallerThreadToken);
7385 }
7386 else
7387 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7388 CoRevertToSelf();
7389 }
7390 else
7391 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7392 }
7393 if (fUseVBoxSDS)
7394 {
7395 /* connect to VBoxSDS */
7396 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7397 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7398 if (FAILED(hrc))
7399 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7400 strMachineName.c_str());
7401
7402 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7403 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7404 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7405 service to access the files. */
7406 hrc = CoSetProxyBlanket(pVBoxSDS,
7407 RPC_C_AUTHN_DEFAULT,
7408 RPC_C_AUTHZ_DEFAULT,
7409 COLE_DEFAULT_PRINCIPAL,
7410 RPC_C_AUTHN_LEVEL_DEFAULT,
7411 RPC_C_IMP_LEVEL_IMPERSONATE,
7412 NULL,
7413 EOAC_DEFAULT);
7414 if (FAILED(hrc))
7415 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7416
7417 size_t const cEnvVars = aEnvironmentChanges.size();
7418 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7419 for (size_t i = 0; i < cEnvVars; i++)
7420 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7421
7422 ULONG uPid = 0;
7423 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7424 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7425 idCallerSession, &uPid);
7426 if (FAILED(hrc))
7427 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7428 pid = (RTPROCESS)uPid;
7429 }
7430 else
7431#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7432 {
7433 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7434 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7435 if (RT_FAILURE(vrc))
7436 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7437 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7438 }
7439
7440 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7441 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7442
7443 if (!fSeparate)
7444 {
7445 /*
7446 * Note that we don't release the lock here before calling the client,
7447 * because it doesn't need to call us back if called with a NULL argument.
7448 * Releasing the lock here is dangerous because we didn't prepare the
7449 * launch data yet, but the client we've just started may happen to be
7450 * too fast and call LockMachine() that will fail (because of PID, etc.),
7451 * so that the Machine will never get out of the Spawning session state.
7452 */
7453
7454 /* inform the session that it will be a remote one */
7455 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7456#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7457 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7458#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7459 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7460#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7461 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
7462
7463 if (FAILED(hrc))
7464 {
7465 /* restore the session state */
7466 mData->mSession.mState = SessionState_Unlocked;
7467 alock.release();
7468 mParent->i_addProcessToReap(pid);
7469 /* The failure may occur w/o any error info (from RPC), so provide one */
7470 return setError(VBOX_E_VM_ERROR,
7471 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
7472 }
7473
7474 /* attach launch data to the machine */
7475 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7476 mData->mSession.mRemoteControls.push_back(aControl);
7477 mData->mSession.mProgress = aProgress;
7478 mData->mSession.mPID = pid;
7479 mData->mSession.mState = SessionState_Spawning;
7480 Assert(strCanonicalName.isNotEmpty());
7481 mData->mSession.mName = strCanonicalName;
7482 }
7483 else
7484 {
7485 /* For separate UI process we declare the launch as completed instantly, as the
7486 * actual headless VM start may or may not come. No point in remembering anything
7487 * yet, as what matters for us is when the headless VM gets started. */
7488 aProgress->i_notifyComplete(S_OK);
7489 }
7490
7491 alock.release();
7492 mParent->i_addProcessToReap(pid);
7493
7494 LogFlowThisFuncLeave();
7495 return S_OK;
7496}
7497
7498/**
7499 * Returns @c true if the given session machine instance has an open direct
7500 * session (and optionally also for direct sessions which are closing) and
7501 * returns the session control machine instance if so.
7502 *
7503 * Note that when the method returns @c false, the arguments remain unchanged.
7504 *
7505 * @param aMachine Session machine object.
7506 * @param aControl Direct session control object (optional).
7507 * @param aRequireVM If true then only allow VM sessions.
7508 * @param aAllowClosing If true then additionally a session which is currently
7509 * being closed will also be allowed.
7510 *
7511 * @note locks this object for reading.
7512 */
7513bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7514 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7515 bool aRequireVM /*= false*/,
7516 bool aAllowClosing /*= false*/)
7517{
7518 AutoLimitedCaller autoCaller(this);
7519 AssertComRCReturn(autoCaller.hrc(), false);
7520
7521 /* just return false for inaccessible machines */
7522 if (getObjectState().getState() != ObjectState::Ready)
7523 return false;
7524
7525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7526
7527 if ( ( mData->mSession.mState == SessionState_Locked
7528 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7529 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7530 )
7531 {
7532 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7533
7534 aMachine = mData->mSession.mMachine;
7535
7536 if (aControl != NULL)
7537 *aControl = mData->mSession.mDirectControl;
7538
7539 return true;
7540 }
7541
7542 return false;
7543}
7544
7545/**
7546 * Returns @c true if the given machine has an spawning direct session.
7547 *
7548 * @note locks this object for reading.
7549 */
7550bool Machine::i_isSessionSpawning()
7551{
7552 AutoLimitedCaller autoCaller(this);
7553 AssertComRCReturn(autoCaller.hrc(), false);
7554
7555 /* just return false for inaccessible machines */
7556 if (getObjectState().getState() != ObjectState::Ready)
7557 return false;
7558
7559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7560
7561 if (mData->mSession.mState == SessionState_Spawning)
7562 return true;
7563
7564 return false;
7565}
7566
7567/**
7568 * Called from the client watcher thread to check for unexpected client process
7569 * death during Session_Spawning state (e.g. before it successfully opened a
7570 * direct session).
7571 *
7572 * On Win32 and on OS/2, this method is called only when we've got the
7573 * direct client's process termination notification, so it always returns @c
7574 * true.
7575 *
7576 * On other platforms, this method returns @c true if the client process is
7577 * terminated and @c false if it's still alive.
7578 *
7579 * @note Locks this object for writing.
7580 */
7581bool Machine::i_checkForSpawnFailure()
7582{
7583 AutoCaller autoCaller(this);
7584 if (!autoCaller.isOk())
7585 {
7586 /* nothing to do */
7587 LogFlowThisFunc(("Already uninitialized!\n"));
7588 return true;
7589 }
7590
7591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7592
7593 if (mData->mSession.mState != SessionState_Spawning)
7594 {
7595 /* nothing to do */
7596 LogFlowThisFunc(("Not spawning any more!\n"));
7597 return true;
7598 }
7599
7600 /* PID not yet initialized, skip check. */
7601 if (mData->mSession.mPID == NIL_RTPROCESS)
7602 return false;
7603
7604 HRESULT hrc = S_OK;
7605 RTPROCSTATUS status;
7606 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7607 if (vrc != VERR_PROCESS_RUNNING)
7608 {
7609 Utf8Str strExtraInfo;
7610
7611#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7612 /* If the startup logfile exists and is of non-zero length, tell the
7613 user to look there for more details to encourage them to attach it
7614 when reporting startup issues. */
7615 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7616 uint64_t cbStartupLogFile = 0;
7617 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7618 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7619 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7620#endif
7621
7622 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7623 hrc = setError(E_FAIL,
7624 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7625 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7626 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7627 hrc = setError(E_FAIL,
7628 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7629 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7630 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7631 hrc = setError(E_FAIL,
7632 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7633 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7634 else
7635 hrc = setErrorBoth(E_FAIL, vrc,
7636 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7637 i_getName().c_str(), vrc, strExtraInfo.c_str());
7638 }
7639
7640 if (FAILED(hrc))
7641 {
7642 /* Close the remote session, remove the remote control from the list
7643 * and reset session state to Closed (@note keep the code in sync with
7644 * the relevant part in LockMachine()). */
7645
7646 Assert(mData->mSession.mRemoteControls.size() == 1);
7647 if (mData->mSession.mRemoteControls.size() == 1)
7648 {
7649 ErrorInfoKeeper eik;
7650 mData->mSession.mRemoteControls.front()->Uninitialize();
7651 }
7652
7653 mData->mSession.mRemoteControls.clear();
7654 mData->mSession.mState = SessionState_Unlocked;
7655
7656 /* finalize the progress after setting the state */
7657 if (!mData->mSession.mProgress.isNull())
7658 {
7659 mData->mSession.mProgress->notifyComplete(hrc);
7660 mData->mSession.mProgress.setNull();
7661 }
7662
7663 mData->mSession.mPID = NIL_RTPROCESS;
7664
7665 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7666 return true;
7667 }
7668
7669 return false;
7670}
7671
7672/**
7673 * Checks whether the machine can be registered. If so, commits and saves
7674 * all settings.
7675 *
7676 * @note Must be called from mParent's write lock. Locks this object and
7677 * children for writing.
7678 */
7679HRESULT Machine::i_prepareRegister()
7680{
7681 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7682
7683 AutoLimitedCaller autoCaller(this);
7684 AssertComRCReturnRC(autoCaller.hrc());
7685
7686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7687
7688 /* wait for state dependents to drop to zero */
7689 i_ensureNoStateDependencies(alock);
7690
7691 if (!mData->mAccessible)
7692 return setError(VBOX_E_INVALID_OBJECT_STATE,
7693 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7694 mUserData->s.strName.c_str(),
7695 mData->mUuid.toString().c_str());
7696
7697 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7698
7699 if (mData->mRegistered)
7700 return setError(VBOX_E_INVALID_OBJECT_STATE,
7701 tr("The machine '%s' with UUID {%s} is already registered"),
7702 mUserData->s.strName.c_str(),
7703 mData->mUuid.toString().c_str());
7704
7705 HRESULT hrc = S_OK;
7706
7707 // Ensure the settings are saved. If we are going to be registered and
7708 // no config file exists yet, create it by calling i_saveSettings() too.
7709 if ( (mData->flModifications)
7710 || (!mData->pMachineConfigFile->fileExists())
7711 )
7712 {
7713 hrc = i_saveSettings(NULL, alock);
7714 // no need to check whether VirtualBox.xml needs saving too since
7715 // we can't have a machine XML file rename pending
7716 if (FAILED(hrc)) return hrc;
7717 }
7718
7719 /* more config checking goes here */
7720
7721 if (SUCCEEDED(hrc))
7722 {
7723 /* we may have had implicit modifications we want to fix on success */
7724 i_commit();
7725
7726 mData->mRegistered = true;
7727 }
7728 else
7729 {
7730 /* we may have had implicit modifications we want to cancel on failure*/
7731 i_rollback(false /* aNotify */);
7732 }
7733
7734 return hrc;
7735}
7736
7737/**
7738 * Increases the number of objects dependent on the machine state or on the
7739 * registered state. Guarantees that these two states will not change at least
7740 * until #i_releaseStateDependency() is called.
7741 *
7742 * Depending on the @a aDepType value, additional state checks may be made.
7743 * These checks will set extended error info on failure. See
7744 * #i_checkStateDependency() for more info.
7745 *
7746 * If this method returns a failure, the dependency is not added and the caller
7747 * is not allowed to rely on any particular machine state or registration state
7748 * value and may return the failed result code to the upper level.
7749 *
7750 * @param aDepType Dependency type to add.
7751 * @param aState Current machine state (NULL if not interested).
7752 * @param aRegistered Current registered state (NULL if not interested).
7753 *
7754 * @note Locks this object for writing.
7755 */
7756HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7757 MachineState_T *aState /* = NULL */,
7758 BOOL *aRegistered /* = NULL */)
7759{
7760 AutoCaller autoCaller(this);
7761 AssertComRCReturnRC(autoCaller.hrc());
7762
7763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7764
7765 HRESULT hrc = i_checkStateDependency(aDepType);
7766 if (FAILED(hrc)) return hrc;
7767
7768 {
7769 if (mData->mMachineStateChangePending != 0)
7770 {
7771 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7772 * drop to zero so don't add more. It may make sense to wait a bit
7773 * and retry before reporting an error (since the pending state
7774 * transition should be really quick) but let's just assert for
7775 * now to see if it ever happens on practice. */
7776
7777 AssertFailed();
7778
7779 return setError(E_ACCESSDENIED,
7780 tr("Machine state change is in progress. Please retry the operation later."));
7781 }
7782
7783 ++mData->mMachineStateDeps;
7784 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7785 }
7786
7787 if (aState)
7788 *aState = mData->mMachineState;
7789 if (aRegistered)
7790 *aRegistered = mData->mRegistered;
7791
7792 return S_OK;
7793}
7794
7795/**
7796 * Decreases the number of objects dependent on the machine state.
7797 * Must always complete the #i_addStateDependency() call after the state
7798 * dependency is no more necessary.
7799 */
7800void Machine::i_releaseStateDependency()
7801{
7802 AutoCaller autoCaller(this);
7803 AssertComRCReturnVoid(autoCaller.hrc());
7804
7805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7806
7807 /* releaseStateDependency() w/o addStateDependency()? */
7808 AssertReturnVoid(mData->mMachineStateDeps != 0);
7809 -- mData->mMachineStateDeps;
7810
7811 if (mData->mMachineStateDeps == 0)
7812 {
7813 /* inform i_ensureNoStateDependencies() that there are no more deps */
7814 if (mData->mMachineStateChangePending != 0)
7815 {
7816 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7817 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7818 }
7819 }
7820}
7821
7822Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7823{
7824 /* start with nothing found */
7825 Utf8Str strResult("");
7826
7827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7828
7829 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7830 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7831 // found:
7832 strResult = it->second; // source is a Utf8Str
7833
7834 return strResult;
7835}
7836
7837FirmwareType_T Machine::i_getFirmwareType() const
7838{
7839 return mFirmwareSettings->i_getFirmwareType();
7840}
7841
7842// protected methods
7843/////////////////////////////////////////////////////////////////////////////
7844
7845/**
7846 * Performs machine state checks based on the @a aDepType value. If a check
7847 * fails, this method will set extended error info, otherwise it will return
7848 * S_OK. It is supposed, that on failure, the caller will immediately return
7849 * the return value of this method to the upper level.
7850 *
7851 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7852 *
7853 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7854 * current state of this machine object allows to change settings of the
7855 * machine (i.e. the machine is not registered, or registered but not running
7856 * and not saved). It is useful to call this method from Machine setters
7857 * before performing any change.
7858 *
7859 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7860 * as for MutableStateDep except that if the machine is saved, S_OK is also
7861 * returned. This is useful in setters which allow changing machine
7862 * properties when it is in the saved state.
7863 *
7864 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7865 * if the current state of this machine object allows to change runtime
7866 * changeable settings of the machine (i.e. the machine is not registered, or
7867 * registered but either running or not running and not saved). It is useful
7868 * to call this method from Machine setters before performing any changes to
7869 * runtime changeable settings.
7870 *
7871 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7872 * the same as for MutableOrRunningStateDep except that if the machine is
7873 * saved, S_OK is also returned. This is useful in setters which allow
7874 * changing runtime and saved state changeable machine properties.
7875 *
7876 * @param aDepType Dependency type to check.
7877 *
7878 * @note Non Machine based classes should use #i_addStateDependency() and
7879 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7880 * template.
7881 *
7882 * @note This method must be called from under this object's read or write
7883 * lock.
7884 */
7885HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7886{
7887 switch (aDepType)
7888 {
7889 case AnyStateDep:
7890 {
7891 break;
7892 }
7893 case MutableStateDep:
7894 {
7895 if ( mData->mRegistered
7896 && ( !i_isSessionMachine()
7897 || ( mData->mMachineState != MachineState_Aborted
7898 && mData->mMachineState != MachineState_Teleported
7899 && mData->mMachineState != MachineState_PoweredOff
7900 )
7901 )
7902 )
7903 return setError(VBOX_E_INVALID_VM_STATE,
7904 tr("The machine is not mutable (state is %s)"),
7905 Global::stringifyMachineState(mData->mMachineState));
7906 break;
7907 }
7908 case MutableOrSavedStateDep:
7909 {
7910 if ( mData->mRegistered
7911 && ( !i_isSessionMachine()
7912 || ( mData->mMachineState != MachineState_Aborted
7913 && mData->mMachineState != MachineState_Teleported
7914 && mData->mMachineState != MachineState_Saved
7915 && mData->mMachineState != MachineState_AbortedSaved
7916 && mData->mMachineState != MachineState_PoweredOff
7917 )
7918 )
7919 )
7920 return setError(VBOX_E_INVALID_VM_STATE,
7921 tr("The machine is not mutable or saved (state is %s)"),
7922 Global::stringifyMachineState(mData->mMachineState));
7923 break;
7924 }
7925 case MutableOrRunningStateDep:
7926 {
7927 if ( mData->mRegistered
7928 && ( !i_isSessionMachine()
7929 || ( mData->mMachineState != MachineState_Aborted
7930 && mData->mMachineState != MachineState_Teleported
7931 && mData->mMachineState != MachineState_PoweredOff
7932 && !Global::IsOnline(mData->mMachineState)
7933 )
7934 )
7935 )
7936 return setError(VBOX_E_INVALID_VM_STATE,
7937 tr("The machine is not mutable or running (state is %s)"),
7938 Global::stringifyMachineState(mData->mMachineState));
7939 break;
7940 }
7941 case MutableOrSavedOrRunningStateDep:
7942 {
7943 if ( mData->mRegistered
7944 && ( !i_isSessionMachine()
7945 || ( mData->mMachineState != MachineState_Aborted
7946 && mData->mMachineState != MachineState_Teleported
7947 && mData->mMachineState != MachineState_Saved
7948 && mData->mMachineState != MachineState_AbortedSaved
7949 && mData->mMachineState != MachineState_PoweredOff
7950 && !Global::IsOnline(mData->mMachineState)
7951 )
7952 )
7953 )
7954 return setError(VBOX_E_INVALID_VM_STATE,
7955 tr("The machine is not mutable, saved or running (state is %s)"),
7956 Global::stringifyMachineState(mData->mMachineState));
7957 break;
7958 }
7959 }
7960
7961 return S_OK;
7962}
7963
7964/**
7965 * Helper to initialize all associated child objects and allocate data
7966 * structures.
7967 *
7968 * This method must be called as a part of the object's initialization procedure
7969 * (usually done in the #init() method).
7970 *
7971 * @note Must be called only from #init() or from #i_registeredInit().
7972 */
7973HRESULT Machine::initDataAndChildObjects()
7974{
7975 AutoCaller autoCaller(this);
7976 AssertComRCReturnRC(autoCaller.hrc());
7977 AssertReturn( getObjectState().getState() == ObjectState::InInit
7978 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7979
7980 AssertReturn(!mData->mAccessible, E_FAIL);
7981
7982 /* allocate data structures */
7983 mSSData.allocate();
7984 mUserData.allocate();
7985 mHWData.allocate();
7986 mMediumAttachments.allocate();
7987 mStorageControllers.allocate();
7988 mUSBControllers.allocate();
7989
7990 /* create the platform + platform properties objects for this machine */
7991 HRESULT hrc = unconst(mPlatform).createObject();
7992 ComAssertComRCRetRC(hrc);
7993 hrc = mPlatform->init(this);
7994 ComAssertComRCRetRC(hrc);
7995 hrc = unconst(mPlatformProperties).createObject();
7996 ComAssertComRCRetRC(hrc);
7997 hrc = mPlatformProperties->init(mParent);
7998 ComAssertComRCRetRC(hrc);
7999
8000 /* initialize mOSTypeId */
8001 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8002
8003 /* create associated firmware settings object */
8004 unconst(mFirmwareSettings).createObject();
8005 mFirmwareSettings->init(this);
8006
8007 /* create associated recording settings object */
8008 unconst(mRecordingSettings).createObject();
8009 mRecordingSettings->init(this);
8010
8011 /* create associated trusted platform module object */
8012 unconst(mTrustedPlatformModule).createObject();
8013 mTrustedPlatformModule->init(this);
8014
8015 /* create associated NVRAM store object */
8016 unconst(mNvramStore).createObject();
8017 mNvramStore->init(this);
8018
8019 /* create the graphics adapter object (always present) */
8020 unconst(mGraphicsAdapter).createObject();
8021 mGraphicsAdapter->init(this);
8022
8023 /* create an associated VRDE object (default is disabled) */
8024 unconst(mVRDEServer).createObject();
8025 mVRDEServer->init(this);
8026
8027 /* create associated serial port objects */
8028 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8029 {
8030 unconst(mSerialPorts[slot]).createObject();
8031 mSerialPorts[slot]->init(this, slot);
8032 }
8033
8034 /* create associated parallel port objects */
8035 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8036 {
8037 unconst(mParallelPorts[slot]).createObject();
8038 mParallelPorts[slot]->init(this, slot);
8039 }
8040
8041 /* create the audio settings object */
8042 unconst(mAudioSettings).createObject();
8043 mAudioSettings->init(this);
8044
8045 /* create the USB device filters object (always present) */
8046 unconst(mUSBDeviceFilters).createObject();
8047 mUSBDeviceFilters->init(this);
8048
8049 /* create associated network adapter objects */
8050 ChipsetType_T enmChipsetType;
8051 hrc = mPlatform->getChipsetType(&enmChipsetType);
8052 ComAssertComRC(hrc);
8053
8054 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType));
8055 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8056 {
8057 unconst(mNetworkAdapters[slot]).createObject();
8058 mNetworkAdapters[slot]->init(this, slot);
8059 }
8060
8061 /* create the bandwidth control */
8062 unconst(mBandwidthControl).createObject();
8063 mBandwidthControl->init(this);
8064
8065 /* create the guest debug control object */
8066 unconst(mGuestDebugControl).createObject();
8067 mGuestDebugControl->init(this);
8068
8069 return hrc;
8070}
8071
8072/**
8073 * Helper to uninitialize all associated child objects and to free all data
8074 * structures.
8075 *
8076 * This method must be called as a part of the object's uninitialization
8077 * procedure (usually done in the #uninit() method).
8078 *
8079 * @note Must be called only from #uninit() or from #i_registeredInit().
8080 */
8081void Machine::uninitDataAndChildObjects()
8082{
8083 AutoCaller autoCaller(this);
8084 AssertComRCReturnVoid(autoCaller.hrc());
8085 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8086 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8087 || getObjectState().getState() == ObjectState::InUninit
8088 || getObjectState().getState() == ObjectState::Limited);
8089
8090 /* tell all our other child objects we've been uninitialized */
8091 if (mGuestDebugControl)
8092 {
8093 mGuestDebugControl->uninit();
8094 unconst(mGuestDebugControl).setNull();
8095 }
8096
8097 if (mBandwidthControl)
8098 {
8099 mBandwidthControl->uninit();
8100 unconst(mBandwidthControl).setNull();
8101 }
8102
8103 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8104 {
8105 if (mNetworkAdapters[slot])
8106 {
8107 mNetworkAdapters[slot]->uninit();
8108 unconst(mNetworkAdapters[slot]).setNull();
8109 }
8110 }
8111
8112 if (mUSBDeviceFilters)
8113 {
8114 mUSBDeviceFilters->uninit();
8115 unconst(mUSBDeviceFilters).setNull();
8116 }
8117
8118 if (mAudioSettings)
8119 {
8120 mAudioSettings->uninit();
8121 unconst(mAudioSettings).setNull();
8122 }
8123
8124 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8125 {
8126 if (mParallelPorts[slot])
8127 {
8128 mParallelPorts[slot]->uninit();
8129 unconst(mParallelPorts[slot]).setNull();
8130 }
8131 }
8132
8133 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8134 {
8135 if (mSerialPorts[slot])
8136 {
8137 mSerialPorts[slot]->uninit();
8138 unconst(mSerialPorts[slot]).setNull();
8139 }
8140 }
8141
8142 if (mVRDEServer)
8143 {
8144 mVRDEServer->uninit();
8145 unconst(mVRDEServer).setNull();
8146 }
8147
8148 if (mGraphicsAdapter)
8149 {
8150 mGraphicsAdapter->uninit();
8151 unconst(mGraphicsAdapter).setNull();
8152 }
8153
8154 if (mPlatform)
8155 {
8156 mPlatform->uninit();
8157 unconst(mPlatform).setNull();
8158 }
8159
8160 if (mPlatformProperties)
8161 {
8162 mPlatformProperties->uninit();
8163 unconst(mPlatformProperties).setNull();
8164 }
8165
8166 if (mFirmwareSettings)
8167 {
8168 mFirmwareSettings->uninit();
8169 unconst(mFirmwareSettings).setNull();
8170 }
8171
8172 if (mRecordingSettings)
8173 {
8174 mRecordingSettings->uninit();
8175 unconst(mRecordingSettings).setNull();
8176 }
8177
8178 if (mTrustedPlatformModule)
8179 {
8180 mTrustedPlatformModule->uninit();
8181 unconst(mTrustedPlatformModule).setNull();
8182 }
8183
8184 if (mNvramStore)
8185 {
8186 mNvramStore->uninit();
8187 unconst(mNvramStore).setNull();
8188 }
8189
8190 /* Deassociate media (only when a real Machine or a SnapshotMachine
8191 * instance is uninitialized; SessionMachine instances refer to real
8192 * Machine media). This is necessary for a clean re-initialization of
8193 * the VM after successfully re-checking the accessibility state. Note
8194 * that in case of normal Machine or SnapshotMachine uninitialization (as
8195 * a result of unregistering or deleting the snapshot), outdated media
8196 * attachments will already be uninitialized and deleted, so this
8197 * code will not affect them. */
8198 if ( !mMediumAttachments.isNull()
8199 && !i_isSessionMachine()
8200 )
8201 {
8202 for (MediumAttachmentList::const_iterator
8203 it = mMediumAttachments->begin();
8204 it != mMediumAttachments->end();
8205 ++it)
8206 {
8207 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8208 if (pMedium.isNull())
8209 continue;
8210 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8211 AssertComRC(hrc);
8212 }
8213 }
8214
8215 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8216 {
8217 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8218 if (mData->mFirstSnapshot)
8219 {
8220 // Snapshots tree is protected by machine write lock.
8221 // Otherwise we assert in Snapshot::uninit()
8222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8223 mData->mFirstSnapshot->uninit();
8224 mData->mFirstSnapshot.setNull();
8225 }
8226
8227 mData->mCurrentSnapshot.setNull();
8228 }
8229
8230 /* free data structures (the essential mData structure is not freed here
8231 * since it may be still in use) */
8232 mMediumAttachments.free();
8233 mStorageControllers.free();
8234 mUSBControllers.free();
8235 mHWData.free();
8236 mUserData.free();
8237 mSSData.free();
8238}
8239
8240/**
8241 * Returns a pointer to the Machine object for this machine that acts like a
8242 * parent for complex machine data objects such as shared folders, etc.
8243 *
8244 * For primary Machine objects and for SnapshotMachine objects, returns this
8245 * object's pointer itself. For SessionMachine objects, returns the peer
8246 * (primary) machine pointer.
8247 */
8248Machine *Machine::i_getMachine()
8249{
8250 if (i_isSessionMachine())
8251 return (Machine*)mPeer;
8252 return this;
8253}
8254
8255/**
8256 * Makes sure that there are no machine state dependents. If necessary, waits
8257 * for the number of dependents to drop to zero.
8258 *
8259 * Make sure this method is called from under this object's write lock to
8260 * guarantee that no new dependents may be added when this method returns
8261 * control to the caller.
8262 *
8263 * @note Receives a lock to this object for writing. The lock will be released
8264 * while waiting (if necessary).
8265 *
8266 * @warning To be used only in methods that change the machine state!
8267 */
8268void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8269{
8270 AssertReturnVoid(isWriteLockOnCurrentThread());
8271
8272 /* Wait for all state dependents if necessary */
8273 if (mData->mMachineStateDeps != 0)
8274 {
8275 /* lazy semaphore creation */
8276 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8277 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8278
8279 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8280 mData->mMachineStateDeps));
8281
8282 ++mData->mMachineStateChangePending;
8283
8284 /* reset the semaphore before waiting, the last dependent will signal
8285 * it */
8286 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8287
8288 alock.release();
8289
8290 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8291
8292 alock.acquire();
8293
8294 -- mData->mMachineStateChangePending;
8295 }
8296}
8297
8298/**
8299 * Changes the machine state and informs callbacks.
8300 *
8301 * This method is not intended to fail so it either returns S_OK or asserts (and
8302 * returns a failure).
8303 *
8304 * @note Locks this object for writing.
8305 */
8306HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8307{
8308 LogFlowThisFuncEnter();
8309 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8310 Assert(aMachineState != MachineState_Null);
8311
8312 AutoCaller autoCaller(this);
8313 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8314
8315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8316
8317 /* wait for state dependents to drop to zero */
8318 i_ensureNoStateDependencies(alock);
8319
8320 MachineState_T const enmOldState = mData->mMachineState;
8321 if (enmOldState != aMachineState)
8322 {
8323 mData->mMachineState = aMachineState;
8324 RTTimeNow(&mData->mLastStateChange);
8325
8326#ifdef VBOX_WITH_DTRACE_R3_MAIN
8327 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8328#endif
8329 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8330 }
8331
8332 LogFlowThisFuncLeave();
8333 return S_OK;
8334}
8335
8336/**
8337 * Searches for a shared folder with the given logical name
8338 * in the collection of shared folders.
8339 *
8340 * @param aName logical name of the shared folder
8341 * @param aSharedFolder where to return the found object
8342 * @param aSetError whether to set the error info if the folder is
8343 * not found
8344 * @return
8345 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8346 *
8347 * @note
8348 * must be called from under the object's lock!
8349 */
8350HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8351 ComObjPtr<SharedFolder> &aSharedFolder,
8352 bool aSetError /* = false */)
8353{
8354 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8355 for (HWData::SharedFolderList::const_iterator
8356 it = mHWData->mSharedFolders.begin();
8357 it != mHWData->mSharedFolders.end();
8358 ++it)
8359 {
8360 SharedFolder *pSF = *it;
8361 AutoCaller autoCaller(pSF);
8362 if (pSF->i_getName() == aName)
8363 {
8364 aSharedFolder = pSF;
8365 hrc = S_OK;
8366 break;
8367 }
8368 }
8369
8370 if (aSetError && FAILED(hrc))
8371 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8372
8373 return hrc;
8374}
8375
8376/**
8377 * Initializes all machine instance data from the given settings structures
8378 * from XML. The exception is the machine UUID which needs special handling
8379 * depending on the caller's use case, so the caller needs to set that herself.
8380 *
8381 * This gets called in several contexts during machine initialization:
8382 *
8383 * -- When machine XML exists on disk already and needs to be loaded into memory,
8384 * for example, from #i_registeredInit() to load all registered machines on
8385 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8386 * attached to the machine should be part of some media registry already.
8387 *
8388 * -- During OVF import, when a machine config has been constructed from an
8389 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8390 * ensure that the media listed as attachments in the config (which have
8391 * been imported from the OVF) receive the correct registry ID.
8392 *
8393 * -- During VM cloning.
8394 *
8395 * @param config Machine settings from XML.
8396 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8397 * for each attached medium in the config.
8398 * @return
8399 */
8400HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8401 const Guid *puuidRegistry)
8402{
8403 // copy name, description, OS type, teleporter, UTC etc.
8404 mUserData->s = config.machineUserData;
8405
8406 // look up the object by Id to check it is valid
8407 ComObjPtr<GuestOSType> pGuestOSType;
8408 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8409 if (!pGuestOSType.isNull())
8410 mUserData->s.strOsType = pGuestOSType->i_id();
8411
8412#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8413 // stateFile encryption (optional)
8414 mSSData->strStateKeyId = config.strStateKeyId;
8415 mSSData->strStateKeyStore = config.strStateKeyStore;
8416 mData->mstrLogKeyId = config.strLogKeyId;
8417 mData->mstrLogKeyStore = config.strLogKeyStore;
8418#endif
8419
8420 // stateFile (optional)
8421 if (config.strStateFile.isEmpty())
8422 mSSData->strStateFilePath.setNull();
8423 else
8424 {
8425 Utf8Str stateFilePathFull(config.strStateFile);
8426 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8427 if (RT_FAILURE(vrc))
8428 return setErrorBoth(E_FAIL, vrc,
8429 tr("Invalid saved state file path '%s' (%Rrc)"),
8430 config.strStateFile.c_str(),
8431 vrc);
8432 mSSData->strStateFilePath = stateFilePathFull;
8433 }
8434
8435 // snapshot folder needs special processing so set it again
8436 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8437 if (FAILED(hrc)) return hrc;
8438
8439 /* Copy the extra data items (config may or may not be the same as
8440 * mData->pMachineConfigFile) if necessary. When loading the XML files
8441 * from disk they are the same, but not for OVF import. */
8442 if (mData->pMachineConfigFile != &config)
8443 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8444
8445 /* currentStateModified (optional, default is true) */
8446 mData->mCurrentStateModified = config.fCurrentStateModified;
8447
8448 mData->mLastStateChange = config.timeLastStateChange;
8449
8450 /*
8451 * note: all mUserData members must be assigned prior this point because
8452 * we need to commit changes in order to let mUserData be shared by all
8453 * snapshot machine instances.
8454 */
8455 mUserData.commitCopy();
8456
8457 // machine registry, if present (must be loaded before snapshots)
8458 if (config.canHaveOwnMediaRegistry())
8459 {
8460 // determine machine folder
8461 Utf8Str strMachineFolder = i_getSettingsFileFull();
8462 strMachineFolder.stripFilename();
8463 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8464 config.mediaRegistry,
8465 strMachineFolder);
8466 if (FAILED(hrc)) return hrc;
8467 }
8468
8469 /* Snapshot node (optional) */
8470 size_t cRootSnapshots;
8471 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8472 {
8473 // there must be only one root snapshot
8474 Assert(cRootSnapshots == 1);
8475 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8476
8477 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
8478 if (FAILED(hrc)) return hrc;
8479 }
8480
8481 // hardware data
8482 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
8483 config.recordingSettings);
8484 if (FAILED(hrc)) return hrc;
8485
8486 /*
8487 * NOTE: the assignment below must be the last thing to do,
8488 * otherwise it will be not possible to change the settings
8489 * somewhere in the code above because all setters will be
8490 * blocked by i_checkStateDependency(MutableStateDep).
8491 */
8492
8493 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8494 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8495 {
8496 /* no need to use i_setMachineState() during init() */
8497 mData->mMachineState = MachineState_AbortedSaved;
8498 }
8499 else if (config.fAborted)
8500 {
8501 mSSData->strStateFilePath.setNull();
8502
8503 /* no need to use i_setMachineState() during init() */
8504 mData->mMachineState = MachineState_Aborted;
8505 }
8506 else if (!mSSData->strStateFilePath.isEmpty())
8507 {
8508 /* no need to use i_setMachineState() during init() */
8509 mData->mMachineState = MachineState_Saved;
8510 }
8511
8512 // after loading settings, we are no longer different from the XML on disk
8513 mData->flModifications = 0;
8514
8515 return S_OK;
8516}
8517
8518/**
8519 * Loads all snapshots starting from the given settings.
8520 *
8521 * @param data snapshot settings.
8522 * @param aCurSnapshotId Current snapshot ID from the settings file.
8523 */
8524HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8525 const Guid &aCurSnapshotId)
8526{
8527 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8528 AssertReturn(!i_isSessionMachine(), E_FAIL);
8529
8530 HRESULT hrc = S_OK;
8531
8532 std::list<const settings::Snapshot *> llSettingsTodo;
8533 llSettingsTodo.push_back(&data);
8534 std::list<Snapshot *> llParentsTodo;
8535 llParentsTodo.push_back(NULL);
8536
8537 while (llSettingsTodo.size() > 0)
8538 {
8539 const settings::Snapshot *current = llSettingsTodo.front();
8540 llSettingsTodo.pop_front();
8541 Snapshot *pParent = llParentsTodo.front();
8542 llParentsTodo.pop_front();
8543
8544 Utf8Str strStateFile;
8545 if (!current->strStateFile.isEmpty())
8546 {
8547 /* optional */
8548 strStateFile = current->strStateFile;
8549 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8550 if (RT_FAILURE(vrc))
8551 {
8552 setErrorBoth(E_FAIL, vrc,
8553 tr("Invalid saved state file path '%s' (%Rrc)"),
8554 strStateFile.c_str(), vrc);
8555 }
8556 }
8557
8558 /* create a snapshot machine object */
8559 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8560 pSnapshotMachine.createObject();
8561 hrc = pSnapshotMachine->initFromSettings(this,
8562 current->hardware,
8563 &current->debugging,
8564 &current->autostart,
8565 current->recordingSettings,
8566 current->uuid.ref(),
8567 strStateFile);
8568 if (FAILED(hrc)) break;
8569
8570 /* create a snapshot object */
8571 ComObjPtr<Snapshot> pSnapshot;
8572 pSnapshot.createObject();
8573 /* initialize the snapshot */
8574 hrc = pSnapshot->init(mParent, // VirtualBox object
8575 current->uuid,
8576 current->strName,
8577 current->strDescription,
8578 current->timestamp,
8579 pSnapshotMachine,
8580 pParent);
8581 if (FAILED(hrc)) break;
8582
8583 /* memorize the first snapshot if necessary */
8584 if (!mData->mFirstSnapshot)
8585 {
8586 Assert(pParent == NULL);
8587 mData->mFirstSnapshot = pSnapshot;
8588 }
8589
8590 /* memorize the current snapshot when appropriate */
8591 if ( !mData->mCurrentSnapshot
8592 && pSnapshot->i_getId() == aCurSnapshotId
8593 )
8594 mData->mCurrentSnapshot = pSnapshot;
8595
8596 /* create all children */
8597 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8598 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8599 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8600 {
8601 llSettingsTodo.push_back(&*it);
8602 llParentsTodo.push_back(pSnapshot);
8603 }
8604 }
8605
8606 return hrc;
8607}
8608
8609/**
8610 * Loads settings into mHWData.
8611 *
8612 * @param puuidRegistry Registry ID.
8613 * @param puuidSnapshot Snapshot ID
8614 * @param data Reference to the hardware settings.
8615 * @param pDbg Pointer to the debugging settings.
8616 * @param pAutostart Pointer to the autostart settings
8617 * @param recording Reference to recording settings.
8618 */
8619HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8620 const Guid *puuidSnapshot,
8621 const settings::Hardware &data,
8622 const settings::Debugging *pDbg,
8623 const settings::Autostart *pAutostart,
8624 const settings::Recording &recording)
8625{
8626 AssertReturn(!i_isSessionMachine(), E_FAIL);
8627
8628 HRESULT hrc = S_OK;
8629
8630 try
8631 {
8632 ComObjPtr<GuestOSType> pGuestOSType;
8633 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8634
8635 /* The hardware version attribute (optional). */
8636 mHWData->mHWVersion = data.strVersion;
8637 mHWData->mHardwareUUID = data.uuid;
8638
8639 mHWData->mCPUCount = data.cCPUs;
8640 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8641 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8642 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8643 mHWData->mCpuProfile = data.strCpuProfile;
8644
8645 // cpu
8646 if (mHWData->mCPUHotPlugEnabled)
8647 {
8648 for (settings::CpuList::const_iterator
8649 it = data.llCpus.begin();
8650 it != data.llCpus.end();
8651 ++it)
8652 {
8653 const settings::Cpu &cpu = *it;
8654
8655 mHWData->mCPUAttached[cpu.ulId] = true;
8656 }
8657 }
8658
8659 mHWData->mMemorySize = data.ulMemorySizeMB;
8660 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8661
8662 // boot order
8663 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8664 {
8665 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8666 if (it == data.mapBootOrder.end())
8667 mHWData->mBootOrder[i] = DeviceType_Null;
8668 else
8669 mHWData->mBootOrder[i] = it->second;
8670 }
8671
8672 mHWData->mPointingHIDType = data.pointingHIDType;
8673 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8674 mHWData->mParavirtProvider = data.paravirtProvider;
8675 mHWData->mParavirtDebug = data.strParavirtDebug;
8676 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8677
8678 /* GraphicsAdapter */
8679 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8680 if (FAILED(hrc)) return hrc;
8681
8682 /* VRDEServer */
8683 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8684 if (FAILED(hrc)) return hrc;
8685
8686 /* Platform */
8687 hrc = mPlatform->i_loadSettings(data.platformSettings);
8688 if (FAILED(hrc)) return hrc;
8689
8690 i_platformPropertiesUpdate();
8691
8692 /* Firmware */
8693 hrc = mFirmwareSettings->i_loadSettings(data.firmwareSettings);
8694 if (FAILED(hrc)) return hrc;
8695
8696 /* Recording */
8697 hrc = mRecordingSettings->i_loadSettings(recording);
8698 if (FAILED(hrc)) return hrc;
8699
8700 /* Trusted Platform Module */
8701 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8702 if (FAILED(hrc)) return hrc;
8703
8704 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
8705 if (FAILED(hrc)) return hrc;
8706
8707 // Bandwidth control (must come before network adapters)
8708 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
8709 if (FAILED(hrc)) return hrc;
8710
8711 /* USB controllers */
8712 for (settings::USBControllerList::const_iterator
8713 it = data.usbSettings.llUSBControllers.begin();
8714 it != data.usbSettings.llUSBControllers.end();
8715 ++it)
8716 {
8717 const settings::USBController &settingsCtrl = *it;
8718 ComObjPtr<USBController> newCtrl;
8719
8720 newCtrl.createObject();
8721 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8722 mUSBControllers->push_back(newCtrl);
8723 }
8724
8725 /* USB device filters */
8726 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8727 if (FAILED(hrc)) return hrc;
8728
8729 // network adapters (establish array size first and apply defaults, to
8730 // ensure reading the same settings as we saved, since the list skips
8731 // adapters having defaults)
8732 size_t const newCount = PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType);
8733 size_t const oldCount = mNetworkAdapters.size();
8734 if (newCount > oldCount)
8735 {
8736 mNetworkAdapters.resize(newCount);
8737 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8738 {
8739 unconst(mNetworkAdapters[slot]).createObject();
8740 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8741 }
8742 }
8743 else if (newCount < oldCount)
8744 mNetworkAdapters.resize(newCount);
8745 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8746 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8747 for (settings::NetworkAdaptersList::const_iterator
8748 it = data.llNetworkAdapters.begin();
8749 it != data.llNetworkAdapters.end();
8750 ++it)
8751 {
8752 const settings::NetworkAdapter &nic = *it;
8753
8754 /* slot uniqueness is guaranteed by XML Schema */
8755 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8756 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8757 if (FAILED(hrc)) return hrc;
8758 }
8759
8760 // serial ports (establish defaults first, to ensure reading the same
8761 // settings as we saved, since the list skips ports having defaults)
8762 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8763 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8764 for (settings::SerialPortsList::const_iterator
8765 it = data.llSerialPorts.begin();
8766 it != data.llSerialPorts.end();
8767 ++it)
8768 {
8769 const settings::SerialPort &s = *it;
8770
8771 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8772 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8773 if (FAILED(hrc)) return hrc;
8774 }
8775
8776 // parallel ports (establish defaults first, to ensure reading the same
8777 // settings as we saved, since the list skips ports having defaults)
8778 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8779 mParallelPorts[i]->i_applyDefaults();
8780 for (settings::ParallelPortsList::const_iterator
8781 it = data.llParallelPorts.begin();
8782 it != data.llParallelPorts.end();
8783 ++it)
8784 {
8785 const settings::ParallelPort &p = *it;
8786
8787 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8788 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8789 if (FAILED(hrc)) return hrc;
8790 }
8791
8792 /* Audio settings */
8793 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
8794 if (FAILED(hrc)) return hrc;
8795
8796 /* storage controllers */
8797 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
8798 if (FAILED(hrc)) return hrc;
8799
8800 /* Shared folders */
8801 for (settings::SharedFoldersList::const_iterator
8802 it = data.llSharedFolders.begin();
8803 it != data.llSharedFolders.end();
8804 ++it)
8805 {
8806 const settings::SharedFolder &sf = *it;
8807
8808 ComObjPtr<SharedFolder> sharedFolder;
8809 /* Check for double entries. Not allowed! */
8810 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8811 if (SUCCEEDED(hrc))
8812 return setError(VBOX_E_OBJECT_IN_USE,
8813 tr("Shared folder named '%s' already exists"),
8814 sf.strName.c_str());
8815
8816 /* Create the new shared folder. Don't break on error. This will be
8817 * reported when the machine starts. */
8818 sharedFolder.createObject();
8819 hrc = sharedFolder->init(i_getMachine(),
8820 sf.strName,
8821 sf.strHostPath,
8822 RT_BOOL(sf.fWritable),
8823 RT_BOOL(sf.fAutoMount),
8824 sf.strAutoMountPoint,
8825 false /* fFailOnError */,
8826 sf.enmSymlinkPolicy);
8827 if (FAILED(hrc)) return hrc;
8828 mHWData->mSharedFolders.push_back(sharedFolder);
8829 }
8830
8831 // Clipboard
8832 mHWData->mClipboardMode = data.clipboardMode;
8833 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8834
8835 // drag'n'drop
8836 mHWData->mDnDMode = data.dndMode;
8837
8838 // guest settings
8839 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8840
8841 // IO settings
8842 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8843 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8844
8845 // Host PCI devices
8846 for (settings::HostPCIDeviceAttachmentList::const_iterator
8847 it = data.pciAttachments.begin();
8848 it != data.pciAttachments.end();
8849 ++it)
8850 {
8851 const settings::HostPCIDeviceAttachment &hpda = *it;
8852 ComObjPtr<PCIDeviceAttachment> pda;
8853
8854 pda.createObject();
8855 pda->i_loadSettings(this, hpda);
8856 mHWData->mPCIDeviceAssignments.push_back(pda);
8857 }
8858
8859 /*
8860 * (The following isn't really real hardware, but it lives in HWData
8861 * for reasons of convenience.)
8862 */
8863
8864#ifdef VBOX_WITH_GUEST_PROPS
8865 /* Guest properties (optional) */
8866
8867 /* Only load transient guest properties for configs which have saved
8868 * state, because there shouldn't be any for powered off VMs. The same
8869 * logic applies for snapshots, as offline snapshots shouldn't have
8870 * any such properties. They confuse the code in various places.
8871 * Note: can't rely on the machine state, as it isn't set yet. */
8872 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8873 /* apologies for the hacky unconst() usage, but this needs hacking
8874 * actually inconsistent settings into consistency, otherwise there
8875 * will be some corner cases where the inconsistency survives
8876 * surprisingly long without getting fixed, especially for snapshots
8877 * as there are no config changes. */
8878 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8879 for (settings::GuestPropertiesList::iterator
8880 it = llGuestProperties.begin();
8881 it != llGuestProperties.end();
8882 /*nothing*/)
8883 {
8884 const settings::GuestProperty &prop = *it;
8885 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8886 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8887 if ( fSkipTransientGuestProperties
8888 && ( fFlags & GUEST_PROP_F_TRANSIENT
8889 || fFlags & GUEST_PROP_F_TRANSRESET))
8890 {
8891 it = llGuestProperties.erase(it);
8892 continue;
8893 }
8894 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8895 mHWData->mGuestProperties[prop.strName] = property;
8896 ++it;
8897 }
8898#endif /* VBOX_WITH_GUEST_PROPS defined */
8899
8900 hrc = i_loadDebugging(pDbg);
8901 if (FAILED(hrc))
8902 return hrc;
8903
8904 mHWData->mAutostart = *pAutostart;
8905
8906 /* default frontend */
8907 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8908 }
8909 catch (std::bad_alloc &)
8910 {
8911 return E_OUTOFMEMORY;
8912 }
8913
8914 AssertComRC(hrc);
8915 return hrc;
8916}
8917
8918/**
8919 * Called from i_loadHardware() to load the debugging settings of the
8920 * machine.
8921 *
8922 * @param pDbg Pointer to the settings.
8923 */
8924HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8925{
8926 mHWData->mDebugging = *pDbg;
8927 /* no more processing currently required, this will probably change. */
8928
8929 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
8930 if (FAILED(hrc)) return hrc;
8931
8932 return S_OK;
8933}
8934
8935/**
8936 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8937 *
8938 * @param data storage settings.
8939 * @param puuidRegistry media registry ID to set media to or NULL;
8940 * see Machine::i_loadMachineDataFromSettings()
8941 * @param puuidSnapshot snapshot ID
8942 * @return
8943 */
8944HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8945 const Guid *puuidRegistry,
8946 const Guid *puuidSnapshot)
8947{
8948 AssertReturn(!i_isSessionMachine(), E_FAIL);
8949
8950 HRESULT hrc = S_OK;
8951
8952 for (settings::StorageControllersList::const_iterator
8953 it = data.llStorageControllers.begin();
8954 it != data.llStorageControllers.end();
8955 ++it)
8956 {
8957 const settings::StorageController &ctlData = *it;
8958
8959 ComObjPtr<StorageController> pCtl;
8960 /* Try to find one with the name first. */
8961 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8962 if (SUCCEEDED(hrc))
8963 return setError(VBOX_E_OBJECT_IN_USE,
8964 tr("Storage controller named '%s' already exists"),
8965 ctlData.strName.c_str());
8966
8967 pCtl.createObject();
8968 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
8969 if (FAILED(hrc)) return hrc;
8970
8971 mStorageControllers->push_back(pCtl);
8972
8973 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8974 if (FAILED(hrc)) return hrc;
8975
8976 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8977 if (FAILED(hrc)) return hrc;
8978
8979 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8980 if (FAILED(hrc)) return hrc;
8981
8982 /* Load the attached devices now. */
8983 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
8984 if (FAILED(hrc)) return hrc;
8985 }
8986
8987 return S_OK;
8988}
8989
8990/**
8991 * Called from i_loadStorageControllers for a controller's devices.
8992 *
8993 * @param aStorageController
8994 * @param data
8995 * @param puuidRegistry media registry ID to set media to or NULL; see
8996 * Machine::i_loadMachineDataFromSettings()
8997 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
8998 * @return
8999 */
9000HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9001 const settings::StorageController &data,
9002 const Guid *puuidRegistry,
9003 const Guid *puuidSnapshot)
9004{
9005 HRESULT hrc = S_OK;
9006
9007 /* paranoia: detect duplicate attachments */
9008 for (settings::AttachedDevicesList::const_iterator
9009 it = data.llAttachedDevices.begin();
9010 it != data.llAttachedDevices.end();
9011 ++it)
9012 {
9013 const settings::AttachedDevice &ad = *it;
9014
9015 for (settings::AttachedDevicesList::const_iterator it2 = it;
9016 it2 != data.llAttachedDevices.end();
9017 ++it2)
9018 {
9019 if (it == it2)
9020 continue;
9021
9022 const settings::AttachedDevice &ad2 = *it2;
9023
9024 if ( ad.lPort == ad2.lPort
9025 && ad.lDevice == ad2.lDevice)
9026 {
9027 return setError(E_FAIL,
9028 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9029 aStorageController->i_getName().c_str(),
9030 ad.lPort,
9031 ad.lDevice,
9032 mUserData->s.strName.c_str());
9033 }
9034 }
9035 }
9036
9037 for (settings::AttachedDevicesList::const_iterator
9038 it = data.llAttachedDevices.begin();
9039 it != data.llAttachedDevices.end();
9040 ++it)
9041 {
9042 const settings::AttachedDevice &dev = *it;
9043 ComObjPtr<Medium> medium;
9044
9045 switch (dev.deviceType)
9046 {
9047 case DeviceType_Floppy:
9048 case DeviceType_DVD:
9049 if (dev.strHostDriveSrc.isNotEmpty())
9050 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9051 false /* fRefresh */, medium);
9052 else
9053 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9054 dev.uuid,
9055 false /* fRefresh */,
9056 false /* aSetError */,
9057 medium);
9058 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9059 // This is not an error. The host drive or UUID might have vanished, so just go
9060 // ahead without this removeable medium attachment
9061 hrc = S_OK;
9062 break;
9063
9064 case DeviceType_HardDisk:
9065 {
9066 /* find a hard disk by UUID */
9067 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9068 if (FAILED(hrc))
9069 {
9070 if (i_isSnapshotMachine())
9071 {
9072 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9073 // so the user knows that the bad disk is in a snapshot somewhere
9074 com::ErrorInfo info;
9075 return setError(E_FAIL,
9076 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9077 puuidSnapshot->raw(),
9078 info.getText().raw());
9079 }
9080 return hrc;
9081 }
9082
9083 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9084
9085 if (medium->i_getType() == MediumType_Immutable)
9086 {
9087 if (i_isSnapshotMachine())
9088 return setError(E_FAIL,
9089 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9090 "of the virtual machine '%s' ('%s')"),
9091 medium->i_getLocationFull().c_str(),
9092 dev.uuid.raw(),
9093 puuidSnapshot->raw(),
9094 mUserData->s.strName.c_str(),
9095 mData->m_strConfigFileFull.c_str());
9096
9097 return setError(E_FAIL,
9098 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9099 medium->i_getLocationFull().c_str(),
9100 dev.uuid.raw(),
9101 mUserData->s.strName.c_str(),
9102 mData->m_strConfigFileFull.c_str());
9103 }
9104
9105 if (medium->i_getType() == MediumType_MultiAttach)
9106 {
9107 if (i_isSnapshotMachine())
9108 return setError(E_FAIL,
9109 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9110 "of the virtual machine '%s' ('%s')"),
9111 medium->i_getLocationFull().c_str(),
9112 dev.uuid.raw(),
9113 puuidSnapshot->raw(),
9114 mUserData->s.strName.c_str(),
9115 mData->m_strConfigFileFull.c_str());
9116
9117 return setError(E_FAIL,
9118 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9119 medium->i_getLocationFull().c_str(),
9120 dev.uuid.raw(),
9121 mUserData->s.strName.c_str(),
9122 mData->m_strConfigFileFull.c_str());
9123 }
9124
9125 if ( !i_isSnapshotMachine()
9126 && medium->i_getChildren().size() != 0
9127 )
9128 return setError(E_FAIL,
9129 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9130 "because it has %d differencing child hard disks"),
9131 medium->i_getLocationFull().c_str(),
9132 dev.uuid.raw(),
9133 mUserData->s.strName.c_str(),
9134 mData->m_strConfigFileFull.c_str(),
9135 medium->i_getChildren().size());
9136
9137 if (i_findAttachment(*mMediumAttachments.data(),
9138 medium))
9139 return setError(E_FAIL,
9140 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9141 medium->i_getLocationFull().c_str(),
9142 dev.uuid.raw(),
9143 mUserData->s.strName.c_str(),
9144 mData->m_strConfigFileFull.c_str());
9145
9146 break;
9147 }
9148
9149 default:
9150 return setError(E_FAIL,
9151 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9152 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9153 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9154 }
9155
9156 if (FAILED(hrc))
9157 break;
9158
9159 /* Bandwidth groups are loaded at this point. */
9160 ComObjPtr<BandwidthGroup> pBwGroup;
9161
9162 if (!dev.strBwGroup.isEmpty())
9163 {
9164 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9165 if (FAILED(hrc))
9166 return setError(E_FAIL,
9167 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9168 medium->i_getLocationFull().c_str(),
9169 dev.strBwGroup.c_str(),
9170 mUserData->s.strName.c_str(),
9171 mData->m_strConfigFileFull.c_str());
9172 pBwGroup->i_reference();
9173 }
9174
9175 const Utf8Str controllerName = aStorageController->i_getName();
9176 ComObjPtr<MediumAttachment> pAttachment;
9177 pAttachment.createObject();
9178 hrc = pAttachment->init(this,
9179 medium,
9180 controllerName,
9181 dev.lPort,
9182 dev.lDevice,
9183 dev.deviceType,
9184 false,
9185 dev.fPassThrough,
9186 dev.fTempEject,
9187 dev.fNonRotational,
9188 dev.fDiscard,
9189 dev.fHotPluggable,
9190 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9191 if (FAILED(hrc)) break;
9192
9193 /* associate the medium with this machine and snapshot */
9194 if (!medium.isNull())
9195 {
9196 AutoCaller medCaller(medium);
9197 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9198 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9199
9200 if (i_isSnapshotMachine())
9201 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9202 else
9203 hrc = medium->i_addBackReference(mData->mUuid);
9204 /* If the medium->addBackReference fails it sets an appropriate
9205 * error message, so no need to do any guesswork here. */
9206
9207 if (puuidRegistry)
9208 // caller wants registry ID to be set on all attached media (OVF import case)
9209 medium->i_addRegistry(*puuidRegistry);
9210 }
9211
9212 if (FAILED(hrc))
9213 break;
9214
9215 /* back up mMediumAttachments to let registeredInit() properly rollback
9216 * on failure (= limited accessibility) */
9217 i_setModified(IsModified_Storage);
9218 mMediumAttachments.backup();
9219 mMediumAttachments->push_back(pAttachment);
9220 }
9221
9222 return hrc;
9223}
9224
9225/**
9226 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9227 *
9228 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9229 * @param aSnapshot where to return the found snapshot
9230 * @param aSetError true to set extended error info on failure
9231 */
9232HRESULT Machine::i_findSnapshotById(const Guid &aId,
9233 ComObjPtr<Snapshot> &aSnapshot,
9234 bool aSetError /* = false */)
9235{
9236 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9237
9238 if (!mData->mFirstSnapshot)
9239 {
9240 if (aSetError)
9241 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9242 return E_FAIL;
9243 }
9244
9245 if (aId.isZero())
9246 aSnapshot = mData->mFirstSnapshot;
9247 else
9248 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9249
9250 if (!aSnapshot)
9251 {
9252 if (aSetError)
9253 return setError(E_FAIL,
9254 tr("Could not find a snapshot with UUID {%s}"),
9255 aId.toString().c_str());
9256 return E_FAIL;
9257 }
9258
9259 return S_OK;
9260}
9261
9262/**
9263 * Returns the snapshot with the given name or fails of no such snapshot.
9264 *
9265 * @param strName snapshot name to find
9266 * @param aSnapshot where to return the found snapshot
9267 * @param aSetError true to set extended error info on failure
9268 */
9269HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9270 ComObjPtr<Snapshot> &aSnapshot,
9271 bool aSetError /* = false */)
9272{
9273 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9274
9275 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9276
9277 if (!mData->mFirstSnapshot)
9278 {
9279 if (aSetError)
9280 return setError(VBOX_E_OBJECT_NOT_FOUND,
9281 tr("This machine does not have any snapshots"));
9282 return VBOX_E_OBJECT_NOT_FOUND;
9283 }
9284
9285 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9286
9287 if (!aSnapshot)
9288 {
9289 if (aSetError)
9290 return setError(VBOX_E_OBJECT_NOT_FOUND,
9291 tr("Could not find a snapshot named '%s'"), strName.c_str());
9292 return VBOX_E_OBJECT_NOT_FOUND;
9293 }
9294
9295 return S_OK;
9296}
9297
9298/**
9299 * Returns a storage controller object with the given name.
9300 *
9301 * @param aName storage controller name to find
9302 * @param aStorageController where to return the found storage controller
9303 * @param aSetError true to set extended error info on failure
9304 */
9305HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9306 ComObjPtr<StorageController> &aStorageController,
9307 bool aSetError /* = false */)
9308{
9309 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9310
9311 for (StorageControllerList::const_iterator
9312 it = mStorageControllers->begin();
9313 it != mStorageControllers->end();
9314 ++it)
9315 {
9316 if ((*it)->i_getName() == aName)
9317 {
9318 aStorageController = (*it);
9319 return S_OK;
9320 }
9321 }
9322
9323 if (aSetError)
9324 return setError(VBOX_E_OBJECT_NOT_FOUND,
9325 tr("Could not find a storage controller named '%s'"),
9326 aName.c_str());
9327 return VBOX_E_OBJECT_NOT_FOUND;
9328}
9329
9330/**
9331 * Returns a USB controller object with the given name.
9332 *
9333 * @param aName USB controller name to find
9334 * @param aUSBController where to return the found USB controller
9335 * @param aSetError true to set extended error info on failure
9336 */
9337HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9338 ComObjPtr<USBController> &aUSBController,
9339 bool aSetError /* = false */)
9340{
9341 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9342
9343 for (USBControllerList::const_iterator
9344 it = mUSBControllers->begin();
9345 it != mUSBControllers->end();
9346 ++it)
9347 {
9348 if ((*it)->i_getName() == aName)
9349 {
9350 aUSBController = (*it);
9351 return S_OK;
9352 }
9353 }
9354
9355 if (aSetError)
9356 return setError(VBOX_E_OBJECT_NOT_FOUND,
9357 tr("Could not find a storage controller named '%s'"),
9358 aName.c_str());
9359 return VBOX_E_OBJECT_NOT_FOUND;
9360}
9361
9362/**
9363 * Returns the number of USB controller instance of the given type.
9364 *
9365 * @param enmType USB controller type.
9366 */
9367ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9368{
9369 ULONG cCtrls = 0;
9370
9371 for (USBControllerList::const_iterator
9372 it = mUSBControllers->begin();
9373 it != mUSBControllers->end();
9374 ++it)
9375 {
9376 if ((*it)->i_getControllerType() == enmType)
9377 cCtrls++;
9378 }
9379
9380 return cCtrls;
9381}
9382
9383HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9384 MediumAttachmentList &atts)
9385{
9386 AutoCaller autoCaller(this);
9387 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
9388
9389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9390
9391 for (MediumAttachmentList::const_iterator
9392 it = mMediumAttachments->begin();
9393 it != mMediumAttachments->end();
9394 ++it)
9395 {
9396 const ComObjPtr<MediumAttachment> &pAtt = *it;
9397 // should never happen, but deal with NULL pointers in the list.
9398 AssertContinue(!pAtt.isNull());
9399
9400 // getControllerName() needs caller+read lock
9401 AutoCaller autoAttCaller(pAtt);
9402 if (FAILED(autoAttCaller.hrc()))
9403 {
9404 atts.clear();
9405 return autoAttCaller.hrc();
9406 }
9407 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9408
9409 if (pAtt->i_getControllerName() == aName)
9410 atts.push_back(pAtt);
9411 }
9412
9413 return S_OK;
9414}
9415
9416
9417/**
9418 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9419 * file if the machine name was changed and about creating a new settings file
9420 * if this is a new machine.
9421 *
9422 * @note Must be never called directly but only from #saveSettings().
9423 */
9424HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9425 bool *pfSettingsFileIsNew)
9426{
9427 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9428
9429 HRESULT hrc = S_OK;
9430
9431 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9432 /// @todo need to handle primary group change, too
9433
9434 /* attempt to rename the settings file if machine name is changed */
9435 if ( mUserData->s.fNameSync
9436 && mUserData.isBackedUp()
9437 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9438 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9439 )
9440 {
9441 bool dirRenamed = false;
9442 bool fileRenamed = false;
9443
9444 Utf8Str configFile, newConfigFile;
9445 Utf8Str configFilePrev, newConfigFilePrev;
9446 Utf8Str NVRAMFile, newNVRAMFile;
9447 Utf8Str configDir, newConfigDir;
9448
9449 do
9450 {
9451 int vrc = VINF_SUCCESS;
9452
9453 Utf8Str name = mUserData.backedUpData()->s.strName;
9454 Utf8Str newName = mUserData->s.strName;
9455 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9456 if (group == "/")
9457 group.setNull();
9458 Utf8Str newGroup = mUserData->s.llGroups.front();
9459 if (newGroup == "/")
9460 newGroup.setNull();
9461
9462 configFile = mData->m_strConfigFileFull;
9463
9464 /* first, rename the directory if it matches the group and machine name */
9465 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9466 /** @todo hack, make somehow use of ComposeMachineFilename */
9467 if (mUserData->s.fDirectoryIncludesUUID)
9468 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9469 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9470 /** @todo hack, make somehow use of ComposeMachineFilename */
9471 if (mUserData->s.fDirectoryIncludesUUID)
9472 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9473 configDir = configFile;
9474 configDir.stripFilename();
9475 newConfigDir = configDir;
9476 if ( configDir.length() >= groupPlusName.length()
9477 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9478 groupPlusName.c_str()))
9479 {
9480 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9481 Utf8Str newConfigBaseDir(newConfigDir);
9482 newConfigDir.append(newGroupPlusName);
9483 /* consistency: use \ if appropriate on the platform */
9484 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9485 /* new dir and old dir cannot be equal here because of 'if'
9486 * above and because name != newName */
9487 Assert(configDir != newConfigDir);
9488 if (!fSettingsFileIsNew)
9489 {
9490 /* perform real rename only if the machine is not new */
9491 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9492 if ( vrc == VERR_FILE_NOT_FOUND
9493 || vrc == VERR_PATH_NOT_FOUND)
9494 {
9495 /* create the parent directory, then retry renaming */
9496 Utf8Str parent(newConfigDir);
9497 parent.stripFilename();
9498 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9499 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9500 }
9501 if (RT_FAILURE(vrc))
9502 {
9503 hrc = setErrorBoth(E_FAIL, vrc,
9504 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9505 configDir.c_str(),
9506 newConfigDir.c_str(),
9507 vrc);
9508 break;
9509 }
9510 /* delete subdirectories which are no longer needed */
9511 Utf8Str dir(configDir);
9512 dir.stripFilename();
9513 while (dir != newConfigBaseDir && dir != ".")
9514 {
9515 vrc = RTDirRemove(dir.c_str());
9516 if (RT_FAILURE(vrc))
9517 break;
9518 dir.stripFilename();
9519 }
9520 dirRenamed = true;
9521 }
9522 }
9523
9524 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9525
9526 /* then try to rename the settings file itself */
9527 if (newConfigFile != configFile)
9528 {
9529 /* get the path to old settings file in renamed directory */
9530 Assert(mData->m_strConfigFileFull == configFile);
9531 configFile.printf("%s%c%s",
9532 newConfigDir.c_str(),
9533 RTPATH_DELIMITER,
9534 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9535 if (!fSettingsFileIsNew)
9536 {
9537 /* perform real rename only if the machine is not new */
9538 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9539 if (RT_FAILURE(vrc))
9540 {
9541 hrc = setErrorBoth(E_FAIL, vrc,
9542 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9543 configFile.c_str(),
9544 newConfigFile.c_str(),
9545 vrc);
9546 break;
9547 }
9548 fileRenamed = true;
9549 configFilePrev = configFile;
9550 configFilePrev += "-prev";
9551 newConfigFilePrev = newConfigFile;
9552 newConfigFilePrev += "-prev";
9553 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9554 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9555 if (NVRAMFile.isNotEmpty())
9556 {
9557 // in the NVRAM file path, replace the old directory with the new directory
9558 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9559 {
9560 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9561 NVRAMFile = newConfigDir + strNVRAMFile;
9562 }
9563 newNVRAMFile = newConfigFile;
9564 newNVRAMFile.stripSuffix();
9565 newNVRAMFile += ".nvram";
9566 RTPathRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9567 }
9568 }
9569 }
9570
9571 // update m_strConfigFileFull amd mConfigFile
9572 mData->m_strConfigFileFull = newConfigFile;
9573 // compute the relative path too
9574 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9575
9576 // store the old and new so that VirtualBox::i_saveSettings() can update
9577 // the media registry
9578 if ( mData->mRegistered
9579 && (configDir != newConfigDir || configFile != newConfigFile))
9580 {
9581 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9582
9583 if (pfNeedsGlobalSaveSettings)
9584 *pfNeedsGlobalSaveSettings = true;
9585 }
9586
9587 // in the saved state file path, replace the old directory with the new directory
9588 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9589 {
9590 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9591 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9592 }
9593 if (newNVRAMFile.isNotEmpty())
9594 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9595
9596 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9597 if (mData->mFirstSnapshot)
9598 {
9599 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9600 newConfigDir.c_str());
9601 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9602 newConfigDir.c_str());
9603 }
9604 }
9605 while (0);
9606
9607 if (FAILED(hrc))
9608 {
9609 /* silently try to rename everything back */
9610 if (fileRenamed)
9611 {
9612 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9613 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9614 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9615 RTPathRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9616 }
9617 if (dirRenamed)
9618 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9619 }
9620
9621 if (FAILED(hrc)) return hrc;
9622 }
9623
9624 if (fSettingsFileIsNew)
9625 {
9626 /* create a virgin config file */
9627 int vrc = VINF_SUCCESS;
9628
9629 /* ensure the settings directory exists */
9630 Utf8Str path(mData->m_strConfigFileFull);
9631 path.stripFilename();
9632 if (!RTDirExists(path.c_str()))
9633 {
9634 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9635 if (RT_FAILURE(vrc))
9636 {
9637 return setErrorBoth(E_FAIL, vrc,
9638 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9639 path.c_str(),
9640 vrc);
9641 }
9642 }
9643
9644 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9645 path = mData->m_strConfigFileFull;
9646 RTFILE f = NIL_RTFILE;
9647 vrc = RTFileOpen(&f, path.c_str(),
9648 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9649 if (RT_FAILURE(vrc))
9650 return setErrorBoth(E_FAIL, vrc,
9651 tr("Could not create the settings file '%s' (%Rrc)"),
9652 path.c_str(),
9653 vrc);
9654 RTFileClose(f);
9655 }
9656 if (pfSettingsFileIsNew)
9657 *pfSettingsFileIsNew = fSettingsFileIsNew;
9658
9659 return hrc;
9660}
9661
9662/**
9663 * Saves and commits machine data, user data and hardware data.
9664 *
9665 * Note that on failure, the data remains uncommitted.
9666 *
9667 * @a aFlags may combine the following flags:
9668 *
9669 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9670 * Used when saving settings after an operation that makes them 100%
9671 * correspond to the settings from the current snapshot.
9672 * - SaveS_Force: settings will be saved without doing a deep compare of the
9673 * settings structures. This is used when this is called because snapshots
9674 * have changed to avoid the overhead of the deep compare.
9675 *
9676 * @note Must be called from under this object's write lock. Locks children for
9677 * writing.
9678 *
9679 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9680 * initialized to false and that will be set to true by this function if
9681 * the caller must invoke VirtualBox::i_saveSettings() because the global
9682 * settings have changed. This will happen if a machine rename has been
9683 * saved and the global machine and media registries will therefore need
9684 * updating.
9685 * @param alock Reference to the lock for this machine object.
9686 * @param aFlags Flags.
9687 */
9688HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9689 AutoWriteLock &alock,
9690 int aFlags /*= 0*/)
9691{
9692 LogFlowThisFuncEnter();
9693
9694 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9695
9696 /* make sure child objects are unable to modify the settings while we are
9697 * saving them */
9698 i_ensureNoStateDependencies(alock);
9699
9700 AssertReturn(!i_isSnapshotMachine(),
9701 E_FAIL);
9702
9703 if (!mData->mAccessible)
9704 return setError(VBOX_E_INVALID_VM_STATE,
9705 tr("The machine is not accessible, so cannot save settings"));
9706
9707 HRESULT hrc = S_OK;
9708 PCVBOXCRYPTOIF pCryptoIf = NULL;
9709 const char *pszPassword = NULL;
9710 SecretKey *pKey = NULL;
9711
9712#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9713 if (mData->mstrKeyId.isNotEmpty())
9714 {
9715 /* VM is going to be encrypted. */
9716 alock.release(); /** @todo Revise the locking. */
9717 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
9718 alock.acquire();
9719 if (FAILED(hrc)) return hrc; /* Error is set. */
9720
9721 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
9722 if (RT_SUCCESS(vrc))
9723 pszPassword = (const char *)pKey->getKeyBuffer();
9724 else
9725 {
9726 mParent->i_releaseCryptoIf(pCryptoIf);
9727 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
9728 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
9729 mData->mstrKeyId.c_str(), vrc);
9730 }
9731 }
9732#else
9733 RT_NOREF(pKey);
9734#endif
9735
9736 bool fNeedsWrite = false;
9737 bool fSettingsFileIsNew = false;
9738
9739 /* First, prepare to save settings. It will care about renaming the
9740 * settings directory and file if the machine name was changed and about
9741 * creating a new settings file if this is a new machine. */
9742 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
9743 if (FAILED(hrc))
9744 {
9745#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9746 if (pCryptoIf)
9747 {
9748 alock.release(); /** @todo Revise the locking. */
9749 mParent->i_releaseCryptoIf(pCryptoIf);
9750 alock.acquire();
9751 }
9752 if (pKey)
9753 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9754#endif
9755 return hrc;
9756 }
9757
9758 // keep a pointer to the current settings structures
9759 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9760 settings::MachineConfigFile *pNewConfig = NULL;
9761
9762 try
9763 {
9764 // make a fresh one to have everyone write stuff into
9765 pNewConfig = new settings::MachineConfigFile(NULL);
9766 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9767#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9768 pNewConfig->strKeyId = mData->mstrKeyId;
9769 pNewConfig->strKeyStore = mData->mstrKeyStore;
9770#endif
9771
9772 // now go and copy all the settings data from COM to the settings structures
9773 // (this calls i_saveSettings() on all the COM objects in the machine)
9774 i_copyMachineDataToSettings(*pNewConfig);
9775
9776 if (aFlags & SaveS_ResetCurStateModified)
9777 {
9778 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9779 mData->mCurrentStateModified = FALSE;
9780 fNeedsWrite = true; // always, no need to compare
9781 }
9782 else if (aFlags & SaveS_Force)
9783 {
9784 fNeedsWrite = true; // always, no need to compare
9785 }
9786 else
9787 {
9788 if (!mData->mCurrentStateModified)
9789 {
9790 // do a deep compare of the settings that we just saved with the settings
9791 // previously stored in the config file; this invokes MachineConfigFile::operator==
9792 // which does a deep compare of all the settings, which is expensive but less expensive
9793 // than writing out XML in vain
9794 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9795
9796 // could still be modified if any settings changed
9797 mData->mCurrentStateModified = fAnySettingsChanged;
9798
9799 fNeedsWrite = fAnySettingsChanged;
9800 }
9801 else
9802 fNeedsWrite = true;
9803 }
9804
9805 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9806
9807 if (fNeedsWrite)
9808 {
9809 // now spit it all out!
9810 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
9811 if (aFlags & SaveS_RemoveBackup)
9812 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
9813 }
9814
9815 mData->pMachineConfigFile = pNewConfig;
9816 delete pOldConfig;
9817 i_commit();
9818
9819 // after saving settings, we are no longer different from the XML on disk
9820 mData->flModifications = 0;
9821 }
9822 catch (HRESULT err)
9823 {
9824 // we assume that error info is set by the thrower
9825 hrc = err;
9826
9827 // delete any newly created settings file
9828 if (fSettingsFileIsNew)
9829 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
9830
9831 // restore old config
9832 delete pNewConfig;
9833 mData->pMachineConfigFile = pOldConfig;
9834 }
9835 catch (...)
9836 {
9837 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9838 }
9839
9840#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9841 if (pCryptoIf)
9842 {
9843 alock.release(); /** @todo Revise the locking. */
9844 mParent->i_releaseCryptoIf(pCryptoIf);
9845 alock.acquire();
9846 }
9847 if (pKey)
9848 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9849#endif
9850
9851 if (fNeedsWrite)
9852 {
9853 /* Fire the data change event, even on failure (since we've already
9854 * committed all data). This is done only for SessionMachines because
9855 * mutable Machine instances are always not registered (i.e. private
9856 * to the client process that creates them) and thus don't need to
9857 * inform callbacks. */
9858 if (i_isSessionMachine())
9859 mParent->i_onMachineDataChanged(mData->mUuid);
9860 }
9861
9862 LogFlowThisFunc(("hrc=%08X\n", hrc));
9863 LogFlowThisFuncLeave();
9864 return hrc;
9865}
9866
9867/**
9868 * Implementation for saving the machine settings into the given
9869 * settings::MachineConfigFile instance. This copies machine extradata
9870 * from the previous machine config file in the instance data, if any.
9871 *
9872 * This gets called from two locations:
9873 *
9874 * -- Machine::i_saveSettings(), during the regular XML writing;
9875 *
9876 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9877 * exported to OVF and we write the VirtualBox proprietary XML
9878 * into a <vbox:Machine> tag.
9879 *
9880 * This routine fills all the fields in there, including snapshots, *except*
9881 * for the following:
9882 *
9883 * -- fCurrentStateModified. There is some special logic associated with that.
9884 *
9885 * The caller can then call MachineConfigFile::write() or do something else
9886 * with it.
9887 *
9888 * Caller must hold the machine lock!
9889 *
9890 * This throws XML errors and HRESULT, so the caller must have a catch block!
9891 */
9892void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9893{
9894 // deep copy extradata, being extra careful with self assignment (the STL
9895 // map assignment on Mac OS X clang based Xcode isn't checking)
9896 if (&config != mData->pMachineConfigFile)
9897 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9898
9899 config.uuid = mData->mUuid;
9900
9901 // copy name, description, OS type, teleport, UTC etc.
9902 config.machineUserData = mUserData->s;
9903
9904#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9905 config.strStateKeyId = mSSData->strStateKeyId;
9906 config.strStateKeyStore = mSSData->strStateKeyStore;
9907 config.strLogKeyId = mData->mstrLogKeyId;
9908 config.strLogKeyStore = mData->mstrLogKeyStore;
9909#endif
9910
9911 if ( mData->mMachineState == MachineState_Saved
9912 || mData->mMachineState == MachineState_AbortedSaved
9913 || mData->mMachineState == MachineState_Restoring
9914 // when doing certain snapshot operations we may or may not have
9915 // a saved state in the current state, so keep everything as is
9916 || ( ( mData->mMachineState == MachineState_Snapshotting
9917 || mData->mMachineState == MachineState_DeletingSnapshot
9918 || mData->mMachineState == MachineState_RestoringSnapshot)
9919 && (!mSSData->strStateFilePath.isEmpty())
9920 )
9921 )
9922 {
9923 Assert(!mSSData->strStateFilePath.isEmpty());
9924 /* try to make the file name relative to the settings file dir */
9925 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9926 }
9927 else
9928 {
9929 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9930 config.strStateFile.setNull();
9931 }
9932
9933 if (mData->mCurrentSnapshot)
9934 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9935 else
9936 config.uuidCurrentSnapshot.clear();
9937
9938 config.timeLastStateChange = mData->mLastStateChange;
9939 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
9940 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9941
9942 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
9943 if (FAILED(hrc)) throw hrc;
9944
9945 // save machine's media registry if this is VirtualBox 4.0 or later
9946 if (config.canHaveOwnMediaRegistry())
9947 {
9948 // determine machine folder
9949 Utf8Str strMachineFolder = i_getSettingsFileFull();
9950 strMachineFolder.stripFilename();
9951 mParent->i_saveMediaRegistry(config.mediaRegistry,
9952 i_getId(), // only media with registry ID == machine UUID
9953 strMachineFolder);
9954 // this throws HRESULT
9955 }
9956
9957 // save snapshots
9958 hrc = i_saveAllSnapshots(config);
9959 if (FAILED(hrc)) throw hrc;
9960}
9961
9962/**
9963 * Saves all snapshots of the machine into the given machine config file. Called
9964 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9965 * @param config
9966 * @return
9967 */
9968HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9969{
9970 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9971
9972 HRESULT hrc = S_OK;
9973
9974 try
9975 {
9976 config.llFirstSnapshot.clear();
9977
9978 if (mData->mFirstSnapshot)
9979 {
9980 // the settings use a list for "the first snapshot"
9981 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9982
9983 // get reference to the snapshot on the list and work on that
9984 // element straight in the list to avoid excessive copying later
9985 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9986 if (FAILED(hrc)) throw hrc;
9987 }
9988
9989// if (mType == IsSessionMachine)
9990// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9991
9992 }
9993 catch (HRESULT err)
9994 {
9995 /* we assume that error info is set by the thrower */
9996 hrc = err;
9997 }
9998 catch (...)
9999 {
10000 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10001 }
10002
10003 return hrc;
10004}
10005
10006/**
10007 * Saves the VM hardware configuration. It is assumed that the
10008 * given node is empty.
10009 *
10010 * @param data Reference to the settings object for the hardware config.
10011 * @param pDbg Pointer to the settings object for the debugging config
10012 * which happens to live in mHWData.
10013 * @param pAutostart Pointer to the settings object for the autostart config
10014 * which happens to live in mHWData.
10015 * @param recording Reference to reecording settings.
10016 */
10017HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10018 settings::Autostart *pAutostart, settings::Recording &recording)
10019{
10020 HRESULT hrc = S_OK;
10021
10022 try
10023 {
10024 /* The hardware version attribute (optional).
10025 Automatically upgrade from 1 to current default hardware version
10026 when there is no saved state. (ugly!) */
10027 if ( mHWData->mHWVersion == "1"
10028 && mSSData->strStateFilePath.isEmpty()
10029 )
10030 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10031
10032 data.strVersion = mHWData->mHWVersion;
10033 data.uuid = mHWData->mHardwareUUID;
10034
10035 // CPU
10036 data.cCPUs = mHWData->mCPUCount;
10037 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10038 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10039 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10040 data.strCpuProfile = mHWData->mCpuProfile;
10041
10042 data.llCpus.clear();
10043 if (data.fCpuHotPlug)
10044 {
10045 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10046 {
10047 if (mHWData->mCPUAttached[idx])
10048 {
10049 settings::Cpu cpu;
10050 cpu.ulId = idx;
10051 data.llCpus.push_back(cpu);
10052 }
10053 }
10054 }
10055
10056 // memory
10057 data.ulMemorySizeMB = mHWData->mMemorySize;
10058 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10059
10060 // HID
10061 data.pointingHIDType = mHWData->mPointingHIDType;
10062 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10063
10064 // paravirt
10065 data.paravirtProvider = mHWData->mParavirtProvider;
10066 data.strParavirtDebug = mHWData->mParavirtDebug;
10067
10068 // emulated USB card reader
10069 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10070
10071 // boot order
10072 data.mapBootOrder.clear();
10073 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10074 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10075
10076 /* VRDEServer settings (optional) */
10077 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10078 if (FAILED(hrc)) throw hrc;
10079
10080 /* Platform (required) */
10081 hrc = mPlatform->i_saveSettings(data.platformSettings);
10082 if (FAILED(hrc)) return hrc;
10083
10084 /* Firmware settings (required) */
10085 hrc = mFirmwareSettings->i_saveSettings(data.firmwareSettings);
10086 if (FAILED(hrc)) throw hrc;
10087
10088 /* Recording settings. */
10089 hrc = mRecordingSettings->i_saveSettings(recording);
10090 if (FAILED(hrc)) throw hrc;
10091
10092 /* Trusted Platform Module settings (required) */
10093 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10094 if (FAILED(hrc)) throw hrc;
10095
10096 /* NVRAM settings (required) */
10097 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10098 if (FAILED(hrc)) throw hrc;
10099
10100 /* GraphicsAdapter settings (required) */
10101 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10102 if (FAILED(hrc)) throw hrc;
10103
10104 /* USB Controller (required) */
10105 data.usbSettings.llUSBControllers.clear();
10106 for (USBControllerList::const_iterator
10107 it = mUSBControllers->begin();
10108 it != mUSBControllers->end();
10109 ++it)
10110 {
10111 ComObjPtr<USBController> ctrl = *it;
10112 settings::USBController settingsCtrl;
10113
10114 settingsCtrl.strName = ctrl->i_getName();
10115 settingsCtrl.enmType = ctrl->i_getControllerType();
10116
10117 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10118 }
10119
10120 /* USB device filters (required) */
10121 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10122 if (FAILED(hrc)) throw hrc;
10123
10124 /* Network adapters (required) */
10125 size_t const uMaxNICs =
10126 RT_MIN(PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType), mNetworkAdapters.size());
10127 data.llNetworkAdapters.clear();
10128 /* Write out only the nominal number of network adapters for this
10129 * chipset type. Since Machine::commit() hasn't been called there
10130 * may be extra NIC settings in the vector. */
10131 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10132 {
10133 settings::NetworkAdapter nic;
10134 nic.ulSlot = (uint32_t)slot;
10135 /* paranoia check... must not be NULL, but must not crash either. */
10136 if (mNetworkAdapters[slot])
10137 {
10138 if (mNetworkAdapters[slot]->i_hasDefaults())
10139 continue;
10140
10141 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10142 if (FAILED(hrc)) throw hrc;
10143
10144 data.llNetworkAdapters.push_back(nic);
10145 }
10146 }
10147
10148 /* Serial ports */
10149 data.llSerialPorts.clear();
10150 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10151 {
10152 if (mSerialPorts[slot]->i_hasDefaults())
10153 continue;
10154
10155 settings::SerialPort s;
10156 s.ulSlot = slot;
10157 hrc = mSerialPorts[slot]->i_saveSettings(s);
10158 if (FAILED(hrc)) return hrc;
10159
10160 data.llSerialPorts.push_back(s);
10161 }
10162
10163 /* Parallel ports */
10164 data.llParallelPorts.clear();
10165 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10166 {
10167 if (mParallelPorts[slot]->i_hasDefaults())
10168 continue;
10169
10170 settings::ParallelPort p;
10171 p.ulSlot = slot;
10172 hrc = mParallelPorts[slot]->i_saveSettings(p);
10173 if (FAILED(hrc)) return hrc;
10174
10175 data.llParallelPorts.push_back(p);
10176 }
10177
10178 /* Audio settings */
10179 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10180 if (FAILED(hrc)) return hrc;
10181
10182 hrc = i_saveStorageControllers(data.storage);
10183 if (FAILED(hrc)) return hrc;
10184
10185 /* Shared folders */
10186 data.llSharedFolders.clear();
10187 for (HWData::SharedFolderList::const_iterator
10188 it = mHWData->mSharedFolders.begin();
10189 it != mHWData->mSharedFolders.end();
10190 ++it)
10191 {
10192 SharedFolder *pSF = *it;
10193 AutoCaller sfCaller(pSF);
10194 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10195 settings::SharedFolder sf;
10196 sf.strName = pSF->i_getName();
10197 sf.strHostPath = pSF->i_getHostPath();
10198 sf.fWritable = !!pSF->i_isWritable();
10199 sf.fAutoMount = !!pSF->i_isAutoMounted();
10200 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10201 sf.enmSymlinkPolicy = pSF->i_getSymlinkPolicy();
10202
10203 data.llSharedFolders.push_back(sf);
10204 }
10205
10206 // clipboard
10207 data.clipboardMode = mHWData->mClipboardMode;
10208 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10209
10210 // drag'n'drop
10211 data.dndMode = mHWData->mDnDMode;
10212
10213 /* Guest */
10214 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10215
10216 // IO settings
10217 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10218 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10219
10220 /* BandwidthControl (required) */
10221 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10222 if (FAILED(hrc)) throw hrc;
10223
10224 /* Host PCI devices */
10225 data.pciAttachments.clear();
10226 for (HWData::PCIDeviceAssignmentList::const_iterator
10227 it = mHWData->mPCIDeviceAssignments.begin();
10228 it != mHWData->mPCIDeviceAssignments.end();
10229 ++it)
10230 {
10231 ComObjPtr<PCIDeviceAttachment> pda = *it;
10232 settings::HostPCIDeviceAttachment hpda;
10233
10234 hrc = pda->i_saveSettings(hpda);
10235 if (FAILED(hrc)) throw hrc;
10236
10237 data.pciAttachments.push_back(hpda);
10238 }
10239
10240 // guest properties
10241 data.llGuestProperties.clear();
10242#ifdef VBOX_WITH_GUEST_PROPS
10243 for (HWData::GuestPropertyMap::const_iterator
10244 it = mHWData->mGuestProperties.begin();
10245 it != mHWData->mGuestProperties.end();
10246 ++it)
10247 {
10248 HWData::GuestProperty property = it->second;
10249
10250 /* Remove transient guest properties at shutdown unless we
10251 * are saving state. Note that restoring snapshot intentionally
10252 * keeps them, they will be removed if appropriate once the final
10253 * machine state is set (as crashes etc. need to work). */
10254 if ( ( mData->mMachineState == MachineState_PoweredOff
10255 || mData->mMachineState == MachineState_Aborted
10256 || mData->mMachineState == MachineState_Teleported)
10257 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10258 continue;
10259 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10260 prop.strName = it->first;
10261 prop.strValue = property.strValue;
10262 prop.timestamp = (uint64_t)property.mTimestamp;
10263 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10264 GuestPropWriteFlags(property.mFlags, szFlags);
10265 prop.strFlags = szFlags;
10266
10267 data.llGuestProperties.push_back(prop);
10268 }
10269
10270 /* I presume this doesn't require a backup(). */
10271 mData->mGuestPropertiesModified = FALSE;
10272#endif /* VBOX_WITH_GUEST_PROPS defined */
10273
10274 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10275 if (FAILED(hrc)) throw hrc;
10276
10277 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10278 *pAutostart = mHWData->mAutostart;
10279
10280 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10281 }
10282 catch (std::bad_alloc &)
10283 {
10284 return E_OUTOFMEMORY;
10285 }
10286
10287 AssertComRC(hrc);
10288 return hrc;
10289}
10290
10291/**
10292 * Saves the storage controller configuration.
10293 *
10294 * @param data storage settings.
10295 */
10296HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10297{
10298 data.llStorageControllers.clear();
10299
10300 for (StorageControllerList::const_iterator
10301 it = mStorageControllers->begin();
10302 it != mStorageControllers->end();
10303 ++it)
10304 {
10305 ComObjPtr<StorageController> pCtl = *it;
10306
10307 settings::StorageController ctl;
10308 ctl.strName = pCtl->i_getName();
10309 ctl.controllerType = pCtl->i_getControllerType();
10310 ctl.storageBus = pCtl->i_getStorageBus();
10311 ctl.ulInstance = pCtl->i_getInstance();
10312 ctl.fBootable = pCtl->i_getBootable();
10313
10314 /* Save the port count. */
10315 ULONG portCount;
10316 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10317 ComAssertComRCRet(hrc, hrc);
10318 ctl.ulPortCount = portCount;
10319
10320 /* Save fUseHostIOCache */
10321 BOOL fUseHostIOCache;
10322 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10323 ComAssertComRCRet(hrc, hrc);
10324 ctl.fUseHostIOCache = !!fUseHostIOCache;
10325
10326 /* save the devices now. */
10327 hrc = i_saveStorageDevices(pCtl, ctl);
10328 ComAssertComRCRet(hrc, hrc);
10329
10330 data.llStorageControllers.push_back(ctl);
10331 }
10332
10333 return S_OK;
10334}
10335
10336/**
10337 * Saves the hard disk configuration.
10338 */
10339HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10340 settings::StorageController &data)
10341{
10342 MediumAttachmentList atts;
10343
10344 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10345 if (FAILED(hrc)) return hrc;
10346
10347 data.llAttachedDevices.clear();
10348 for (MediumAttachmentList::const_iterator
10349 it = atts.begin();
10350 it != atts.end();
10351 ++it)
10352 {
10353 settings::AttachedDevice dev;
10354 IMediumAttachment *iA = *it;
10355 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10356 Medium *pMedium = pAttach->i_getMedium();
10357
10358 dev.deviceType = pAttach->i_getType();
10359 dev.lPort = pAttach->i_getPort();
10360 dev.lDevice = pAttach->i_getDevice();
10361 dev.fPassThrough = pAttach->i_getPassthrough();
10362 dev.fHotPluggable = pAttach->i_getHotPluggable();
10363 if (pMedium)
10364 {
10365 if (pMedium->i_isHostDrive())
10366 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10367 else
10368 dev.uuid = pMedium->i_getId();
10369 dev.fTempEject = pAttach->i_getTempEject();
10370 dev.fNonRotational = pAttach->i_getNonRotational();
10371 dev.fDiscard = pAttach->i_getDiscard();
10372 }
10373
10374 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10375
10376 data.llAttachedDevices.push_back(dev);
10377 }
10378
10379 return S_OK;
10380}
10381
10382/**
10383 * Saves machine state settings as defined by aFlags
10384 * (SaveSTS_* values).
10385 *
10386 * @param aFlags Combination of SaveSTS_* flags.
10387 *
10388 * @note Locks objects for writing.
10389 */
10390HRESULT Machine::i_saveStateSettings(int aFlags)
10391{
10392 if (aFlags == 0)
10393 return S_OK;
10394
10395 AutoCaller autoCaller(this);
10396 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10397
10398 /* This object's write lock is also necessary to serialize file access
10399 * (prevent concurrent reads and writes) */
10400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10401
10402 HRESULT hrc = S_OK;
10403
10404 Assert(mData->pMachineConfigFile);
10405
10406 try
10407 {
10408 if (aFlags & SaveSTS_CurStateModified)
10409 mData->pMachineConfigFile->fCurrentStateModified = true;
10410
10411 if (aFlags & SaveSTS_StateFilePath)
10412 {
10413 if (!mSSData->strStateFilePath.isEmpty())
10414 /* try to make the file name relative to the settings file dir */
10415 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10416 else
10417 mData->pMachineConfigFile->strStateFile.setNull();
10418 }
10419
10420 if (aFlags & SaveSTS_StateTimeStamp)
10421 {
10422 Assert( mData->mMachineState != MachineState_Aborted
10423 || mSSData->strStateFilePath.isEmpty());
10424
10425 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10426
10427 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10428 || mData->mMachineState == MachineState_AbortedSaved);
10429/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10430 }
10431
10432 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10433 }
10434 catch (...)
10435 {
10436 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10437 }
10438
10439 return hrc;
10440}
10441
10442/**
10443 * Ensures that the given medium is added to a media registry. If this machine
10444 * was created with 4.0 or later, then the machine registry is used. Otherwise
10445 * the global VirtualBox media registry is used.
10446 *
10447 * Caller must NOT hold machine lock, media tree or any medium locks!
10448 *
10449 * @param pMedium
10450 */
10451void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10452{
10453 /* Paranoia checks: do not hold machine or media tree locks. */
10454 AssertReturnVoid(!isWriteLockOnCurrentThread());
10455 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10456
10457 ComObjPtr<Medium> pBase;
10458 {
10459 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10460 pBase = pMedium->i_getBase();
10461 }
10462
10463 /* Paranoia checks: do not hold medium locks. */
10464 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10465 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10466
10467 // decide which medium registry to use now that the medium is attached:
10468 Guid uuid;
10469 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10470 if (fCanHaveOwnMediaRegistry)
10471 // machine XML is VirtualBox 4.0 or higher:
10472 uuid = i_getId(); // machine UUID
10473 else
10474 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10475
10476 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10477 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10478 if (pMedium->i_addRegistry(uuid))
10479 mParent->i_markRegistryModified(uuid);
10480
10481 /* For more complex hard disk structures it can happen that the base
10482 * medium isn't yet associated with any medium registry. Do that now. */
10483 if (pMedium != pBase)
10484 {
10485 /* Tree lock needed by Medium::addRegistryAll. */
10486 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10487 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10488 {
10489 treeLock.release();
10490 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10491 treeLock.acquire();
10492 }
10493 if (pBase->i_addRegistryAll(uuid))
10494 {
10495 treeLock.release();
10496 mParent->i_markRegistryModified(uuid);
10497 }
10498 }
10499}
10500
10501/**
10502 * Physically deletes a file belonging to a machine.
10503 *
10504 * @returns HRESULT
10505 * @retval VBOX_E_FILE_ERROR on failure.
10506 * @param strFile File to delete.
10507 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
10508 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
10509 * @param strWhat File hint which will be used when setting an error. Optional.
10510 * @param prc Where to return IPRT's status code on failure.
10511 * Optional and can be NULL.
10512 */
10513HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
10514 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
10515{
10516 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
10517
10518 HRESULT hrc = S_OK;
10519
10520 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
10521
10522 int vrc = RTFileDelete(strFile.c_str());
10523 if (RT_FAILURE(vrc))
10524 {
10525 if ( !fIgnoreFailures
10526 /* Don't (externally) bitch about stuff which doesn't exist. */
10527 && ( vrc != VERR_FILE_NOT_FOUND
10528 && vrc != VERR_PATH_NOT_FOUND
10529 )
10530 )
10531 {
10532 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
10533
10534 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
10535 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
10536 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
10537 }
10538 }
10539
10540 if (prc)
10541 *prc = vrc;
10542 return hrc;
10543}
10544
10545/**
10546 * Creates differencing hard disks for all normal hard disks attached to this
10547 * machine and a new set of attachments to refer to created disks.
10548 *
10549 * Used when taking a snapshot or when deleting the current state. Gets called
10550 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10551 *
10552 * This method assumes that mMediumAttachments contains the original hard disk
10553 * attachments it needs to create diffs for. On success, these attachments will
10554 * be replaced with the created diffs.
10555 *
10556 * Attachments with non-normal hard disks are left as is.
10557 *
10558 * If @a aOnline is @c false then the original hard disks that require implicit
10559 * diffs will be locked for reading. Otherwise it is assumed that they are
10560 * already locked for writing (when the VM was started). Note that in the latter
10561 * case it is responsibility of the caller to lock the newly created diffs for
10562 * writing if this method succeeds.
10563 *
10564 * @param aProgress Progress object to run (must contain at least as
10565 * many operations left as the number of hard disks
10566 * attached).
10567 * @param aWeight Weight of this operation.
10568 * @param aOnline Whether the VM was online prior to this operation.
10569 *
10570 * @note The progress object is not marked as completed, neither on success nor
10571 * on failure. This is a responsibility of the caller.
10572 *
10573 * @note Locks this object and the media tree for writing.
10574 */
10575HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10576 ULONG aWeight,
10577 bool aOnline)
10578{
10579 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10580
10581 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10582 AssertReturn(!!pProgressControl, E_INVALIDARG);
10583
10584 AutoCaller autoCaller(this);
10585 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10586
10587 /* We can't use AutoMultiWriteLock2 here as some error code paths acquire
10588 * the media tree lock which means we need to be able to drop the media
10589 * tree lock individually in those cases. */
10590 AutoWriteLock aMachineLock(this->lockHandle() COMMA_LOCKVAL_SRC_POS);
10591 AutoWriteLock aTreeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10592
10593 /* must be in a protective state because we release the lock below */
10594 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10595 || mData->mMachineState == MachineState_OnlineSnapshotting
10596 || mData->mMachineState == MachineState_LiveSnapshotting
10597 || mData->mMachineState == MachineState_RestoringSnapshot
10598 || mData->mMachineState == MachineState_DeletingSnapshot
10599 , E_FAIL);
10600
10601 HRESULT hrc = S_OK;
10602
10603 // use appropriate locked media map (online or offline)
10604 MediumLockListMap lockedMediaOffline;
10605 MediumLockListMap *lockedMediaMap;
10606 if (aOnline)
10607 lockedMediaMap = &mData->mSession.mLockedMedia;
10608 else
10609 lockedMediaMap = &lockedMediaOffline;
10610
10611 try
10612 {
10613 if (!aOnline)
10614 {
10615 /* lock all attached hard disks early to detect "in use"
10616 * situations before creating actual diffs */
10617 for (MediumAttachmentList::const_iterator
10618 it = mMediumAttachments->begin();
10619 it != mMediumAttachments->end();
10620 ++it)
10621 {
10622 MediumAttachment *pAtt = *it;
10623 if (pAtt->i_getType() == DeviceType_HardDisk)
10624 {
10625 Medium *pMedium = pAtt->i_getMedium();
10626 Assert(pMedium);
10627
10628 MediumLockList *pMediumLockList(new MediumLockList());
10629 aTreeLock.release();
10630 aMachineLock.release();
10631 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10632 NULL /* pToLockWrite */,
10633 false /* fMediumLockWriteAll */,
10634 NULL,
10635 *pMediumLockList);
10636 aMachineLock.acquire();
10637 aTreeLock.acquire();
10638 if (FAILED(hrc))
10639 {
10640 delete pMediumLockList;
10641 throw hrc;
10642 }
10643 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10644 if (FAILED(hrc))
10645 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
10646 }
10647 }
10648
10649 /* Now lock all media. If this fails, nothing is locked. */
10650 aTreeLock.release();
10651 aMachineLock.release();
10652 hrc = lockedMediaMap->Lock();
10653 aMachineLock.acquire();
10654 aTreeLock.acquire();
10655 if (FAILED(hrc))
10656 throw setError(hrc, tr("Locking of attached media failed"));
10657 }
10658
10659 /* remember the current list (note that we don't use backup() since
10660 * mMediumAttachments may be already backed up) */
10661 MediumAttachmentList atts = *mMediumAttachments.data();
10662
10663 /* start from scratch */
10664 mMediumAttachments->clear();
10665
10666 /* go through remembered attachments and create diffs for normal hard
10667 * disks and attach them */
10668 for (MediumAttachmentList::const_iterator
10669 it = atts.begin();
10670 it != atts.end();
10671 ++it)
10672 {
10673 MediumAttachment *pAtt = *it;
10674
10675 DeviceType_T devType = pAtt->i_getType();
10676 Medium *pMedium = pAtt->i_getMedium();
10677
10678 if ( devType != DeviceType_HardDisk
10679 || pMedium == NULL
10680 || pMedium->i_getType() != MediumType_Normal)
10681 {
10682 /* copy the attachment as is */
10683
10684 /** @todo the progress object created in SessionMachine::TakeSnaphot
10685 * only expects operations for hard disks. Later other
10686 * device types need to show up in the progress as well. */
10687 if (devType == DeviceType_HardDisk)
10688 {
10689 if (pMedium == NULL)
10690 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10691 aWeight); // weight
10692 else
10693 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10694 pMedium->i_getBase()->i_getName().c_str()).raw(),
10695 aWeight); // weight
10696 }
10697
10698 mMediumAttachments->push_back(pAtt);
10699 continue;
10700 }
10701
10702 /* need a diff */
10703 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10704 pMedium->i_getBase()->i_getName().c_str()).raw(),
10705 aWeight); // weight
10706
10707 Utf8Str strFullSnapshotFolder;
10708 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10709
10710 ComObjPtr<Medium> diff;
10711 diff.createObject();
10712 // store the diff in the same registry as the parent
10713 // (this cannot fail here because we can't create implicit diffs for
10714 // unregistered images)
10715 Guid uuidRegistryParent;
10716 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10717 Assert(fInRegistry); NOREF(fInRegistry);
10718 hrc = diff->init(mParent,
10719 pMedium->i_getPreferredDiffFormat(),
10720 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10721 uuidRegistryParent,
10722 DeviceType_HardDisk);
10723 if (FAILED(hrc))
10724 {
10725 /* Throwing an exception here causes the 'diff' object to go out of scope
10726 * which triggers its destructor (ComObjPtr<Medium>::~ComObjPtr) which will
10727 * ultimately call Medium::uninit() which acquires the media tree lock
10728 * (VirtualBox::i_getMediaTreeLockHandle()) so drop the media tree lock here. */
10729 aTreeLock.release();
10730 throw hrc;
10731 }
10732
10733 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10734 * the push_back? Looks like we're going to release medium with the
10735 * wrong kind of lock (general issue with if we fail anywhere at all)
10736 * and an orphaned VDI in the snapshots folder. */
10737
10738 /* update the appropriate lock list */
10739 MediumLockList *pMediumLockList;
10740 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
10741 AssertComRCThrowRC(hrc);
10742 if (aOnline)
10743 {
10744 aTreeLock.release();
10745 aMachineLock.release();
10746 /* The currently attached medium will be read-only, change
10747 * the lock type to read. */
10748 hrc = pMediumLockList->Update(pMedium, false);
10749 aMachineLock.acquire();
10750 aTreeLock.acquire();
10751 AssertComRCThrowRC(hrc);
10752 }
10753
10754 /* release the locks before the potentially lengthy operation */
10755 aTreeLock.release();
10756 aMachineLock.release();
10757 hrc = pMedium->i_createDiffStorage(diff,
10758 pMedium->i_getPreferredDiffVariant(),
10759 pMediumLockList,
10760 NULL /* aProgress */,
10761 true /* aWait */,
10762 false /* aNotify */);
10763 aMachineLock.acquire();
10764 aTreeLock.acquire();
10765 if (FAILED(hrc))
10766 {
10767 /* As above, 'diff' will go out of scope via the 'throw' here resulting in
10768 * Medium::uninit() being called which acquires the media tree lock. */
10769 aTreeLock.release();
10770 throw hrc;
10771 }
10772
10773 /* actual lock list update is done in Machine::i_commitMedia */
10774
10775 hrc = diff->i_addBackReference(mData->mUuid);
10776 AssertComRCThrowRC(hrc);
10777
10778 /* add a new attachment */
10779 ComObjPtr<MediumAttachment> attachment;
10780 attachment.createObject();
10781 hrc = attachment->init(this,
10782 diff,
10783 pAtt->i_getControllerName(),
10784 pAtt->i_getPort(),
10785 pAtt->i_getDevice(),
10786 DeviceType_HardDisk,
10787 true /* aImplicit */,
10788 false /* aPassthrough */,
10789 false /* aTempEject */,
10790 pAtt->i_getNonRotational(),
10791 pAtt->i_getDiscard(),
10792 pAtt->i_getHotPluggable(),
10793 pAtt->i_getBandwidthGroup());
10794 if (FAILED(hrc)) {
10795 /* As above, 'diff' will go out of scope via the 'throw' here resulting in
10796 * Medium::uninit() being called which acquires the media tree lock. */
10797 aTreeLock.release();
10798 throw hrc;
10799 }
10800
10801 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10802 AssertComRCThrowRC(hrc);
10803 mMediumAttachments->push_back(attachment);
10804 }
10805 }
10806 catch (HRESULT hrcXcpt)
10807 {
10808 hrc = hrcXcpt;
10809 }
10810
10811 /* unlock all hard disks we locked when there is no VM */
10812 if (!aOnline)
10813 {
10814 ErrorInfoKeeper eik;
10815
10816 HRESULT hrc2 = lockedMediaMap->Clear();
10817 AssertComRC(hrc2);
10818 }
10819
10820 return hrc;
10821}
10822
10823/**
10824 * Deletes implicit differencing hard disks created either by
10825 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10826 * mMediumAttachments.
10827 *
10828 * Note that to delete hard disks created by #attachDevice() this method is
10829 * called from #i_rollbackMedia() when the changes are rolled back.
10830 *
10831 * @note Locks this object and the media tree for writing.
10832 */
10833HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10834{
10835 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10836
10837 AutoCaller autoCaller(this);
10838 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10839
10840 AutoMultiWriteLock2 alock(this->lockHandle(),
10841 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10842
10843 /* We absolutely must have backed up state. */
10844 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10845
10846 /* Check if there are any implicitly created diff images. */
10847 bool fImplicitDiffs = false;
10848 for (MediumAttachmentList::const_iterator
10849 it = mMediumAttachments->begin();
10850 it != mMediumAttachments->end();
10851 ++it)
10852 {
10853 const ComObjPtr<MediumAttachment> &pAtt = *it;
10854 if (pAtt->i_isImplicit())
10855 {
10856 fImplicitDiffs = true;
10857 break;
10858 }
10859 }
10860 /* If there is nothing to do, leave early. This saves lots of image locking
10861 * effort. It also avoids a MachineStateChanged event without real reason.
10862 * This is important e.g. when loading a VM config, because there should be
10863 * no events. Otherwise API clients can become thoroughly confused for
10864 * inaccessible VMs (the code for loading VM configs uses this method for
10865 * cleanup if the config makes no sense), as they take such events as an
10866 * indication that the VM is alive, and they would force the VM config to
10867 * be reread, leading to an endless loop. */
10868 if (!fImplicitDiffs)
10869 return S_OK;
10870
10871 HRESULT hrc = S_OK;
10872 MachineState_T oldState = mData->mMachineState;
10873
10874 /* will release the lock before the potentially lengthy operation,
10875 * so protect with the special state (unless already protected) */
10876 if ( oldState != MachineState_Snapshotting
10877 && oldState != MachineState_OnlineSnapshotting
10878 && oldState != MachineState_LiveSnapshotting
10879 && oldState != MachineState_RestoringSnapshot
10880 && oldState != MachineState_DeletingSnapshot
10881 && oldState != MachineState_DeletingSnapshotOnline
10882 && oldState != MachineState_DeletingSnapshotPaused
10883 )
10884 i_setMachineState(MachineState_SettingUp);
10885
10886 // use appropriate locked media map (online or offline)
10887 MediumLockListMap lockedMediaOffline;
10888 MediumLockListMap *lockedMediaMap;
10889 if (aOnline)
10890 lockedMediaMap = &mData->mSession.mLockedMedia;
10891 else
10892 lockedMediaMap = &lockedMediaOffline;
10893
10894 try
10895 {
10896 if (!aOnline)
10897 {
10898 /* lock all attached hard disks early to detect "in use"
10899 * situations before deleting actual diffs */
10900 for (MediumAttachmentList::const_iterator
10901 it = mMediumAttachments->begin();
10902 it != mMediumAttachments->end();
10903 ++it)
10904 {
10905 MediumAttachment *pAtt = *it;
10906 if (pAtt->i_getType() == DeviceType_HardDisk)
10907 {
10908 Medium *pMedium = pAtt->i_getMedium();
10909 Assert(pMedium);
10910
10911 MediumLockList *pMediumLockList(new MediumLockList());
10912 alock.release();
10913 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10914 NULL /* pToLockWrite */,
10915 false /* fMediumLockWriteAll */,
10916 NULL,
10917 *pMediumLockList);
10918 alock.acquire();
10919
10920 if (FAILED(hrc))
10921 {
10922 delete pMediumLockList;
10923 throw hrc;
10924 }
10925
10926 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10927 if (FAILED(hrc))
10928 throw hrc;
10929 }
10930 }
10931
10932 if (FAILED(hrc))
10933 throw hrc;
10934 } // end of offline
10935
10936 /* Lock lists are now up to date and include implicitly created media */
10937
10938 /* Go through remembered attachments and delete all implicitly created
10939 * diffs and fix up the attachment information */
10940 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10941 MediumAttachmentList implicitAtts;
10942 for (MediumAttachmentList::const_iterator
10943 it = mMediumAttachments->begin();
10944 it != mMediumAttachments->end();
10945 ++it)
10946 {
10947 ComObjPtr<MediumAttachment> pAtt = *it;
10948 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10949 if (pMedium.isNull())
10950 continue;
10951
10952 // Implicit attachments go on the list for deletion and back references are removed.
10953 if (pAtt->i_isImplicit())
10954 {
10955 /* Deassociate and mark for deletion */
10956 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10957 hrc = pMedium->i_removeBackReference(mData->mUuid);
10958 if (FAILED(hrc))
10959 throw hrc;
10960 implicitAtts.push_back(pAtt);
10961 continue;
10962 }
10963
10964 /* Was this medium attached before? */
10965 if (!i_findAttachment(oldAtts, pMedium))
10966 {
10967 /* no: de-associate */
10968 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10969 hrc = pMedium->i_removeBackReference(mData->mUuid);
10970 if (FAILED(hrc))
10971 throw hrc;
10972 continue;
10973 }
10974 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10975 }
10976
10977 /* If there are implicit attachments to delete, throw away the lock
10978 * map contents (which will unlock all media) since the medium
10979 * attachments will be rolled back. Below we need to completely
10980 * recreate the lock map anyway since it is infinitely complex to
10981 * do this incrementally (would need reconstructing each attachment
10982 * change, which would be extremely hairy). */
10983 if (implicitAtts.size() != 0)
10984 {
10985 ErrorInfoKeeper eik;
10986
10987 HRESULT hrc2 = lockedMediaMap->Clear();
10988 AssertComRC(hrc2);
10989 }
10990
10991 /* rollback hard disk changes */
10992 mMediumAttachments.rollback();
10993
10994 MultiResult mrc(S_OK);
10995
10996 // Delete unused implicit diffs.
10997 if (implicitAtts.size() != 0)
10998 {
10999 alock.release();
11000
11001 for (MediumAttachmentList::const_iterator
11002 it = implicitAtts.begin();
11003 it != implicitAtts.end();
11004 ++it)
11005 {
11006 // Remove medium associated with this attachment.
11007 ComObjPtr<MediumAttachment> pAtt = *it;
11008 Assert(pAtt);
11009 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11010 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11011 Assert(pMedium);
11012
11013 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11014 // continue on delete failure, just collect error messages
11015 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
11016 pMedium->i_getLocationFull().c_str() ));
11017 mrc = hrc;
11018 }
11019 // Clear the list of deleted implicit attachments now, while not
11020 // holding the lock, as it will ultimately trigger Medium::uninit()
11021 // calls which assume that the media tree lock isn't held.
11022 implicitAtts.clear();
11023
11024 alock.acquire();
11025
11026 /* if there is a VM recreate media lock map as mentioned above,
11027 * otherwise it is a waste of time and we leave things unlocked */
11028 if (aOnline)
11029 {
11030 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11031 /* must never be NULL, but better safe than sorry */
11032 if (!pMachine.isNull())
11033 {
11034 alock.release();
11035 hrc = mData->mSession.mMachine->i_lockMedia();
11036 alock.acquire();
11037 if (FAILED(hrc))
11038 throw hrc;
11039 }
11040 }
11041 }
11042 }
11043 catch (HRESULT hrcXcpt)
11044 {
11045 hrc = hrcXcpt;
11046 }
11047
11048 if (mData->mMachineState == MachineState_SettingUp)
11049 i_setMachineState(oldState);
11050
11051 /* unlock all hard disks we locked when there is no VM */
11052 if (!aOnline)
11053 {
11054 ErrorInfoKeeper eik;
11055
11056 HRESULT hrc2 = lockedMediaMap->Clear();
11057 AssertComRC(hrc2);
11058 }
11059
11060 return hrc;
11061}
11062
11063
11064/**
11065 * Looks through the given list of media attachments for one with the given parameters
11066 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11067 * can be searched as well if needed.
11068 *
11069 * @param ll
11070 * @param aControllerName
11071 * @param aControllerPort
11072 * @param aDevice
11073 * @return
11074 */
11075MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11076 const Utf8Str &aControllerName,
11077 LONG aControllerPort,
11078 LONG aDevice)
11079{
11080 for (MediumAttachmentList::const_iterator
11081 it = ll.begin();
11082 it != ll.end();
11083 ++it)
11084 {
11085 MediumAttachment *pAttach = *it;
11086 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11087 return pAttach;
11088 }
11089
11090 return NULL;
11091}
11092
11093/**
11094 * Looks through the given list of media attachments for one with the given parameters
11095 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11096 * can be searched as well if needed.
11097 *
11098 * @param ll
11099 * @param pMedium
11100 * @return
11101 */
11102MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11103 ComObjPtr<Medium> pMedium)
11104{
11105 for (MediumAttachmentList::const_iterator
11106 it = ll.begin();
11107 it != ll.end();
11108 ++it)
11109 {
11110 MediumAttachment *pAttach = *it;
11111 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11112 if (pMediumThis == pMedium)
11113 return pAttach;
11114 }
11115
11116 return NULL;
11117}
11118
11119/**
11120 * Looks through the given list of media attachments for one with the given parameters
11121 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11122 * can be searched as well if needed.
11123 *
11124 * @param ll
11125 * @param id
11126 * @return
11127 */
11128MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11129 Guid &id)
11130{
11131 for (MediumAttachmentList::const_iterator
11132 it = ll.begin();
11133 it != ll.end();
11134 ++it)
11135 {
11136 MediumAttachment *pAttach = *it;
11137 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11138 if (pMediumThis->i_getId() == id)
11139 return pAttach;
11140 }
11141
11142 return NULL;
11143}
11144
11145/**
11146 * Main implementation for Machine::DetachDevice. This also gets called
11147 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11148 *
11149 * @param pAttach Medium attachment to detach.
11150 * @param writeLock Machine write lock which the caller must have locked once.
11151 * This may be released temporarily in here.
11152 * @param pSnapshot If NULL, then the detachment is for the current machine.
11153 * Otherwise this is for a SnapshotMachine, and this must be
11154 * its snapshot.
11155 * @return
11156 */
11157HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11158 AutoWriteLock &writeLock,
11159 Snapshot *pSnapshot)
11160{
11161 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11162 DeviceType_T mediumType = pAttach->i_getType();
11163
11164 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11165
11166 if (pAttach->i_isImplicit())
11167 {
11168 /* attempt to implicitly delete the implicitly created diff */
11169
11170 /// @todo move the implicit flag from MediumAttachment to Medium
11171 /// and forbid any hard disk operation when it is implicit. Or maybe
11172 /// a special media state for it to make it even more simple.
11173
11174 Assert(mMediumAttachments.isBackedUp());
11175
11176 /* will release the lock before the potentially lengthy operation, so
11177 * protect with the special state */
11178 MachineState_T oldState = mData->mMachineState;
11179 i_setMachineState(MachineState_SettingUp);
11180
11181 writeLock.release();
11182
11183 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11184
11185 writeLock.acquire();
11186
11187 i_setMachineState(oldState);
11188
11189 if (FAILED(hrc)) return hrc;
11190 }
11191
11192 i_setModified(IsModified_Storage);
11193 mMediumAttachments.backup();
11194 mMediumAttachments->remove(pAttach);
11195
11196 if (!oldmedium.isNull())
11197 {
11198 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11199 if (pSnapshot)
11200 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11201 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11202 else if (mediumType != DeviceType_HardDisk)
11203 oldmedium->i_removeBackReference(mData->mUuid);
11204 }
11205
11206 return S_OK;
11207}
11208
11209/**
11210 * Goes thru all media of the given list and
11211 *
11212 * 1) calls i_detachDevice() on each of them for this machine and
11213 * 2) adds all Medium objects found in the process to the given list,
11214 * depending on cleanupMode.
11215 *
11216 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11217 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11218 * media to the list.
11219 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11220 * also removable media if they are located in the VM folder and referenced
11221 * only by this VM (media prepared by unattended installer).
11222 *
11223 * This gets called from Machine::Unregister, both for the actual Machine and
11224 * the SnapshotMachine objects that might be found in the snapshots.
11225 *
11226 * Requires caller and locking. The machine lock must be passed in because it
11227 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11228 *
11229 * @param writeLock Machine lock from top-level caller; this gets passed to
11230 * i_detachDevice.
11231 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11232 * object if called for a SnapshotMachine.
11233 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11234 * added to llMedia; if Full, then all media get added;
11235 * otherwise no media get added.
11236 * @param llMedia Caller's list to receive Medium objects which got detached so
11237 * caller can close() them, depending on cleanupMode.
11238 * @return
11239 */
11240HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11241 Snapshot *pSnapshot,
11242 CleanupMode_T cleanupMode,
11243 MediaList &llMedia)
11244{
11245 Assert(isWriteLockOnCurrentThread());
11246
11247 HRESULT hrc;
11248
11249 // make a temporary list because i_detachDevice invalidates iterators into
11250 // mMediumAttachments
11251 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11252
11253 for (MediumAttachmentList::iterator
11254 it = llAttachments2.begin();
11255 it != llAttachments2.end();
11256 ++it)
11257 {
11258 ComObjPtr<MediumAttachment> &pAttach = *it;
11259 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11260
11261 if (!pMedium.isNull())
11262 {
11263 AutoCaller mac(pMedium);
11264 if (FAILED(mac.hrc())) return mac.hrc();
11265 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11266 DeviceType_T devType = pMedium->i_getDeviceType();
11267 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11268 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11269 strMediumLocation.stripFilename();
11270 Utf8Str strMachineFolder = i_getSettingsFileFull();
11271 strMachineFolder.stripFilename();
11272 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11273 && devType == DeviceType_HardDisk)
11274 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11275 && ( devType == DeviceType_HardDisk
11276 || ( cBackRefs <= 1
11277 && strMediumLocation == strMachineFolder
11278 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11279 || (cleanupMode == CleanupMode_Full)
11280 )
11281 {
11282 llMedia.push_back(pMedium);
11283 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11284 /* Not allowed to keep this lock as below we need the parent
11285 * medium lock, and the lock order is parent to child. */
11286 lock.release();
11287 /*
11288 * Search for media which are not attached to any machine, but
11289 * in the chain to an attached disk. Media are only consided
11290 * if they are:
11291 * - have only one child
11292 * - no references to any machines
11293 * - are of normal medium type
11294 */
11295 while (!pParent.isNull())
11296 {
11297 AutoCaller mac1(pParent);
11298 if (FAILED(mac1.hrc())) return mac1.hrc();
11299 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11300 if (pParent->i_getChildren().size() == 1)
11301 {
11302 if ( pParent->i_getMachineBackRefCount() == 0
11303 && pParent->i_getType() == MediumType_Normal
11304 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11305 llMedia.push_back(pParent);
11306 }
11307 else
11308 break;
11309 pParent = pParent->i_getParent();
11310 }
11311 }
11312 }
11313
11314 // real machine: then we need to use the proper method
11315 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11316
11317 if (FAILED(hrc))
11318 return hrc;
11319 }
11320
11321 return S_OK;
11322}
11323
11324/**
11325 * Perform deferred hard disk detachments.
11326 *
11327 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11328 * changed (not backed up).
11329 *
11330 * If @a aOnline is @c true then this method will also unlock the old hard
11331 * disks for which the new implicit diffs were created and will lock these new
11332 * diffs for writing.
11333 *
11334 * @param aOnline Whether the VM was online prior to this operation.
11335 *
11336 * @note Locks this object for writing!
11337 */
11338void Machine::i_commitMedia(bool aOnline /*= false*/)
11339{
11340 AutoCaller autoCaller(this);
11341 AssertComRCReturnVoid(autoCaller.hrc());
11342
11343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11344
11345 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11346
11347 HRESULT hrc = S_OK;
11348
11349 /* no attach/detach operations -- nothing to do */
11350 if (!mMediumAttachments.isBackedUp())
11351 return;
11352
11353 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11354 bool fMediaNeedsLocking = false;
11355
11356 /* enumerate new attachments */
11357 for (MediumAttachmentList::const_iterator
11358 it = mMediumAttachments->begin();
11359 it != mMediumAttachments->end();
11360 ++it)
11361 {
11362 MediumAttachment *pAttach = *it;
11363
11364 pAttach->i_commit();
11365
11366 Medium *pMedium = pAttach->i_getMedium();
11367 bool fImplicit = pAttach->i_isImplicit();
11368
11369 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11370 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11371 fImplicit));
11372
11373 /** @todo convert all this Machine-based voodoo to MediumAttachment
11374 * based commit logic. */
11375 if (fImplicit)
11376 {
11377 /* convert implicit attachment to normal */
11378 pAttach->i_setImplicit(false);
11379
11380 if ( aOnline
11381 && pMedium
11382 && pAttach->i_getType() == DeviceType_HardDisk
11383 )
11384 {
11385 /* update the appropriate lock list */
11386 MediumLockList *pMediumLockList;
11387 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11388 AssertComRC(hrc);
11389 if (pMediumLockList)
11390 {
11391 /* unlock if there's a need to change the locking */
11392 if (!fMediaNeedsLocking)
11393 {
11394 Assert(mData->mSession.mLockedMedia.IsLocked());
11395 hrc = mData->mSession.mLockedMedia.Unlock();
11396 AssertComRC(hrc);
11397 fMediaNeedsLocking = true;
11398 }
11399 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
11400 AssertComRC(hrc);
11401 hrc = pMediumLockList->Append(pMedium, true);
11402 AssertComRC(hrc);
11403 }
11404 }
11405
11406 continue;
11407 }
11408
11409 if (pMedium)
11410 {
11411 /* was this medium attached before? */
11412 for (MediumAttachmentList::iterator
11413 oldIt = oldAtts.begin();
11414 oldIt != oldAtts.end();
11415 ++oldIt)
11416 {
11417 MediumAttachment *pOldAttach = *oldIt;
11418 if (pOldAttach->i_getMedium() == pMedium)
11419 {
11420 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11421
11422 /* yes: remove from old to avoid de-association */
11423 oldAtts.erase(oldIt);
11424 break;
11425 }
11426 }
11427 }
11428 }
11429
11430 /* enumerate remaining old attachments and de-associate from the
11431 * current machine state */
11432 for (MediumAttachmentList::const_iterator
11433 it = oldAtts.begin();
11434 it != oldAtts.end();
11435 ++it)
11436 {
11437 MediumAttachment *pAttach = *it;
11438 Medium *pMedium = pAttach->i_getMedium();
11439
11440 /* Detach only hard disks, since DVD/floppy media is detached
11441 * instantly in MountMedium. */
11442 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11443 {
11444 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11445
11446 /* now de-associate from the current machine state */
11447 hrc = pMedium->i_removeBackReference(mData->mUuid);
11448 AssertComRC(hrc);
11449
11450 if (aOnline)
11451 {
11452 /* unlock since medium is not used anymore */
11453 MediumLockList *pMediumLockList;
11454 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11455 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
11456 {
11457 /* this happens for online snapshots, there the attachment
11458 * is changing, but only to a diff image created under
11459 * the old one, so there is no separate lock list */
11460 Assert(!pMediumLockList);
11461 }
11462 else
11463 {
11464 AssertComRC(hrc);
11465 if (pMediumLockList)
11466 {
11467 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
11468 AssertComRC(hrc);
11469 }
11470 }
11471 }
11472 }
11473 }
11474
11475 /* take media locks again so that the locking state is consistent */
11476 if (fMediaNeedsLocking)
11477 {
11478 Assert(aOnline);
11479 hrc = mData->mSession.mLockedMedia.Lock();
11480 AssertComRC(hrc);
11481 }
11482
11483 /* commit the hard disk changes */
11484 mMediumAttachments.commit();
11485
11486 if (i_isSessionMachine())
11487 {
11488 /*
11489 * Update the parent machine to point to the new owner.
11490 * This is necessary because the stored parent will point to the
11491 * session machine otherwise and cause crashes or errors later
11492 * when the session machine gets invalid.
11493 */
11494 /** @todo Change the MediumAttachment class to behave like any other
11495 * class in this regard by creating peer MediumAttachment
11496 * objects for session machines and share the data with the peer
11497 * machine.
11498 */
11499 for (MediumAttachmentList::const_iterator
11500 it = mMediumAttachments->begin();
11501 it != mMediumAttachments->end();
11502 ++it)
11503 (*it)->i_updateParentMachine(mPeer);
11504
11505 /* attach new data to the primary machine and reshare it */
11506 mPeer->mMediumAttachments.attach(mMediumAttachments);
11507 }
11508
11509 return;
11510}
11511
11512/**
11513 * Perform deferred deletion of implicitly created diffs.
11514 *
11515 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11516 * changed (not backed up).
11517 *
11518 * @note Locks this object for writing!
11519 */
11520void Machine::i_rollbackMedia()
11521{
11522 AutoCaller autoCaller(this);
11523 AssertComRCReturnVoid(autoCaller.hrc());
11524
11525 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11526 LogFlowThisFunc(("Entering rollbackMedia\n"));
11527
11528 HRESULT hrc = S_OK;
11529
11530 /* no attach/detach operations -- nothing to do */
11531 if (!mMediumAttachments.isBackedUp())
11532 return;
11533
11534 /* enumerate new attachments */
11535 for (MediumAttachmentList::const_iterator
11536 it = mMediumAttachments->begin();
11537 it != mMediumAttachments->end();
11538 ++it)
11539 {
11540 MediumAttachment *pAttach = *it;
11541 /* Fix up the backrefs for DVD/floppy media. */
11542 if (pAttach->i_getType() != DeviceType_HardDisk)
11543 {
11544 Medium *pMedium = pAttach->i_getMedium();
11545 if (pMedium)
11546 {
11547 hrc = pMedium->i_removeBackReference(mData->mUuid);
11548 AssertComRC(hrc);
11549 }
11550 }
11551
11552 (*it)->i_rollback();
11553
11554 pAttach = *it;
11555 /* Fix up the backrefs for DVD/floppy media. */
11556 if (pAttach->i_getType() != DeviceType_HardDisk)
11557 {
11558 Medium *pMedium = pAttach->i_getMedium();
11559 if (pMedium)
11560 {
11561 hrc = pMedium->i_addBackReference(mData->mUuid);
11562 AssertComRC(hrc);
11563 }
11564 }
11565 }
11566
11567 /** @todo convert all this Machine-based voodoo to MediumAttachment
11568 * based rollback logic. */
11569 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11570
11571 return;
11572}
11573
11574/**
11575 * Returns true if the settings file is located in the directory named exactly
11576 * as the machine; this means, among other things, that the machine directory
11577 * should be auto-renamed.
11578 *
11579 * @param aSettingsDir if not NULL, the full machine settings file directory
11580 * name will be assigned there.
11581 *
11582 * @note Doesn't lock anything.
11583 * @note Not thread safe (must be called from this object's lock).
11584 */
11585bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11586{
11587 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11588 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11589 if (aSettingsDir)
11590 *aSettingsDir = strMachineDirName;
11591 strMachineDirName.stripPath(); // vmname
11592 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11593 strConfigFileOnly.stripPath() // vmname.vbox
11594 .stripSuffix(); // vmname
11595 /** @todo hack, make somehow use of ComposeMachineFilename */
11596 if (mUserData->s.fDirectoryIncludesUUID)
11597 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11598
11599 AssertReturn(!strMachineDirName.isEmpty(), false);
11600 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11601
11602 return strMachineDirName == strConfigFileOnly;
11603}
11604
11605/**
11606 * Discards all changes to machine settings.
11607 *
11608 * @param aNotify Whether to notify the direct session about changes or not.
11609 *
11610 * @note Locks objects for writing!
11611 */
11612void Machine::i_rollback(bool aNotify)
11613{
11614 AutoCaller autoCaller(this);
11615 AssertComRCReturn(autoCaller.hrc(), (void)0);
11616
11617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11618
11619 if (!mStorageControllers.isNull())
11620 {
11621 if (mStorageControllers.isBackedUp())
11622 {
11623 /* unitialize all new devices (absent in the backed up list). */
11624 StorageControllerList *backedList = mStorageControllers.backedUpData();
11625 for (StorageControllerList::const_iterator
11626 it = mStorageControllers->begin();
11627 it != mStorageControllers->end();
11628 ++it)
11629 {
11630 if ( std::find(backedList->begin(), backedList->end(), *it)
11631 == backedList->end()
11632 )
11633 {
11634 (*it)->uninit();
11635 }
11636 }
11637
11638 /* restore the list */
11639 mStorageControllers.rollback();
11640 }
11641
11642 /* rollback any changes to devices after restoring the list */
11643 if (mData->flModifications & IsModified_Storage)
11644 {
11645 for (StorageControllerList::const_iterator
11646 it = mStorageControllers->begin();
11647 it != mStorageControllers->end();
11648 ++it)
11649 {
11650 (*it)->i_rollback();
11651 }
11652 }
11653 }
11654
11655 if (!mUSBControllers.isNull())
11656 {
11657 if (mUSBControllers.isBackedUp())
11658 {
11659 /* unitialize all new devices (absent in the backed up list). */
11660 USBControllerList *backedList = mUSBControllers.backedUpData();
11661 for (USBControllerList::const_iterator
11662 it = mUSBControllers->begin();
11663 it != mUSBControllers->end();
11664 ++it)
11665 {
11666 if ( std::find(backedList->begin(), backedList->end(), *it)
11667 == backedList->end()
11668 )
11669 {
11670 (*it)->uninit();
11671 }
11672 }
11673
11674 /* restore the list */
11675 mUSBControllers.rollback();
11676 }
11677
11678 /* rollback any changes to devices after restoring the list */
11679 if (mData->flModifications & IsModified_USB)
11680 {
11681 for (USBControllerList::const_iterator
11682 it = mUSBControllers->begin();
11683 it != mUSBControllers->end();
11684 ++it)
11685 {
11686 (*it)->i_rollback();
11687 }
11688 }
11689 }
11690
11691 mUserData.rollback();
11692
11693 mHWData.rollback();
11694
11695 if (mData->flModifications & IsModified_Storage)
11696 i_rollbackMedia();
11697
11698 if (mPlatform)
11699 {
11700 mPlatform->i_rollback();
11701 i_platformPropertiesUpdate();
11702 }
11703
11704 if (mFirmwareSettings)
11705 mFirmwareSettings->i_rollback();
11706
11707 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11708 mRecordingSettings->i_rollback();
11709
11710 if (mTrustedPlatformModule)
11711 mTrustedPlatformModule->i_rollback();
11712
11713 if (mNvramStore)
11714 mNvramStore->i_rollback();
11715
11716 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11717 mGraphicsAdapter->i_rollback();
11718
11719 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11720 mVRDEServer->i_rollback();
11721
11722 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
11723 mAudioSettings->i_rollback();
11724
11725 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11726 mUSBDeviceFilters->i_rollback();
11727
11728 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11729 mBandwidthControl->i_rollback();
11730
11731 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
11732 mGuestDebugControl->i_rollback();
11733
11734 if (mPlatform && (mData->flModifications & IsModified_Platform))
11735 {
11736 ChipsetType_T enmChipset;
11737 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11738 ComAssertComRC(hrc);
11739
11740 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipset));
11741 }
11742
11743 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11744 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11745 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11746
11747 if (mData->flModifications & IsModified_NetworkAdapters)
11748 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11749 if ( mNetworkAdapters[slot]
11750 && mNetworkAdapters[slot]->i_isModified())
11751 {
11752 mNetworkAdapters[slot]->i_rollback();
11753 networkAdapters[slot] = mNetworkAdapters[slot];
11754 }
11755
11756 if (mData->flModifications & IsModified_SerialPorts)
11757 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11758 if ( mSerialPorts[slot]
11759 && mSerialPorts[slot]->i_isModified())
11760 {
11761 mSerialPorts[slot]->i_rollback();
11762 serialPorts[slot] = mSerialPorts[slot];
11763 }
11764
11765 if (mData->flModifications & IsModified_ParallelPorts)
11766 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11767 if ( mParallelPorts[slot]
11768 && mParallelPorts[slot]->i_isModified())
11769 {
11770 mParallelPorts[slot]->i_rollback();
11771 parallelPorts[slot] = mParallelPorts[slot];
11772 }
11773
11774 if (aNotify)
11775 {
11776 /* inform the direct session about changes */
11777
11778 ComObjPtr<Machine> that = this;
11779 uint32_t flModifications = mData->flModifications;
11780 alock.release();
11781
11782 if (flModifications & IsModified_SharedFolders)
11783 that->i_onSharedFolderChange();
11784
11785 if (flModifications & IsModified_VRDEServer)
11786 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11787 if (flModifications & IsModified_USB)
11788 that->i_onUSBControllerChange();
11789
11790 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11791 if (networkAdapters[slot])
11792 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11793 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11794 if (serialPorts[slot])
11795 that->i_onSerialPortChange(serialPorts[slot]);
11796 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11797 if (parallelPorts[slot])
11798 that->i_onParallelPortChange(parallelPorts[slot]);
11799
11800 if (flModifications & IsModified_Storage)
11801 {
11802 for (StorageControllerList::const_iterator
11803 it = mStorageControllers->begin();
11804 it != mStorageControllers->end();
11805 ++it)
11806 {
11807 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11808 }
11809 }
11810
11811 if (flModifications & IsModified_GuestDebugControl)
11812 that->i_onGuestDebugControlChange(mGuestDebugControl);
11813
11814#if 0
11815 if (flModifications & IsModified_BandwidthControl)
11816 that->onBandwidthControlChange();
11817#endif
11818 }
11819}
11820
11821/**
11822 * Commits all the changes to machine settings.
11823 *
11824 * Note that this operation is supposed to never fail.
11825 *
11826 * @note Locks this object and children for writing.
11827 */
11828void Machine::i_commit()
11829{
11830 AutoCaller autoCaller(this);
11831 AssertComRCReturnVoid(autoCaller.hrc());
11832
11833 AutoCaller peerCaller(mPeer);
11834 AssertComRCReturnVoid(peerCaller.hrc());
11835
11836 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11837
11838 /*
11839 * use safe commit to ensure Snapshot machines (that share mUserData)
11840 * will still refer to a valid memory location
11841 */
11842 mUserData.commitCopy();
11843
11844 mHWData.commit();
11845
11846 if (mMediumAttachments.isBackedUp())
11847 i_commitMedia(Global::IsOnline(mData->mMachineState));
11848
11849 mPlatform->i_commit();
11850 mFirmwareSettings->i_commit();
11851 mRecordingSettings->i_commit();
11852 mTrustedPlatformModule->i_commit();
11853 mNvramStore->i_commit();
11854 mGraphicsAdapter->i_commit();
11855 mVRDEServer->i_commit();
11856 mAudioSettings->i_commit();
11857 mUSBDeviceFilters->i_commit();
11858 mBandwidthControl->i_commit();
11859 mGuestDebugControl->i_commit();
11860
11861 /* Since mNetworkAdapters is a list which might have been changed (resized)
11862 * without using the Backupable<> template we need to handle the copying
11863 * of the list entries manually, including the creation of peers for the
11864 * new objects. */
11865 ChipsetType_T enmChipset;
11866 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11867 ComAssertComRC(hrc);
11868
11869 bool commitNetworkAdapters = false;
11870 size_t const newSize = PlatformProperties::s_getMaxNetworkAdapters(enmChipset);
11871 if (mPeer)
11872 {
11873 size_t const oldSize = mNetworkAdapters.size();
11874 size_t const oldPeerSize = mPeer->mNetworkAdapters.size();
11875
11876 /* commit everything, even the ones which will go away */
11877 for (size_t slot = 0; slot < oldSize; slot++)
11878 mNetworkAdapters[slot]->i_commit();
11879 /* copy over the new entries, creating a peer and uninit the original */
11880 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, oldPeerSize));
11881 /* make sure to have enough room for iterating over the (newly added) slots down below */
11882 if (newSize > oldSize)
11883 {
11884 mNetworkAdapters.resize(newSize);
11885
11886 com::Utf8Str osTypeId;
11887 ComObjPtr<GuestOSType> osType = NULL;
11888 hrc = getOSTypeId(osTypeId);
11889 if (SUCCEEDED(hrc))
11890 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
11891
11892 for (size_t slot = oldSize; slot < newSize; slot++)
11893 {
11894 mNetworkAdapters[slot].createObject();
11895 mNetworkAdapters[slot]->init(this, (ULONG)slot);
11896 mNetworkAdapters[slot]->i_applyDefaults(SUCCEEDED(hrc) ? osType : NULL);
11897 }
11898 }
11899 for (size_t slot = 0; slot < newSize; slot++)
11900 {
11901 /* look if this adapter has a peer device */
11902 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11903 if (!peer)
11904 {
11905 /* no peer means the adapter is a newly created one;
11906 * create a peer owning data this data share it with */
11907 peer.createObject();
11908 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11909 }
11910 mPeer->mNetworkAdapters[slot] = peer;
11911 }
11912 /* uninit any no longer needed network adapters */
11913 for (size_t slot = newSize; slot < oldSize; ++slot)
11914 mNetworkAdapters[slot]->uninit();
11915 for (size_t slot = newSize; slot < oldPeerSize; ++slot)
11916 {
11917 if (mPeer->mNetworkAdapters[slot])
11918 mPeer->mNetworkAdapters[slot]->uninit();
11919 }
11920 /* Keep the original network adapter count until this point, so that
11921 * discarding a chipset type change will not lose settings. */
11922 mNetworkAdapters.resize(newSize);
11923 mPeer->mNetworkAdapters.resize(newSize);
11924 }
11925 else
11926 {
11927 /* we have no peer (our parent is the newly created machine);
11928 * just commit changes to the network adapters */
11929 commitNetworkAdapters = true;
11930 }
11931 if (commitNetworkAdapters)
11932 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11933 mNetworkAdapters[slot]->i_commit();
11934
11935 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11936 mSerialPorts[slot]->i_commit();
11937 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11938 mParallelPorts[slot]->i_commit();
11939
11940 bool commitStorageControllers = false;
11941
11942 if (mStorageControllers.isBackedUp())
11943 {
11944 mStorageControllers.commit();
11945
11946 if (mPeer)
11947 {
11948 /* Commit all changes to new controllers (this will reshare data with
11949 * peers for those who have peers) */
11950 StorageControllerList *newList = new StorageControllerList();
11951 for (StorageControllerList::const_iterator
11952 it = mStorageControllers->begin();
11953 it != mStorageControllers->end();
11954 ++it)
11955 {
11956 (*it)->i_commit();
11957
11958 /* look if this controller has a peer device */
11959 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11960 if (!peer)
11961 {
11962 /* no peer means the device is a newly created one;
11963 * create a peer owning data this device share it with */
11964 peer.createObject();
11965 peer->init(mPeer, *it, true /* aReshare */);
11966 }
11967 else
11968 {
11969 /* remove peer from the old list */
11970 mPeer->mStorageControllers->remove(peer);
11971 }
11972 /* and add it to the new list */
11973 newList->push_back(peer);
11974 }
11975
11976 /* uninit old peer's controllers that are left */
11977 for (StorageControllerList::const_iterator
11978 it = mPeer->mStorageControllers->begin();
11979 it != mPeer->mStorageControllers->end();
11980 ++it)
11981 {
11982 (*it)->uninit();
11983 }
11984
11985 /* attach new list of controllers to our peer */
11986 mPeer->mStorageControllers.attach(newList);
11987 }
11988 else
11989 {
11990 /* we have no peer (our parent is the newly created machine);
11991 * just commit changes to devices */
11992 commitStorageControllers = true;
11993 }
11994 }
11995 else
11996 {
11997 /* the list of controllers itself is not changed,
11998 * just commit changes to controllers themselves */
11999 commitStorageControllers = true;
12000 }
12001
12002 if (commitStorageControllers)
12003 {
12004 for (StorageControllerList::const_iterator
12005 it = mStorageControllers->begin();
12006 it != mStorageControllers->end();
12007 ++it)
12008 {
12009 (*it)->i_commit();
12010 }
12011 }
12012
12013 bool commitUSBControllers = false;
12014
12015 if (mUSBControllers.isBackedUp())
12016 {
12017 mUSBControllers.commit();
12018
12019 if (mPeer)
12020 {
12021 /* Commit all changes to new controllers (this will reshare data with
12022 * peers for those who have peers) */
12023 USBControllerList *newList = new USBControllerList();
12024 for (USBControllerList::const_iterator
12025 it = mUSBControllers->begin();
12026 it != mUSBControllers->end();
12027 ++it)
12028 {
12029 (*it)->i_commit();
12030
12031 /* look if this controller has a peer device */
12032 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12033 if (!peer)
12034 {
12035 /* no peer means the device is a newly created one;
12036 * create a peer owning data this device share it with */
12037 peer.createObject();
12038 peer->init(mPeer, *it, true /* aReshare */);
12039 }
12040 else
12041 {
12042 /* remove peer from the old list */
12043 mPeer->mUSBControllers->remove(peer);
12044 }
12045 /* and add it to the new list */
12046 newList->push_back(peer);
12047 }
12048
12049 /* uninit old peer's controllers that are left */
12050 for (USBControllerList::const_iterator
12051 it = mPeer->mUSBControllers->begin();
12052 it != mPeer->mUSBControllers->end();
12053 ++it)
12054 {
12055 (*it)->uninit();
12056 }
12057
12058 /* attach new list of controllers to our peer */
12059 mPeer->mUSBControllers.attach(newList);
12060 }
12061 else
12062 {
12063 /* we have no peer (our parent is the newly created machine);
12064 * just commit changes to devices */
12065 commitUSBControllers = true;
12066 }
12067 }
12068 else
12069 {
12070 /* the list of controllers itself is not changed,
12071 * just commit changes to controllers themselves */
12072 commitUSBControllers = true;
12073 }
12074
12075 if (commitUSBControllers)
12076 {
12077 for (USBControllerList::const_iterator
12078 it = mUSBControllers->begin();
12079 it != mUSBControllers->end();
12080 ++it)
12081 {
12082 (*it)->i_commit();
12083 }
12084 }
12085
12086 if (i_isSessionMachine())
12087 {
12088 /* attach new data to the primary machine and reshare it */
12089 mPeer->mUserData.attach(mUserData);
12090 mPeer->mHWData.attach(mHWData);
12091 /* mmMediumAttachments is reshared by fixupMedia */
12092 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12093 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12094 }
12095}
12096
12097/**
12098 * Copies all the hardware data from the given machine.
12099 *
12100 * Currently, only called when the VM is being restored from a snapshot. In
12101 * particular, this implies that the VM is not running during this method's
12102 * call.
12103 *
12104 * @note This method must be called from under this object's lock.
12105 *
12106 * @note This method doesn't call #i_commit(), so all data remains backed up and
12107 * unsaved.
12108 */
12109void Machine::i_copyFrom(Machine *aThat)
12110{
12111 AssertReturnVoid(!i_isSnapshotMachine());
12112 AssertReturnVoid(aThat->i_isSnapshotMachine());
12113
12114 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12115
12116 mHWData.assignCopy(aThat->mHWData);
12117
12118 // create copies of all shared folders (mHWData after attaching a copy
12119 // contains just references to original objects)
12120 for (HWData::SharedFolderList::iterator
12121 it = mHWData->mSharedFolders.begin();
12122 it != mHWData->mSharedFolders.end();
12123 ++it)
12124 {
12125 ComObjPtr<SharedFolder> folder;
12126 folder.createObject();
12127 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12128 AssertComRC(hrc);
12129 *it = folder;
12130 }
12131
12132 mPlatform->i_copyFrom(aThat->mPlatform);
12133 i_platformPropertiesUpdate();
12134 mFirmwareSettings->i_copyFrom(aThat->mFirmwareSettings);
12135 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12136 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12137 mNvramStore->i_copyFrom(aThat->mNvramStore);
12138 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12139 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12140 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12141 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12142 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12143 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12144
12145 /* create private copies of all controllers */
12146 mStorageControllers.backup();
12147 mStorageControllers->clear();
12148 for (StorageControllerList::const_iterator
12149 it = aThat->mStorageControllers->begin();
12150 it != aThat->mStorageControllers->end();
12151 ++it)
12152 {
12153 ComObjPtr<StorageController> ctrl;
12154 ctrl.createObject();
12155 ctrl->initCopy(this, *it);
12156 mStorageControllers->push_back(ctrl);
12157 }
12158
12159 /* create private copies of all USB controllers */
12160 mUSBControllers.backup();
12161 mUSBControllers->clear();
12162 for (USBControllerList::const_iterator
12163 it = aThat->mUSBControllers->begin();
12164 it != aThat->mUSBControllers->end();
12165 ++it)
12166 {
12167 ComObjPtr<USBController> ctrl;
12168 ctrl.createObject();
12169 ctrl->initCopy(this, *it);
12170 mUSBControllers->push_back(ctrl);
12171 }
12172
12173 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12174 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12175 {
12176 if (mNetworkAdapters[slot].isNotNull())
12177 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12178 else
12179 {
12180 unconst(mNetworkAdapters[slot]).createObject();
12181 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12182 }
12183 }
12184 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12185 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12186 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12187 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12188}
12189
12190/**
12191 * Returns whether the given storage controller is hotplug capable.
12192 *
12193 * @returns true if the controller supports hotplugging
12194 * false otherwise.
12195 * @param enmCtrlType The controller type to check for.
12196 */
12197bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12198{
12199 BOOL aHotplugCapable = FALSE;
12200 HRESULT hrc = mPlatformProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12201 AssertComRC(hrc);
12202
12203 return RT_BOOL(aHotplugCapable);
12204}
12205
12206#ifdef VBOX_WITH_RESOURCE_USAGE_API
12207
12208void Machine::i_getDiskList(MediaList &list)
12209{
12210 for (MediumAttachmentList::const_iterator
12211 it = mMediumAttachments->begin();
12212 it != mMediumAttachments->end();
12213 ++it)
12214 {
12215 MediumAttachment *pAttach = *it;
12216 /* just in case */
12217 AssertContinue(pAttach);
12218
12219 AutoCaller localAutoCallerA(pAttach);
12220 if (FAILED(localAutoCallerA.hrc())) continue;
12221
12222 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12223
12224 if (pAttach->i_getType() == DeviceType_HardDisk)
12225 list.push_back(pAttach->i_getMedium());
12226 }
12227}
12228
12229void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12230{
12231 AssertReturnVoid(isWriteLockOnCurrentThread());
12232 AssertPtrReturnVoid(aCollector);
12233
12234 pm::CollectorHAL *hal = aCollector->getHAL();
12235 /* Create sub metrics */
12236 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12237 "Percentage of processor time spent in user mode by the VM process.");
12238 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12239 "Percentage of processor time spent in kernel mode by the VM process.");
12240 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12241 "Size of resident portion of VM process in memory.");
12242 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12243 "Actual size of all VM disks combined.");
12244 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12245 "Network receive rate.");
12246 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12247 "Network transmit rate.");
12248 /* Create and register base metrics */
12249 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12250 cpuLoadUser, cpuLoadKernel);
12251 aCollector->registerBaseMetric(cpuLoad);
12252 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12253 ramUsageUsed);
12254 aCollector->registerBaseMetric(ramUsage);
12255 MediaList disks;
12256 i_getDiskList(disks);
12257 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12258 diskUsageUsed);
12259 aCollector->registerBaseMetric(diskUsage);
12260
12261 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12262 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12263 new pm::AggregateAvg()));
12264 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12265 new pm::AggregateMin()));
12266 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12267 new pm::AggregateMax()));
12268 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12270 new pm::AggregateAvg()));
12271 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12272 new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12274 new pm::AggregateMax()));
12275
12276 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12277 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12278 new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12280 new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12282 new pm::AggregateMax()));
12283
12284 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12285 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12286 new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12288 new pm::AggregateMin()));
12289 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12290 new pm::AggregateMax()));
12291
12292
12293 /* Guest metrics collector */
12294 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12295 aCollector->registerGuest(mCollectorGuest);
12296 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12297
12298 /* Create sub metrics */
12299 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12300 "Percentage of processor time spent in user mode as seen by the guest.");
12301 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12302 "Percentage of processor time spent in kernel mode as seen by the guest.");
12303 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12304 "Percentage of processor time spent idling as seen by the guest.");
12305
12306 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12307 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12308 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12309 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12310 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12311 pm::SubMetric *guestMemCache = new pm::SubMetric(
12312 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12313
12314 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12315 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12316
12317 /* Create and register base metrics */
12318 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12319 machineNetRx, machineNetTx);
12320 aCollector->registerBaseMetric(machineNetRate);
12321
12322 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12323 guestLoadUser, guestLoadKernel, guestLoadIdle);
12324 aCollector->registerBaseMetric(guestCpuLoad);
12325
12326 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12327 guestMemTotal, guestMemFree,
12328 guestMemBalloon, guestMemShared,
12329 guestMemCache, guestPagedTotal);
12330 aCollector->registerBaseMetric(guestCpuMem);
12331
12332 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12333 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12336
12337 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12338 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12339 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12340 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12341
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12344 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12349 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12351
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12354 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12355 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12356
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12361
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12366
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12371
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12376
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12381
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12386}
12387
12388void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12389{
12390 AssertReturnVoid(isWriteLockOnCurrentThread());
12391
12392 if (aCollector)
12393 {
12394 aCollector->unregisterMetricsFor(aMachine);
12395 aCollector->unregisterBaseMetricsFor(aMachine);
12396 }
12397}
12398
12399#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12400
12401/**
12402 * Updates the machine's platform properties based on the current platform architecture.
12403 *
12404 * @note Called internally when committing, rolling back or loading settings.
12405 */
12406void Machine::i_platformPropertiesUpdate()
12407{
12408 if (mPlatform)
12409 {
12410 /* Update architecture for platform properties. */
12411 PlatformArchitecture_T platformArchitecture;
12412 HRESULT hrc = mPlatform->getArchitecture(&platformArchitecture);
12413 ComAssertComRC(hrc);
12414 hrc = mPlatformProperties->i_setArchitecture(platformArchitecture);
12415 ComAssertComRC(hrc);
12416 }
12417}
12418
12419
12420////////////////////////////////////////////////////////////////////////////////
12421
12422DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12423
12424HRESULT SessionMachine::FinalConstruct()
12425{
12426 LogFlowThisFunc(("\n"));
12427
12428 mClientToken = NULL;
12429
12430 return BaseFinalConstruct();
12431}
12432
12433void SessionMachine::FinalRelease()
12434{
12435 LogFlowThisFunc(("\n"));
12436
12437 Assert(!mClientToken);
12438 /* paranoia, should not hang around any more */
12439 if (mClientToken)
12440 {
12441 delete mClientToken;
12442 mClientToken = NULL;
12443 }
12444
12445 uninit(Uninit::Unexpected);
12446
12447 BaseFinalRelease();
12448}
12449
12450/**
12451 * @note Must be called only by Machine::LockMachine() from its own write lock.
12452 */
12453HRESULT SessionMachine::init(Machine *aMachine)
12454{
12455 LogFlowThisFuncEnter();
12456 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12457
12458 AssertReturn(aMachine, E_INVALIDARG);
12459
12460 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12461
12462 /* Enclose the state transition NotReady->InInit->Ready */
12463 AutoInitSpan autoInitSpan(this);
12464 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12465
12466 HRESULT hrc = S_OK;
12467
12468 RT_ZERO(mAuthLibCtx);
12469
12470 /* create the machine client token */
12471 try
12472 {
12473 mClientToken = new ClientToken(aMachine, this);
12474 if (!mClientToken->isReady())
12475 {
12476 delete mClientToken;
12477 mClientToken = NULL;
12478 hrc = E_FAIL;
12479 }
12480 }
12481 catch (std::bad_alloc &)
12482 {
12483 hrc = E_OUTOFMEMORY;
12484 }
12485 if (FAILED(hrc))
12486 return hrc;
12487
12488 /* memorize the peer Machine */
12489 unconst(mPeer) = aMachine;
12490 /* share the parent pointer */
12491 unconst(mParent) = aMachine->mParent;
12492
12493 /* take the pointers to data to share */
12494 mData.share(aMachine->mData);
12495 mSSData.share(aMachine->mSSData);
12496
12497 mUserData.share(aMachine->mUserData);
12498 mHWData.share(aMachine->mHWData);
12499 mMediumAttachments.share(aMachine->mMediumAttachments);
12500
12501 mStorageControllers.allocate();
12502 for (StorageControllerList::const_iterator
12503 it = aMachine->mStorageControllers->begin();
12504 it != aMachine->mStorageControllers->end();
12505 ++it)
12506 {
12507 ComObjPtr<StorageController> ctl;
12508 ctl.createObject();
12509 ctl->init(this, *it);
12510 mStorageControllers->push_back(ctl);
12511 }
12512
12513 mUSBControllers.allocate();
12514 for (USBControllerList::const_iterator
12515 it = aMachine->mUSBControllers->begin();
12516 it != aMachine->mUSBControllers->end();
12517 ++it)
12518 {
12519 ComObjPtr<USBController> ctl;
12520 ctl.createObject();
12521 ctl->init(this, *it);
12522 mUSBControllers->push_back(ctl);
12523 }
12524
12525 unconst(mPlatformProperties).createObject();
12526 mPlatformProperties->init(mParent);
12527 unconst(mPlatform).createObject();
12528 mPlatform->init(this, aMachine->mPlatform);
12529
12530 i_platformPropertiesUpdate();
12531
12532 unconst(mFirmwareSettings).createObject();
12533 mFirmwareSettings->init(this, aMachine->mFirmwareSettings);
12534
12535 unconst(mRecordingSettings).createObject();
12536 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12537
12538 unconst(mTrustedPlatformModule).createObject();
12539 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12540
12541 unconst(mNvramStore).createObject();
12542 mNvramStore->init(this, aMachine->mNvramStore);
12543
12544 /* create another GraphicsAdapter object that will be mutable */
12545 unconst(mGraphicsAdapter).createObject();
12546 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12547 /* create another VRDEServer object that will be mutable */
12548 unconst(mVRDEServer).createObject();
12549 mVRDEServer->init(this, aMachine->mVRDEServer);
12550 /* create another audio settings object that will be mutable */
12551 unconst(mAudioSettings).createObject();
12552 mAudioSettings->init(this, aMachine->mAudioSettings);
12553 /* create a list of serial ports that will be mutable */
12554 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12555 {
12556 unconst(mSerialPorts[slot]).createObject();
12557 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12558 }
12559 /* create a list of parallel ports that will be mutable */
12560 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12561 {
12562 unconst(mParallelPorts[slot]).createObject();
12563 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12564 }
12565
12566 /* create another USB device filters object that will be mutable */
12567 unconst(mUSBDeviceFilters).createObject();
12568 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12569
12570 /* create a list of network adapters that will be mutable */
12571 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12572 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12573 {
12574 unconst(mNetworkAdapters[slot]).createObject();
12575 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12576 }
12577
12578 /* create another bandwidth control object that will be mutable */
12579 unconst(mBandwidthControl).createObject();
12580 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12581
12582 unconst(mGuestDebugControl).createObject();
12583 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
12584
12585 /* default is to delete saved state on Saved -> PoweredOff transition */
12586 mRemoveSavedState = true;
12587
12588 /* Confirm a successful initialization when it's the case */
12589 autoInitSpan.setSucceeded();
12590
12591 miNATNetworksStarted = 0;
12592
12593 LogFlowThisFuncLeave();
12594 return hrc;
12595}
12596
12597/**
12598 * Uninitializes this session object. If the reason is other than
12599 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12600 * or the client watcher code.
12601 *
12602 * @param aReason uninitialization reason
12603 *
12604 * @note Locks mParent + this object for writing.
12605 */
12606void SessionMachine::uninit(Uninit::Reason aReason)
12607{
12608 LogFlowThisFuncEnter();
12609 LogFlowThisFunc(("reason=%d\n", aReason));
12610
12611 /*
12612 * Strongly reference ourselves to prevent this object deletion after
12613 * mData->mSession.mMachine.setNull() below (which can release the last
12614 * reference and call the destructor). Important: this must be done before
12615 * accessing any members (and before AutoUninitSpan that does it as well).
12616 * This self reference will be released as the very last step on return.
12617 */
12618 ComObjPtr<SessionMachine> selfRef;
12619 if (aReason != Uninit::Unexpected)
12620 selfRef = this;
12621
12622 /* Enclose the state transition Ready->InUninit->NotReady */
12623 AutoUninitSpan autoUninitSpan(this);
12624 if (autoUninitSpan.uninitDone())
12625 {
12626 LogFlowThisFunc(("Already uninitialized\n"));
12627 LogFlowThisFuncLeave();
12628 return;
12629 }
12630
12631 if (autoUninitSpan.initFailed())
12632 {
12633 /* We've been called by init() because it's failed. It's not really
12634 * necessary (nor it's safe) to perform the regular uninit sequence
12635 * below, the following is enough.
12636 */
12637 LogFlowThisFunc(("Initialization failed.\n"));
12638 /* destroy the machine client token */
12639 if (mClientToken)
12640 {
12641 delete mClientToken;
12642 mClientToken = NULL;
12643 }
12644 uninitDataAndChildObjects();
12645 mData.free();
12646 unconst(mParent) = NULL;
12647 unconst(mPeer) = NULL;
12648 LogFlowThisFuncLeave();
12649 return;
12650 }
12651
12652 MachineState_T lastState;
12653 {
12654 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12655 lastState = mData->mMachineState;
12656 }
12657 NOREF(lastState);
12658
12659#ifdef VBOX_WITH_USB
12660 // release all captured USB devices, but do this before requesting the locks below
12661 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12662 {
12663 /* Console::captureUSBDevices() is called in the VM process only after
12664 * setting the machine state to Starting or Restoring.
12665 * Console::detachAllUSBDevices() will be called upon successful
12666 * termination. So, we need to release USB devices only if there was
12667 * an abnormal termination of a running VM.
12668 *
12669 * This is identical to SessionMachine::DetachAllUSBDevices except
12670 * for the aAbnormal argument. */
12671 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12672 AssertComRC(hrc);
12673 NOREF(hrc);
12674
12675 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12676 if (service)
12677 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12678 }
12679#endif /* VBOX_WITH_USB */
12680
12681 /* We need to lock this object in uninit() because the lock is shared
12682 * with mPeer (as well as data we modify below). mParent lock is needed
12683 * by several calls to it.
12684 * We can't use AutoMultiWriteLock2 here as some error code paths
12685 * acquire the machine lock so we need to be able to drop the machine
12686 * lock individually in those cases. */
12687 AutoWriteLock aVBoxLock(mParent COMMA_LOCKVAL_SRC_POS);
12688 AutoWriteLock aMachineLock(this COMMA_LOCKVAL_SRC_POS);
12689
12690#ifdef VBOX_WITH_RESOURCE_USAGE_API
12691 /*
12692 * It is safe to call Machine::i_unregisterMetrics() here because
12693 * PerformanceCollector::samplerCallback no longer accesses guest methods
12694 * holding the lock.
12695 */
12696 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12697 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12698 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12699 if (mCollectorGuest)
12700 {
12701 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12702 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12703 mCollectorGuest = NULL;
12704 }
12705#endif
12706
12707 if (aReason == Uninit::Abnormal)
12708 {
12709 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12710
12711 /*
12712 * Move the VM to the 'Aborted' machine state unless we are restoring a
12713 * VM that was in the 'Saved' machine state. In that case, if the VM
12714 * fails before reaching either the 'Restoring' machine state or the
12715 * 'Running' machine state then we set the machine state to
12716 * 'AbortedSaved' in order to preserve the saved state file so that the
12717 * VM can be restored in the future.
12718 */
12719 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12720 i_setMachineState(MachineState_AbortedSaved);
12721 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12722 i_setMachineState(MachineState_Aborted);
12723 }
12724
12725 // any machine settings modified?
12726 if (mData->flModifications)
12727 {
12728 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12729 aMachineLock.release();
12730 discardSettings();
12731 mParent->i_unmarkRegistryModified(i_getId());
12732 aMachineLock.acquire();
12733 }
12734
12735 mData->mSession.mPID = NIL_RTPROCESS;
12736
12737 if (aReason == Uninit::Unexpected)
12738 {
12739 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12740 * client watcher thread to update the set of machines that have open
12741 * sessions. */
12742 mParent->i_updateClientWatcher();
12743 }
12744
12745 /* uninitialize all remote controls */
12746 if (mData->mSession.mRemoteControls.size())
12747 {
12748 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12749 mData->mSession.mRemoteControls.size()));
12750
12751 /* Always restart a the beginning, since the iterator is invalidated
12752 * by using erase(). */
12753 for (Data::Session::RemoteControlList::iterator
12754 it = mData->mSession.mRemoteControls.begin();
12755 it != mData->mSession.mRemoteControls.end();
12756 it = mData->mSession.mRemoteControls.begin())
12757 {
12758 ComPtr<IInternalSessionControl> pControl = *it;
12759 mData->mSession.mRemoteControls.erase(it);
12760 aMachineLock.release();
12761 aVBoxLock.release();
12762 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12763 HRESULT hrc = pControl->Uninitialize();
12764 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
12765 if (FAILED(hrc))
12766 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12767 aVBoxLock.acquire();
12768 aMachineLock.acquire();
12769 }
12770 mData->mSession.mRemoteControls.clear();
12771 }
12772
12773 /* Remove all references to the NAT network service. The service will stop
12774 * if all references (also from other VMs) are removed. */
12775 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12776 {
12777 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12778 {
12779 BOOL enabled;
12780 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12781 if ( FAILED(hrc)
12782 || !enabled)
12783 continue;
12784
12785 NetworkAttachmentType_T type;
12786 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12787 if ( SUCCEEDED(hrc)
12788 && type == NetworkAttachmentType_NATNetwork)
12789 {
12790 Bstr name;
12791 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12792 if (SUCCEEDED(hrc))
12793 {
12794 aMachineLock.release();
12795 aVBoxLock.release();
12796 Utf8Str strName(name);
12797 LogRel(("VM '%s' stops using NAT network '%s'\n",
12798 mUserData->s.strName.c_str(), strName.c_str()));
12799 mParent->i_natNetworkRefDec(strName);
12800 aVBoxLock.acquire();
12801 aMachineLock.acquire();
12802 }
12803 }
12804 }
12805 }
12806
12807 /*
12808 * An expected uninitialization can come only from #i_checkForDeath().
12809 * Otherwise it means that something's gone really wrong (for example,
12810 * the Session implementation has released the VirtualBox reference
12811 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12812 * etc). However, it's also possible, that the client releases the IPC
12813 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12814 * but the VirtualBox release event comes first to the server process.
12815 * This case is practically possible, so we should not assert on an
12816 * unexpected uninit, just log a warning.
12817 */
12818
12819 if (aReason == Uninit::Unexpected)
12820 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12821
12822 if (aReason != Uninit::Normal)
12823 {
12824 mData->mSession.mDirectControl.setNull();
12825 }
12826 else
12827 {
12828 /* this must be null here (see #OnSessionEnd()) */
12829 Assert(mData->mSession.mDirectControl.isNull());
12830 Assert(mData->mSession.mState == SessionState_Unlocking);
12831 Assert(!mData->mSession.mProgress.isNull());
12832 }
12833 if (mData->mSession.mProgress)
12834 {
12835 if (aReason == Uninit::Normal)
12836 mData->mSession.mProgress->i_notifyComplete(S_OK);
12837 else
12838 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12839 COM_IIDOF(ISession),
12840 getComponentName(),
12841 tr("The VM session was aborted"));
12842 mData->mSession.mProgress.setNull();
12843 }
12844
12845 if (mConsoleTaskData.mProgress)
12846 {
12847 Assert(aReason == Uninit::Abnormal);
12848 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12849 COM_IIDOF(ISession),
12850 getComponentName(),
12851 tr("The VM session was aborted"));
12852 mConsoleTaskData.mProgress.setNull();
12853 }
12854
12855 /* remove the association between the peer machine and this session machine */
12856 Assert( (SessionMachine*)mData->mSession.mMachine == this
12857 || aReason == Uninit::Unexpected);
12858
12859 /* reset the rest of session data */
12860 mData->mSession.mLockType = LockType_Null;
12861 mData->mSession.mMachine.setNull();
12862 mData->mSession.mState = SessionState_Unlocked;
12863 mData->mSession.mName.setNull();
12864
12865 /* destroy the machine client token before leaving the exclusive lock */
12866 if (mClientToken)
12867 {
12868 delete mClientToken;
12869 mClientToken = NULL;
12870 }
12871
12872 /* fire an event */
12873 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12874
12875 uninitDataAndChildObjects();
12876
12877 /* free the essential data structure last */
12878 mData.free();
12879
12880 /* release the exclusive locks before setting the below two to NULL */
12881 aMachineLock.release();
12882 aVBoxLock.release();
12883
12884 unconst(mParent) = NULL;
12885 unconst(mPeer) = NULL;
12886
12887 AuthLibUnload(&mAuthLibCtx);
12888
12889 LogFlowThisFuncLeave();
12890}
12891
12892// util::Lockable interface
12893////////////////////////////////////////////////////////////////////////////////
12894
12895/**
12896 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12897 * with the primary Machine instance (mPeer).
12898 */
12899RWLockHandle *SessionMachine::lockHandle() const
12900{
12901 AssertReturn(mPeer != NULL, NULL);
12902 return mPeer->lockHandle();
12903}
12904
12905// IInternalMachineControl methods
12906////////////////////////////////////////////////////////////////////////////////
12907
12908/**
12909 * Passes collected guest statistics to performance collector object
12910 */
12911HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12912 ULONG aCpuKernel, ULONG aCpuIdle,
12913 ULONG aMemTotal, ULONG aMemFree,
12914 ULONG aMemBalloon, ULONG aMemShared,
12915 ULONG aMemCache, ULONG aPageTotal,
12916 ULONG aAllocVMM, ULONG aFreeVMM,
12917 ULONG aBalloonedVMM, ULONG aSharedVMM,
12918 ULONG aVmNetRx, ULONG aVmNetTx)
12919{
12920#ifdef VBOX_WITH_RESOURCE_USAGE_API
12921 if (mCollectorGuest)
12922 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12923 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12924 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12925 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12926
12927 return S_OK;
12928#else
12929 NOREF(aValidStats);
12930 NOREF(aCpuUser);
12931 NOREF(aCpuKernel);
12932 NOREF(aCpuIdle);
12933 NOREF(aMemTotal);
12934 NOREF(aMemFree);
12935 NOREF(aMemBalloon);
12936 NOREF(aMemShared);
12937 NOREF(aMemCache);
12938 NOREF(aPageTotal);
12939 NOREF(aAllocVMM);
12940 NOREF(aFreeVMM);
12941 NOREF(aBalloonedVMM);
12942 NOREF(aSharedVMM);
12943 NOREF(aVmNetRx);
12944 NOREF(aVmNetTx);
12945 return E_NOTIMPL;
12946#endif
12947}
12948
12949////////////////////////////////////////////////////////////////////////////////
12950//
12951// SessionMachine task records
12952//
12953////////////////////////////////////////////////////////////////////////////////
12954
12955/**
12956 * Task record for saving the machine state.
12957 */
12958class SessionMachine::SaveStateTask
12959 : public Machine::Task
12960{
12961public:
12962 SaveStateTask(SessionMachine *m,
12963 Progress *p,
12964 const Utf8Str &t,
12965 Reason_T enmReason,
12966 const Utf8Str &strStateFilePath)
12967 : Task(m, p, t),
12968 m_enmReason(enmReason),
12969 m_strStateFilePath(strStateFilePath)
12970 {}
12971
12972private:
12973 void handler()
12974 {
12975 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12976 }
12977
12978 Reason_T m_enmReason;
12979 Utf8Str m_strStateFilePath;
12980
12981 friend class SessionMachine;
12982};
12983
12984/**
12985 * Task thread implementation for SessionMachine::SaveState(), called from
12986 * SessionMachine::taskHandler().
12987 *
12988 * @note Locks this object for writing.
12989 *
12990 * @param task
12991 */
12992void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12993{
12994 LogFlowThisFuncEnter();
12995
12996 AutoCaller autoCaller(this);
12997 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12998 if (FAILED(autoCaller.hrc()))
12999 {
13000 /* we might have been uninitialized because the session was accidentally
13001 * closed by the client, so don't assert */
13002 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
13003 task.m_pProgress->i_notifyComplete(hrc);
13004 LogFlowThisFuncLeave();
13005 return;
13006 }
13007
13008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13009
13010 HRESULT hrc = S_OK;
13011
13012 try
13013 {
13014 ComPtr<IInternalSessionControl> directControl;
13015 if (mData->mSession.mLockType == LockType_VM)
13016 directControl = mData->mSession.mDirectControl;
13017 if (directControl.isNull())
13018 throw setError(VBOX_E_INVALID_VM_STATE,
13019 tr("Trying to save state without a running VM"));
13020 alock.release();
13021 BOOL fSuspendedBySave;
13022 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13023 Assert(!fSuspendedBySave);
13024 alock.acquire();
13025
13026 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
13027 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
13028 throw E_FAIL);
13029
13030 if (SUCCEEDED(hrc))
13031 {
13032 mSSData->strStateFilePath = task.m_strStateFilePath;
13033
13034 /* save all VM settings */
13035 hrc = i_saveSettings(NULL, alock);
13036 // no need to check whether VirtualBox.xml needs saving also since
13037 // we can't have a name change pending at this point
13038 }
13039 else
13040 {
13041 // On failure, set the state to the state we had at the beginning.
13042 i_setMachineState(task.m_machineStateBackup);
13043 i_updateMachineStateOnClient();
13044
13045 // Delete the saved state file (might have been already created).
13046 // No need to check whether this is shared with a snapshot here
13047 // because we certainly created a fresh saved state file here.
13048 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13049 }
13050 }
13051 catch (HRESULT hrcXcpt)
13052 {
13053 hrc = hrcXcpt;
13054 }
13055
13056 task.m_pProgress->i_notifyComplete(hrc);
13057
13058 LogFlowThisFuncLeave();
13059}
13060
13061/**
13062 * @note Locks this object for writing.
13063 */
13064HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13065{
13066 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13067}
13068
13069HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13070{
13071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13072
13073 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13074 if (FAILED(hrc)) return hrc;
13075
13076 if ( mData->mMachineState != MachineState_Running
13077 && mData->mMachineState != MachineState_Paused
13078 )
13079 return setError(VBOX_E_INVALID_VM_STATE,
13080 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13081 Global::stringifyMachineState(mData->mMachineState));
13082
13083 ComObjPtr<Progress> pProgress;
13084 pProgress.createObject();
13085 hrc = pProgress->init(i_getVirtualBox(),
13086 static_cast<IMachine *>(this) /* aInitiator */,
13087 tr("Saving the execution state of the virtual machine"),
13088 FALSE /* aCancelable */);
13089 if (FAILED(hrc))
13090 return hrc;
13091
13092 Utf8Str strStateFilePath;
13093 i_composeSavedStateFilename(strStateFilePath);
13094
13095 /* create and start the task on a separate thread (note that it will not
13096 * start working until we release alock) */
13097 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13098 hrc = pTask->createThread();
13099 if (FAILED(hrc))
13100 return hrc;
13101
13102 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13103 i_setMachineState(MachineState_Saving);
13104 i_updateMachineStateOnClient();
13105
13106 pProgress.queryInterfaceTo(aProgress.asOutParam());
13107
13108 return S_OK;
13109}
13110
13111/**
13112 * @note Locks this object for writing.
13113 */
13114HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13115{
13116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13117
13118 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13119 if (FAILED(hrc)) return hrc;
13120
13121 if ( mData->mMachineState != MachineState_PoweredOff
13122 && mData->mMachineState != MachineState_Teleported
13123 && mData->mMachineState != MachineState_Aborted
13124 )
13125 return setError(VBOX_E_INVALID_VM_STATE,
13126 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13127 Global::stringifyMachineState(mData->mMachineState));
13128
13129 com::Utf8Str stateFilePathFull;
13130 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13131 if (RT_FAILURE(vrc))
13132 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13133 tr("Invalid saved state file path '%s' (%Rrc)"),
13134 aSavedStateFile.c_str(),
13135 vrc);
13136
13137 mSSData->strStateFilePath = stateFilePathFull;
13138
13139 /* The below i_setMachineState() will detect the state transition and will
13140 * update the settings file */
13141
13142 return i_setMachineState(MachineState_Saved);
13143}
13144
13145/**
13146 * @note Locks this object for writing.
13147 */
13148HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13149{
13150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13151
13152 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13153 if (FAILED(hrc)) return hrc;
13154
13155 if ( mData->mMachineState != MachineState_Saved
13156 && mData->mMachineState != MachineState_AbortedSaved)
13157 return setError(VBOX_E_INVALID_VM_STATE,
13158 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13159 Global::stringifyMachineState(mData->mMachineState));
13160
13161 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13162
13163 /*
13164 * Saved -> PoweredOff transition will be detected in the SessionMachine
13165 * and properly handled.
13166 */
13167 hrc = i_setMachineState(MachineState_PoweredOff);
13168 return hrc;
13169}
13170
13171
13172/**
13173 * @note Locks the same as #i_setMachineState() does.
13174 */
13175HRESULT SessionMachine::updateState(MachineState_T aState)
13176{
13177 return i_setMachineState(aState);
13178}
13179
13180/**
13181 * @note Locks this object for writing.
13182 */
13183HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13184{
13185 IProgress *pProgress(aProgress);
13186
13187 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13188
13189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13190
13191 if (mData->mSession.mState != SessionState_Locked)
13192 return VBOX_E_INVALID_OBJECT_STATE;
13193
13194 if (!mData->mSession.mProgress.isNull())
13195 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13196
13197 /* If we didn't reference the NAT network service yet, add a reference to
13198 * force a start */
13199 if (miNATNetworksStarted < 1)
13200 {
13201 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13202 {
13203 BOOL enabled;
13204 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13205 if ( FAILED(hrc)
13206 || !enabled)
13207 continue;
13208
13209 NetworkAttachmentType_T type;
13210 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13211 if ( SUCCEEDED(hrc)
13212 && type == NetworkAttachmentType_NATNetwork)
13213 {
13214 Bstr name;
13215 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13216 if (SUCCEEDED(hrc))
13217 {
13218 Utf8Str strName(name);
13219 LogRel(("VM '%s' starts using NAT network '%s'\n",
13220 mUserData->s.strName.c_str(), strName.c_str()));
13221 mPeer->lockHandle()->unlockWrite();
13222 mParent->i_natNetworkRefInc(strName);
13223#ifdef RT_LOCK_STRICT
13224 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13225#else
13226 mPeer->lockHandle()->lockWrite();
13227#endif
13228 }
13229 }
13230 }
13231 miNATNetworksStarted++;
13232 }
13233
13234 LogFlowThisFunc(("returns S_OK.\n"));
13235 return S_OK;
13236}
13237
13238/**
13239 * @note Locks this object for writing.
13240 */
13241HRESULT SessionMachine::endPowerUp(LONG aResult)
13242{
13243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13244
13245 if (mData->mSession.mState != SessionState_Locked)
13246 return VBOX_E_INVALID_OBJECT_STATE;
13247
13248 /* Finalize the LaunchVMProcess progress object. */
13249 if (mData->mSession.mProgress)
13250 {
13251 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13252 mData->mSession.mProgress.setNull();
13253 }
13254
13255 if (SUCCEEDED((HRESULT)aResult))
13256 {
13257#ifdef VBOX_WITH_RESOURCE_USAGE_API
13258 /* The VM has been powered up successfully, so it makes sense
13259 * now to offer the performance metrics for a running machine
13260 * object. Doing it earlier wouldn't be safe. */
13261 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13262 mData->mSession.mPID);
13263#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13264 }
13265
13266 return S_OK;
13267}
13268
13269/**
13270 * @note Locks this object for writing.
13271 */
13272HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13273{
13274 LogFlowThisFuncEnter();
13275
13276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13277
13278 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13279 E_FAIL);
13280
13281 /* create a progress object to track operation completion */
13282 ComObjPtr<Progress> pProgress;
13283 pProgress.createObject();
13284 pProgress->init(i_getVirtualBox(),
13285 static_cast<IMachine *>(this) /* aInitiator */,
13286 tr("Stopping the virtual machine"),
13287 FALSE /* aCancelable */);
13288
13289 /* fill in the console task data */
13290 mConsoleTaskData.mLastState = mData->mMachineState;
13291 mConsoleTaskData.mProgress = pProgress;
13292
13293 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13294 i_setMachineState(MachineState_Stopping);
13295
13296 pProgress.queryInterfaceTo(aProgress.asOutParam());
13297
13298 return S_OK;
13299}
13300
13301/**
13302 * @note Locks this object for writing.
13303 */
13304HRESULT SessionMachine::endPoweringDown(LONG aResult,
13305 const com::Utf8Str &aErrMsg)
13306{
13307 HRESULT const hrcResult = (HRESULT)aResult;
13308 LogFlowThisFuncEnter();
13309
13310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13311
13312 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13313 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13314 && mConsoleTaskData.mLastState != MachineState_Null,
13315 E_FAIL);
13316
13317 /*
13318 * On failure, set the state to the state we had when BeginPoweringDown()
13319 * was called (this is expected by Console::PowerDown() and the associated
13320 * task). On success the VM process already changed the state to
13321 * MachineState_PoweredOff, so no need to do anything.
13322 */
13323 if (FAILED(hrcResult))
13324 i_setMachineState(mConsoleTaskData.mLastState);
13325
13326 /* notify the progress object about operation completion */
13327 Assert(mConsoleTaskData.mProgress);
13328 if (SUCCEEDED(hrcResult))
13329 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13330 else
13331 {
13332 if (aErrMsg.length())
13333 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13334 COM_IIDOF(ISession),
13335 getComponentName(),
13336 aErrMsg.c_str());
13337 else
13338 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13339 }
13340
13341 /* clear out the temporary saved state data */
13342 mConsoleTaskData.mLastState = MachineState_Null;
13343 mConsoleTaskData.mProgress.setNull();
13344
13345 LogFlowThisFuncLeave();
13346 return S_OK;
13347}
13348
13349
13350/**
13351 * Goes through the USB filters of the given machine to see if the given
13352 * device matches any filter or not.
13353 *
13354 * @note Locks the same as USBController::hasMatchingFilter() does.
13355 */
13356HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13357 BOOL *aMatched,
13358 ULONG *aMaskedInterfaces)
13359{
13360 LogFlowThisFunc(("\n"));
13361
13362#ifdef VBOX_WITH_USB
13363 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13364#else
13365 NOREF(aDevice);
13366 NOREF(aMaskedInterfaces);
13367 *aMatched = FALSE;
13368#endif
13369
13370 return S_OK;
13371}
13372
13373/**
13374 * @note Locks the same as Host::captureUSBDevice() does.
13375 */
13376HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380#ifdef VBOX_WITH_USB
13381 /* if captureDeviceForVM() fails, it must have set extended error info */
13382 clearError();
13383 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13384 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13385 return hrc;
13386
13387 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13388 AssertReturn(service, E_FAIL);
13389 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13390#else
13391 RT_NOREF(aId, aCaptureFilename);
13392 return E_NOTIMPL;
13393#endif
13394}
13395
13396/**
13397 * @note Locks the same as Host::detachUSBDevice() does.
13398 */
13399HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13400 BOOL aDone)
13401{
13402 LogFlowThisFunc(("\n"));
13403
13404#ifdef VBOX_WITH_USB
13405 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13406 AssertReturn(service, E_FAIL);
13407 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13408#else
13409 NOREF(aId);
13410 NOREF(aDone);
13411 return E_NOTIMPL;
13412#endif
13413}
13414
13415/**
13416 * Inserts all machine filters to the USB proxy service and then calls
13417 * Host::autoCaptureUSBDevices().
13418 *
13419 * Called by Console from the VM process upon VM startup.
13420 *
13421 * @note Locks what called methods lock.
13422 */
13423HRESULT SessionMachine::autoCaptureUSBDevices()
13424{
13425 LogFlowThisFunc(("\n"));
13426
13427#ifdef VBOX_WITH_USB
13428 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13429 AssertComRC(hrc);
13430 NOREF(hrc);
13431
13432 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13433 AssertReturn(service, E_FAIL);
13434 return service->autoCaptureDevicesForVM(this);
13435#else
13436 return S_OK;
13437#endif
13438}
13439
13440/**
13441 * Removes all machine filters from the USB proxy service and then calls
13442 * Host::detachAllUSBDevices().
13443 *
13444 * Called by Console from the VM process upon normal VM termination or by
13445 * SessionMachine::uninit() upon abnormal VM termination (from under the
13446 * Machine/SessionMachine lock).
13447 *
13448 * @note Locks what called methods lock.
13449 */
13450HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13451{
13452 LogFlowThisFunc(("\n"));
13453
13454#ifdef VBOX_WITH_USB
13455 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13456 AssertComRC(hrc);
13457 NOREF(hrc);
13458
13459 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13460 AssertReturn(service, E_FAIL);
13461 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13462#else
13463 NOREF(aDone);
13464 return S_OK;
13465#endif
13466}
13467
13468/**
13469 * @note Locks this object for writing.
13470 */
13471HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13472 ComPtr<IProgress> &aProgress)
13473{
13474 LogFlowThisFuncEnter();
13475
13476 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13477 /*
13478 * We don't assert below because it might happen that a non-direct session
13479 * informs us it is closed right after we've been uninitialized -- it's ok.
13480 */
13481
13482 /* get IInternalSessionControl interface */
13483 ComPtr<IInternalSessionControl> control(aSession);
13484
13485 ComAssertRet(!control.isNull(), E_INVALIDARG);
13486
13487 /* Creating a Progress object requires the VirtualBox lock, and
13488 * thus locking it here is required by the lock order rules. */
13489 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13490
13491 if (control == mData->mSession.mDirectControl)
13492 {
13493 /* The direct session is being normally closed by the client process
13494 * ----------------------------------------------------------------- */
13495
13496 /* go to the closing state (essential for all open*Session() calls and
13497 * for #i_checkForDeath()) */
13498 Assert(mData->mSession.mState == SessionState_Locked);
13499 mData->mSession.mState = SessionState_Unlocking;
13500
13501 /* set direct control to NULL to release the remote instance */
13502 mData->mSession.mDirectControl.setNull();
13503 LogFlowThisFunc(("Direct control is set to NULL\n"));
13504
13505 if (mData->mSession.mProgress)
13506 {
13507 /* finalize the progress, someone might wait if a frontend
13508 * closes the session before powering on the VM. */
13509 mData->mSession.mProgress->notifyComplete(E_FAIL,
13510 COM_IIDOF(ISession),
13511 getComponentName(),
13512 tr("The VM session was closed before any attempt to power it on"));
13513 mData->mSession.mProgress.setNull();
13514 }
13515
13516 /* Create the progress object the client will use to wait until
13517 * #i_checkForDeath() is called to uninitialize this session object after
13518 * it releases the IPC semaphore.
13519 * Note! Because we're "reusing" mProgress here, this must be a proxy
13520 * object just like for LaunchVMProcess. */
13521 Assert(mData->mSession.mProgress.isNull());
13522 ComObjPtr<ProgressProxy> progress;
13523 progress.createObject();
13524 ComPtr<IUnknown> pPeer(mPeer);
13525 progress->init(mParent, pPeer,
13526 Bstr(tr("Closing session")).raw(),
13527 FALSE /* aCancelable */);
13528 progress.queryInterfaceTo(aProgress.asOutParam());
13529 mData->mSession.mProgress = progress;
13530 }
13531 else
13532 {
13533 /* the remote session is being normally closed */
13534 bool found = false;
13535 for (Data::Session::RemoteControlList::iterator
13536 it = mData->mSession.mRemoteControls.begin();
13537 it != mData->mSession.mRemoteControls.end();
13538 ++it)
13539 {
13540 if (control == *it)
13541 {
13542 found = true;
13543 // This MUST be erase(it), not remove(*it) as the latter
13544 // triggers a very nasty use after free due to the place where
13545 // the value "lives".
13546 mData->mSession.mRemoteControls.erase(it);
13547 break;
13548 }
13549 }
13550 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13551 E_INVALIDARG);
13552 }
13553
13554 /* signal the client watcher thread, because the client is going away */
13555 mParent->i_updateClientWatcher();
13556
13557 LogFlowThisFuncLeave();
13558 return S_OK;
13559}
13560
13561HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13562 std::vector<com::Utf8Str> &aValues,
13563 std::vector<LONG64> &aTimestamps,
13564 std::vector<com::Utf8Str> &aFlags)
13565{
13566 LogFlowThisFunc(("\n"));
13567
13568#ifdef VBOX_WITH_GUEST_PROPS
13569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13570
13571 size_t cEntries = mHWData->mGuestProperties.size();
13572 aNames.resize(cEntries);
13573 aValues.resize(cEntries);
13574 aTimestamps.resize(cEntries);
13575 aFlags.resize(cEntries);
13576
13577 size_t i = 0;
13578 for (HWData::GuestPropertyMap::const_iterator
13579 it = mHWData->mGuestProperties.begin();
13580 it != mHWData->mGuestProperties.end();
13581 ++it, ++i)
13582 {
13583 aNames[i] = it->first;
13584 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
13585 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13586
13587 aValues[i] = it->second.strValue;
13588 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
13589 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13590
13591 aTimestamps[i] = it->second.mTimestamp;
13592
13593 /* If it is NULL, keep it NULL. */
13594 if (it->second.mFlags)
13595 {
13596 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13597 GuestPropWriteFlags(it->second.mFlags, szFlags);
13598 aFlags[i] = szFlags;
13599 }
13600 else
13601 aFlags[i] = "";
13602 }
13603 return S_OK;
13604#else
13605 ReturnComNotImplemented();
13606#endif
13607}
13608
13609HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13610 const com::Utf8Str &aValue,
13611 LONG64 aTimestamp,
13612 const com::Utf8Str &aFlags,
13613 BOOL fWasDeleted)
13614{
13615 LogFlowThisFunc(("\n"));
13616
13617#ifdef VBOX_WITH_GUEST_PROPS
13618 try
13619 {
13620 /*
13621 * Convert input up front.
13622 */
13623 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13624 if (aFlags.length())
13625 {
13626 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13627 AssertRCReturn(vrc, E_INVALIDARG);
13628 }
13629
13630 /*
13631 * Now grab the object lock, validate the state and do the update.
13632 */
13633
13634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13635
13636 if (!Global::IsOnline(mData->mMachineState))
13637 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13638
13639 i_setModified(IsModified_MachineData);
13640 mHWData.backup();
13641
13642 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13643 if (it != mHWData->mGuestProperties.end())
13644 {
13645 if (!fWasDeleted)
13646 {
13647 it->second.strValue = aValue;
13648 it->second.mTimestamp = aTimestamp;
13649 it->second.mFlags = fFlags;
13650 }
13651 else
13652 mHWData->mGuestProperties.erase(it);
13653
13654 mData->mGuestPropertiesModified = TRUE;
13655 }
13656 else if (!fWasDeleted)
13657 {
13658 HWData::GuestProperty prop;
13659 prop.strValue = aValue;
13660 prop.mTimestamp = aTimestamp;
13661 prop.mFlags = fFlags;
13662
13663 mHWData->mGuestProperties[aName] = prop;
13664 mData->mGuestPropertiesModified = TRUE;
13665 }
13666
13667 alock.release();
13668
13669 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13670 }
13671 catch (...)
13672 {
13673 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13674 }
13675 return S_OK;
13676#else
13677 ReturnComNotImplemented();
13678#endif
13679}
13680
13681
13682HRESULT SessionMachine::lockMedia()
13683{
13684 AutoMultiWriteLock2 alock(this->lockHandle(),
13685 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13686
13687 AssertReturn( mData->mMachineState == MachineState_Starting
13688 || mData->mMachineState == MachineState_Restoring
13689 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13690
13691 clearError();
13692 alock.release();
13693 return i_lockMedia();
13694}
13695
13696HRESULT SessionMachine::unlockMedia()
13697{
13698 HRESULT hrc = i_unlockMedia();
13699 return hrc;
13700}
13701
13702HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13703 ComPtr<IMediumAttachment> &aNewAttachment)
13704{
13705 // request the host lock first, since might be calling Host methods for getting host drives;
13706 // next, protect the media tree all the while we're in here, as well as our member variables
13707 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13708 this->lockHandle(),
13709 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13710
13711 IMediumAttachment *iAttach = aAttachment;
13712 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13713
13714 Utf8Str ctrlName;
13715 LONG lPort;
13716 LONG lDevice;
13717 bool fTempEject;
13718 {
13719 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13720
13721 /* Need to query the details first, as the IMediumAttachment reference
13722 * might be to the original settings, which we are going to change. */
13723 ctrlName = pAttach->i_getControllerName();
13724 lPort = pAttach->i_getPort();
13725 lDevice = pAttach->i_getDevice();
13726 fTempEject = pAttach->i_getTempEject();
13727 }
13728
13729 if (!fTempEject)
13730 {
13731 /* Remember previously mounted medium. The medium before taking the
13732 * backup is not necessarily the same thing. */
13733 ComObjPtr<Medium> oldmedium;
13734 oldmedium = pAttach->i_getMedium();
13735
13736 i_setModified(IsModified_Storage);
13737 mMediumAttachments.backup();
13738
13739 // The backup operation makes the pAttach reference point to the
13740 // old settings. Re-get the correct reference.
13741 pAttach = i_findAttachment(*mMediumAttachments.data(),
13742 ctrlName,
13743 lPort,
13744 lDevice);
13745
13746 {
13747 AutoCaller autoAttachCaller(this);
13748 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
13749
13750 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13751 if (!oldmedium.isNull())
13752 oldmedium->i_removeBackReference(mData->mUuid);
13753
13754 pAttach->i_updateMedium(NULL);
13755 pAttach->i_updateEjected();
13756 }
13757
13758 i_setModified(IsModified_Storage);
13759 }
13760 else
13761 {
13762 {
13763 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13764 pAttach->i_updateEjected();
13765 }
13766 }
13767
13768 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13769
13770 return S_OK;
13771}
13772
13773HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13774 com::Utf8Str &aResult)
13775{
13776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13777
13778 HRESULT hrc = S_OK;
13779
13780 if (!mAuthLibCtx.hAuthLibrary)
13781 {
13782 /* Load the external authentication library. */
13783 Bstr authLibrary;
13784 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13785
13786 Utf8Str filename = authLibrary;
13787
13788 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13789 if (RT_FAILURE(vrc))
13790 hrc = setErrorBoth(E_FAIL, vrc,
13791 tr("Could not load the external authentication library '%s' (%Rrc)"),
13792 filename.c_str(), vrc);
13793 }
13794
13795 /* The auth library might need the machine lock. */
13796 alock.release();
13797
13798 if (FAILED(hrc))
13799 return hrc;
13800
13801 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13802 {
13803 enum VRDEAuthParams
13804 {
13805 parmUuid = 1,
13806 parmGuestJudgement,
13807 parmUser,
13808 parmPassword,
13809 parmDomain,
13810 parmClientId
13811 };
13812
13813 AuthResult result = AuthResultAccessDenied;
13814
13815 Guid uuid(aAuthParams[parmUuid]);
13816 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13817 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13818
13819 result = AuthLibAuthenticate(&mAuthLibCtx,
13820 uuid.raw(), guestJudgement,
13821 aAuthParams[parmUser].c_str(),
13822 aAuthParams[parmPassword].c_str(),
13823 aAuthParams[parmDomain].c_str(),
13824 u32ClientId);
13825
13826 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13827 size_t cbPassword = aAuthParams[parmPassword].length();
13828 if (cbPassword)
13829 {
13830 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13831 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13832 }
13833
13834 if (result == AuthResultAccessGranted)
13835 aResult = "granted";
13836 else
13837 aResult = "denied";
13838
13839 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13840 aAuthParams[parmUser].c_str(), aResult.c_str()));
13841 }
13842 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13843 {
13844 enum VRDEAuthDisconnectParams
13845 {
13846 parmUuid = 1,
13847 parmClientId
13848 };
13849
13850 Guid uuid(aAuthParams[parmUuid]);
13851 uint32_t u32ClientId = 0;
13852 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13853 }
13854 else
13855 {
13856 hrc = E_INVALIDARG;
13857 }
13858
13859 return hrc;
13860}
13861
13862// public methods only for internal purposes
13863/////////////////////////////////////////////////////////////////////////////
13864
13865#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13866/**
13867 * Called from the client watcher thread to check for expected or unexpected
13868 * death of the client process that has a direct session to this machine.
13869 *
13870 * On Win32 and on OS/2, this method is called only when we've got the
13871 * mutex (i.e. the client has either died or terminated normally) so it always
13872 * returns @c true (the client is terminated, the session machine is
13873 * uninitialized).
13874 *
13875 * On other platforms, the method returns @c true if the client process has
13876 * terminated normally or abnormally and the session machine was uninitialized,
13877 * and @c false if the client process is still alive.
13878 *
13879 * @note Locks this object for writing.
13880 */
13881bool SessionMachine::i_checkForDeath()
13882{
13883 Uninit::Reason reason;
13884 bool terminated = false;
13885
13886 /* Enclose autoCaller with a block because calling uninit() from under it
13887 * will deadlock. */
13888 {
13889 AutoCaller autoCaller(this);
13890 if (!autoCaller.isOk())
13891 {
13892 /* return true if not ready, to cause the client watcher to exclude
13893 * the corresponding session from watching */
13894 LogFlowThisFunc(("Already uninitialized!\n"));
13895 return true;
13896 }
13897
13898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13899
13900 /* Determine the reason of death: if the session state is Closing here,
13901 * everything is fine. Otherwise it means that the client did not call
13902 * OnSessionEnd() before it released the IPC semaphore. This may happen
13903 * either because the client process has abnormally terminated, or
13904 * because it simply forgot to call ISession::Close() before exiting. We
13905 * threat the latter also as an abnormal termination (see
13906 * Session::uninit() for details). */
13907 reason = mData->mSession.mState == SessionState_Unlocking ?
13908 Uninit::Normal :
13909 Uninit::Abnormal;
13910
13911 if (mClientToken)
13912 terminated = mClientToken->release();
13913 } /* AutoCaller block */
13914
13915 if (terminated)
13916 uninit(reason);
13917
13918 return terminated;
13919}
13920
13921void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13922{
13923 LogFlowThisFunc(("\n"));
13924
13925 strTokenId.setNull();
13926
13927 AutoCaller autoCaller(this);
13928 AssertComRCReturnVoid(autoCaller.hrc());
13929
13930 Assert(mClientToken);
13931 if (mClientToken)
13932 mClientToken->getId(strTokenId);
13933}
13934#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13935IToken *SessionMachine::i_getToken()
13936{
13937 LogFlowThisFunc(("\n"));
13938
13939 AutoCaller autoCaller(this);
13940 AssertComRCReturn(autoCaller.hrc(), NULL);
13941
13942 Assert(mClientToken);
13943 if (mClientToken)
13944 return mClientToken->getToken();
13945 else
13946 return NULL;
13947}
13948#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13949
13950Machine::ClientToken *SessionMachine::i_getClientToken()
13951{
13952 LogFlowThisFunc(("\n"));
13953
13954 AutoCaller autoCaller(this);
13955 AssertComRCReturn(autoCaller.hrc(), NULL);
13956
13957 return mClientToken;
13958}
13959
13960
13961/**
13962 * @note Locks this object for reading.
13963 */
13964HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13965{
13966 LogFlowThisFunc(("\n"));
13967
13968 AutoCaller autoCaller(this);
13969 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13970
13971 ComPtr<IInternalSessionControl> directControl;
13972 {
13973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13974 if (mData->mSession.mLockType == LockType_VM)
13975 directControl = mData->mSession.mDirectControl;
13976 }
13977
13978 /* ignore notifications sent after #OnSessionEnd() is called */
13979 if (!directControl)
13980 return S_OK;
13981
13982 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13983}
13984
13985/**
13986 * @note Locks this object for reading.
13987 */
13988HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13989 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13990 const Utf8Str &aGuestIp, LONG aGuestPort)
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994 AutoCaller autoCaller(this);
13995 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13996
13997 ComPtr<IInternalSessionControl> directControl;
13998 {
13999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14000 if (mData->mSession.mLockType == LockType_VM)
14001 directControl = mData->mSession.mDirectControl;
14002 }
14003
14004 /* ignore notifications sent after #OnSessionEnd() is called */
14005 if (!directControl)
14006 return S_OK;
14007 /*
14008 * instead acting like callback we ask IVirtualBox deliver corresponding event
14009 */
14010
14011 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14012 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14013 return S_OK;
14014}
14015
14016/**
14017 * @note Locks this object for reading.
14018 */
14019HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14020{
14021 LogFlowThisFunc(("\n"));
14022
14023 AutoCaller autoCaller(this);
14024 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14025
14026 ComPtr<IInternalSessionControl> directControl;
14027 {
14028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14029 if (mData->mSession.mLockType == LockType_VM)
14030 directControl = mData->mSession.mDirectControl;
14031 }
14032
14033 /* ignore notifications sent after #OnSessionEnd() is called */
14034 if (!directControl)
14035 return S_OK;
14036
14037 return directControl->OnAudioAdapterChange(audioAdapter);
14038}
14039
14040/**
14041 * @note Locks this object for reading.
14042 */
14043HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14044{
14045 LogFlowThisFunc(("\n"));
14046
14047 AutoCaller autoCaller(this);
14048 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14049
14050 ComPtr<IInternalSessionControl> directControl;
14051 {
14052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14053 if (mData->mSession.mLockType == LockType_VM)
14054 directControl = mData->mSession.mDirectControl;
14055 }
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14062}
14063
14064/**
14065 * @note Locks this object for reading.
14066 */
14067HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14068{
14069 LogFlowThisFunc(("\n"));
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 if (mData->mSession.mLockType == LockType_VM)
14078 directControl = mData->mSession.mDirectControl;
14079 }
14080
14081 /* ignore notifications sent after #OnSessionEnd() is called */
14082 if (!directControl)
14083 return S_OK;
14084
14085 return directControl->OnSerialPortChange(serialPort);
14086}
14087
14088/**
14089 * @note Locks this object for reading.
14090 */
14091HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14092{
14093 LogFlowThisFunc(("\n"));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14097
14098 ComPtr<IInternalSessionControl> directControl;
14099 {
14100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14101 if (mData->mSession.mLockType == LockType_VM)
14102 directControl = mData->mSession.mDirectControl;
14103 }
14104
14105 /* ignore notifications sent after #OnSessionEnd() is called */
14106 if (!directControl)
14107 return S_OK;
14108
14109 return directControl->OnParallelPortChange(parallelPort);
14110}
14111
14112/**
14113 * @note Locks this object for reading.
14114 */
14115HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14116{
14117 LogFlowThisFunc(("\n"));
14118
14119 AutoCaller autoCaller(this);
14120 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14121
14122 ComPtr<IInternalSessionControl> directControl;
14123 {
14124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14125 if (mData->mSession.mLockType == LockType_VM)
14126 directControl = mData->mSession.mDirectControl;
14127 }
14128
14129 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14130
14131 /* ignore notifications sent after #OnSessionEnd() is called */
14132 if (!directControl)
14133 return S_OK;
14134
14135 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14136}
14137
14138/**
14139 * @note Locks this object for reading.
14140 */
14141HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14142{
14143 LogFlowThisFunc(("\n"));
14144
14145 AutoCaller autoCaller(this);
14146 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 if (mData->mSession.mLockType == LockType_VM)
14152 directControl = mData->mSession.mDirectControl;
14153 }
14154
14155 mParent->i_onMediumChanged(aAttachment);
14156
14157 /* ignore notifications sent after #OnSessionEnd() is called */
14158 if (!directControl)
14159 return S_OK;
14160
14161 return directControl->OnMediumChange(aAttachment, aForce);
14162}
14163
14164HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14165{
14166 LogFlowThisFunc(("\n"));
14167
14168 AutoCaller autoCaller(this);
14169 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14170
14171 ComPtr<IInternalSessionControl> directControl;
14172 {
14173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14174 if (mData->mSession.mLockType == LockType_VM)
14175 directControl = mData->mSession.mDirectControl;
14176 }
14177
14178 /* ignore notifications sent after #OnSessionEnd() is called */
14179 if (!directControl)
14180 return S_OK;
14181
14182 return directControl->OnVMProcessPriorityChange(aPriority);
14183}
14184
14185/**
14186 * @note Locks this object for reading.
14187 */
14188HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14189{
14190 LogFlowThisFunc(("\n"));
14191
14192 AutoCaller autoCaller(this);
14193 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14194
14195 ComPtr<IInternalSessionControl> directControl;
14196 {
14197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14198 if (mData->mSession.mLockType == LockType_VM)
14199 directControl = mData->mSession.mDirectControl;
14200 }
14201
14202 /* ignore notifications sent after #OnSessionEnd() is called */
14203 if (!directControl)
14204 return S_OK;
14205
14206 return directControl->OnCPUChange(aCPU, aRemove);
14207}
14208
14209HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14210{
14211 LogFlowThisFunc(("\n"));
14212
14213 AutoCaller autoCaller(this);
14214 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14215
14216 ComPtr<IInternalSessionControl> directControl;
14217 {
14218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14219 if (mData->mSession.mLockType == LockType_VM)
14220 directControl = mData->mSession.mDirectControl;
14221 }
14222
14223 /* ignore notifications sent after #OnSessionEnd() is called */
14224 if (!directControl)
14225 return S_OK;
14226
14227 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14228}
14229
14230/**
14231 * @note Locks this object for reading.
14232 */
14233HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14234{
14235 LogFlowThisFunc(("\n"));
14236
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 if (mData->mSession.mLockType == LockType_VM)
14244 directControl = mData->mSession.mDirectControl;
14245 }
14246
14247 /* ignore notifications sent after #OnSessionEnd() is called */
14248 if (!directControl)
14249 return S_OK;
14250
14251 return directControl->OnVRDEServerChange(aRestart);
14252}
14253
14254/**
14255 * @note Caller needs to take the machine's lock if needed.
14256 */
14257HRESULT SessionMachine::i_onRecordingStateChange(BOOL aEnable, IProgress **aProgress)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 if (mData->mSession.mLockType == LockType_VM)
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 /* ignore notifications sent after #OnSessionEnd() is called */
14271 if (!directControl)
14272 return S_OK;
14273
14274 return directControl->OnRecordingStateChange(aEnable, aProgress);
14275}
14276
14277/**
14278 * @note Locks this object for reading.
14279 */
14280HRESULT SessionMachine::i_onRecordingScreenStateChange(BOOL aEnable, ULONG aScreen)
14281{
14282 LogFlowThisFunc(("\n"));
14283
14284 AutoCaller autoCaller(this);
14285 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14286
14287 ComPtr<IInternalSessionControl> directControl;
14288 {
14289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14290 if (mData->mSession.mLockType == LockType_VM)
14291 directControl = mData->mSession.mDirectControl;
14292 }
14293
14294 /* ignore notifications sent after #OnSessionEnd() is called */
14295 if (!directControl)
14296 return S_OK;
14297
14298 return directControl->OnRecordingScreenStateChange(aEnable, aScreen);
14299}
14300
14301/**
14302 * @note Locks this object for reading.
14303 */
14304HRESULT SessionMachine::i_onUSBControllerChange()
14305{
14306 LogFlowThisFunc(("\n"));
14307
14308 AutoCaller autoCaller(this);
14309 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14310
14311 ComPtr<IInternalSessionControl> directControl;
14312 {
14313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14314 if (mData->mSession.mLockType == LockType_VM)
14315 directControl = mData->mSession.mDirectControl;
14316 }
14317
14318 /* ignore notifications sent after #OnSessionEnd() is called */
14319 if (!directControl)
14320 return S_OK;
14321
14322 return directControl->OnUSBControllerChange();
14323}
14324
14325/**
14326 * @note Locks this object for reading.
14327 */
14328HRESULT SessionMachine::i_onSharedFolderChange()
14329{
14330 LogFlowThisFunc(("\n"));
14331
14332 AutoCaller autoCaller(this);
14333 AssertComRCReturnRC(autoCaller.hrc());
14334
14335 ComPtr<IInternalSessionControl> directControl;
14336 {
14337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14338 if (mData->mSession.mLockType == LockType_VM)
14339 directControl = mData->mSession.mDirectControl;
14340 }
14341
14342 /* ignore notifications sent after #OnSessionEnd() is called */
14343 if (!directControl)
14344 return S_OK;
14345
14346 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14347}
14348
14349/**
14350 * @note Locks this object for reading.
14351 */
14352HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14353{
14354 LogFlowThisFunc(("\n"));
14355
14356 AutoCaller autoCaller(this);
14357 AssertComRCReturnRC(autoCaller.hrc());
14358
14359 ComPtr<IInternalSessionControl> directControl;
14360 {
14361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14362 if (mData->mSession.mLockType == LockType_VM)
14363 directControl = mData->mSession.mDirectControl;
14364 }
14365
14366 /* ignore notifications sent after #OnSessionEnd() is called */
14367 if (!directControl)
14368 return S_OK;
14369
14370 return directControl->OnClipboardModeChange(aClipboardMode);
14371}
14372
14373/**
14374 * @note Locks this object for reading.
14375 */
14376HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14377{
14378 LogFlowThisFunc(("\n"));
14379
14380 AutoCaller autoCaller(this);
14381 AssertComRCReturnRC(autoCaller.hrc());
14382
14383 ComPtr<IInternalSessionControl> directControl;
14384 {
14385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14386 if (mData->mSession.mLockType == LockType_VM)
14387 directControl = mData->mSession.mDirectControl;
14388 }
14389
14390 /* ignore notifications sent after #OnSessionEnd() is called */
14391 if (!directControl)
14392 return S_OK;
14393
14394 return directControl->OnClipboardFileTransferModeChange(aEnable);
14395}
14396
14397/**
14398 * @note Locks this object for reading.
14399 */
14400HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14401{
14402 LogFlowThisFunc(("\n"));
14403
14404 AutoCaller autoCaller(this);
14405 AssertComRCReturnRC(autoCaller.hrc());
14406
14407 ComPtr<IInternalSessionControl> directControl;
14408 {
14409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14410 if (mData->mSession.mLockType == LockType_VM)
14411 directControl = mData->mSession.mDirectControl;
14412 }
14413
14414 /* ignore notifications sent after #OnSessionEnd() is called */
14415 if (!directControl)
14416 return S_OK;
14417
14418 return directControl->OnDnDModeChange(aDnDMode);
14419}
14420
14421/**
14422 * @note Locks this object for reading.
14423 */
14424HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14425{
14426 LogFlowThisFunc(("\n"));
14427
14428 AutoCaller autoCaller(this);
14429 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14430
14431 ComPtr<IInternalSessionControl> directControl;
14432 {
14433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14434 if (mData->mSession.mLockType == LockType_VM)
14435 directControl = mData->mSession.mDirectControl;
14436 }
14437
14438 /* ignore notifications sent after #OnSessionEnd() is called */
14439 if (!directControl)
14440 return S_OK;
14441
14442 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14443}
14444
14445/**
14446 * @note Locks this object for reading.
14447 */
14448HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14449{
14450 LogFlowThisFunc(("\n"));
14451
14452 AutoCaller autoCaller(this);
14453 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14454
14455 ComPtr<IInternalSessionControl> directControl;
14456 {
14457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14458 if (mData->mSession.mLockType == LockType_VM)
14459 directControl = mData->mSession.mDirectControl;
14460 }
14461
14462 /* ignore notifications sent after #OnSessionEnd() is called */
14463 if (!directControl)
14464 return S_OK;
14465
14466 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14467}
14468
14469/**
14470 * @note Locks this object for reading.
14471 */
14472HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14473{
14474 LogFlowThisFunc(("\n"));
14475
14476 AutoCaller autoCaller(this);
14477 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14478
14479 ComPtr<IInternalSessionControl> directControl;
14480 {
14481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14482 if (mData->mSession.mLockType == LockType_VM)
14483 directControl = mData->mSession.mDirectControl;
14484 }
14485
14486 /* ignore notifications sent after #OnSessionEnd() is called */
14487 if (!directControl)
14488 return S_OK;
14489
14490 return directControl->OnGuestDebugControlChange(guestDebugControl);
14491}
14492
14493/**
14494 * Returns @c true if this machine's USB controller reports it has a matching
14495 * filter for the given USB device and @c false otherwise.
14496 *
14497 * @note locks this object for reading.
14498 */
14499bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14500{
14501 AutoCaller autoCaller(this);
14502 /* silently return if not ready -- this method may be called after the
14503 * direct machine session has been called */
14504 if (!autoCaller.isOk())
14505 return false;
14506
14507#ifdef VBOX_WITH_USB
14508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14509
14510 switch (mData->mMachineState)
14511 {
14512 case MachineState_Starting:
14513 case MachineState_Restoring:
14514 case MachineState_TeleportingIn:
14515 case MachineState_Paused:
14516 case MachineState_Running:
14517 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14518 * elsewhere... */
14519 alock.release();
14520 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14521 default: break;
14522 }
14523#else
14524 NOREF(aDevice);
14525 NOREF(aMaskedIfs);
14526#endif
14527 return false;
14528}
14529
14530/**
14531 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14532 */
14533HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14534 IVirtualBoxErrorInfo *aError,
14535 ULONG aMaskedIfs,
14536 const com::Utf8Str &aCaptureFilename)
14537{
14538 LogFlowThisFunc(("\n"));
14539
14540 AutoCaller autoCaller(this);
14541
14542 /* This notification may happen after the machine object has been
14543 * uninitialized (the session was closed), so don't assert. */
14544 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14545
14546 ComPtr<IInternalSessionControl> directControl;
14547 {
14548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14549 if (mData->mSession.mLockType == LockType_VM)
14550 directControl = mData->mSession.mDirectControl;
14551 }
14552
14553 /* fail on notifications sent after #OnSessionEnd() is called, it is
14554 * expected by the caller */
14555 if (!directControl)
14556 return E_FAIL;
14557
14558 /* No locks should be held at this point. */
14559 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14560 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14561
14562 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14563}
14564
14565/**
14566 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14567 */
14568HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14569 IVirtualBoxErrorInfo *aError)
14570{
14571 LogFlowThisFunc(("\n"));
14572
14573 AutoCaller autoCaller(this);
14574
14575 /* This notification may happen after the machine object has been
14576 * uninitialized (the session was closed), so don't assert. */
14577 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14578
14579 ComPtr<IInternalSessionControl> directControl;
14580 {
14581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14582 if (mData->mSession.mLockType == LockType_VM)
14583 directControl = mData->mSession.mDirectControl;
14584 }
14585
14586 /* fail on notifications sent after #OnSessionEnd() is called, it is
14587 * expected by the caller */
14588 if (!directControl)
14589 return E_FAIL;
14590
14591 /* No locks should be held at this point. */
14592 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14593 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14594
14595 return directControl->OnUSBDeviceDetach(aId, aError);
14596}
14597
14598// protected methods
14599/////////////////////////////////////////////////////////////////////////////
14600
14601/**
14602 * Deletes the given file if it is no longer in use by either the current machine state
14603 * (if the machine is "saved") or any of the machine's snapshots.
14604 *
14605 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14606 * but is different for each SnapshotMachine. When calling this, the order of calling this
14607 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14608 * is therefore critical. I know, it's all rather messy.
14609 *
14610 * @param strStateFile
14611 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14612 * the test for whether the saved state file is in use.
14613 */
14614void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14615 Snapshot *pSnapshotToIgnore)
14616{
14617 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14618 if ( (strStateFile.isNotEmpty())
14619 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14620 )
14621 // ... and it must also not be shared with other snapshots
14622 if ( !mData->mFirstSnapshot
14623 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14624 // this checks the SnapshotMachine's state file paths
14625 )
14626 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
14627}
14628
14629/**
14630 * Locks the attached media.
14631 *
14632 * All attached hard disks are locked for writing and DVD/floppy are locked for
14633 * reading. Parents of attached hard disks (if any) are locked for reading.
14634 *
14635 * This method also performs accessibility check of all media it locks: if some
14636 * media is inaccessible, the method will return a failure and a bunch of
14637 * extended error info objects per each inaccessible medium.
14638 *
14639 * Note that this method is atomic: if it returns a success, all media are
14640 * locked as described above; on failure no media is locked at all (all
14641 * succeeded individual locks will be undone).
14642 *
14643 * The caller is responsible for doing the necessary state sanity checks.
14644 *
14645 * The locks made by this method must be undone by calling #unlockMedia() when
14646 * no more needed.
14647 */
14648HRESULT SessionMachine::i_lockMedia()
14649{
14650 AutoCaller autoCaller(this);
14651 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14652
14653 AutoMultiWriteLock2 alock(this->lockHandle(),
14654 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14655
14656 /* bail out if trying to lock things with already set up locking */
14657 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14658
14659 MultiResult hrcMult(S_OK);
14660
14661 /* Collect locking information for all medium objects attached to the VM. */
14662 for (MediumAttachmentList::const_iterator
14663 it = mMediumAttachments->begin();
14664 it != mMediumAttachments->end();
14665 ++it)
14666 {
14667 MediumAttachment *pAtt = *it;
14668 DeviceType_T devType = pAtt->i_getType();
14669 Medium *pMedium = pAtt->i_getMedium();
14670
14671 MediumLockList *pMediumLockList(new MediumLockList());
14672 // There can be attachments without a medium (floppy/dvd), and thus
14673 // it's impossible to create a medium lock list. It still makes sense
14674 // to have the empty medium lock list in the map in case a medium is
14675 // attached later.
14676 if (pMedium != NULL)
14677 {
14678 MediumType_T mediumType = pMedium->i_getType();
14679 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14680 || mediumType == MediumType_Shareable;
14681 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14682
14683 alock.release();
14684 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14685 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14686 false /* fMediumLockWriteAll */,
14687 NULL,
14688 *pMediumLockList);
14689 alock.acquire();
14690 if (FAILED(hrcMult))
14691 {
14692 delete pMediumLockList;
14693 mData->mSession.mLockedMedia.Clear();
14694 break;
14695 }
14696 }
14697
14698 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14699 if (FAILED(hrc))
14700 {
14701 mData->mSession.mLockedMedia.Clear();
14702 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
14703 break;
14704 }
14705 }
14706
14707 if (SUCCEEDED(hrcMult))
14708 {
14709 /* Now lock all media. If this fails, nothing is locked. */
14710 alock.release();
14711 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
14712 alock.acquire();
14713 if (FAILED(hrc))
14714 hrcMult = setError(hrc,
14715 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14716 }
14717
14718 return hrcMult;
14719}
14720
14721/**
14722 * Undoes the locks made by by #lockMedia().
14723 */
14724HRESULT SessionMachine::i_unlockMedia()
14725{
14726 AutoCaller autoCaller(this);
14727 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
14728
14729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14730
14731 /* we may be holding important error info on the current thread;
14732 * preserve it */
14733 ErrorInfoKeeper eik;
14734
14735 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
14736 AssertComRC(hrc);
14737 return hrc;
14738}
14739
14740/**
14741 * Helper to change the machine state (reimplementation).
14742 *
14743 * @note Locks this object for writing.
14744 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14745 * it can cause crashes in random places due to unexpectedly committing
14746 * the current settings. The caller is responsible for that. The call
14747 * to saveStateSettings is fine, because this method does not commit.
14748 */
14749HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14750{
14751 LogFlowThisFuncEnter();
14752
14753 AutoCaller autoCaller(this);
14754 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14755
14756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14757
14758 MachineState_T oldMachineState = mData->mMachineState;
14759
14760 AssertMsgReturn(oldMachineState != aMachineState,
14761 ("oldMachineState=%s, aMachineState=%s\n",
14762 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14763 E_FAIL);
14764
14765 HRESULT hrc = S_OK;
14766
14767 int stsFlags = 0;
14768 bool deleteSavedState = false;
14769
14770 /* detect some state transitions */
14771
14772 if ( ( ( oldMachineState == MachineState_Saved
14773 || oldMachineState == MachineState_AbortedSaved
14774 )
14775 && aMachineState == MachineState_Restoring
14776 )
14777 || ( ( oldMachineState == MachineState_PoweredOff
14778 || oldMachineState == MachineState_Teleported
14779 || oldMachineState == MachineState_Aborted
14780 )
14781 && ( aMachineState == MachineState_TeleportingIn
14782 || aMachineState == MachineState_Starting
14783 )
14784 )
14785 )
14786 {
14787 /* The EMT thread is about to start */
14788
14789 /* Nothing to do here for now... */
14790
14791 /// @todo NEWMEDIA don't let mDVDDrive and other children
14792 /// change anything when in the Starting/Restoring state
14793 }
14794 else if ( ( oldMachineState == MachineState_Running
14795 || oldMachineState == MachineState_Paused
14796 || oldMachineState == MachineState_Teleporting
14797 || oldMachineState == MachineState_OnlineSnapshotting
14798 || oldMachineState == MachineState_LiveSnapshotting
14799 || oldMachineState == MachineState_Stuck
14800 || oldMachineState == MachineState_Starting
14801 || oldMachineState == MachineState_Stopping
14802 || oldMachineState == MachineState_Saving
14803 || oldMachineState == MachineState_Restoring
14804 || oldMachineState == MachineState_TeleportingPausedVM
14805 || oldMachineState == MachineState_TeleportingIn
14806 )
14807 && ( aMachineState == MachineState_PoweredOff
14808 || aMachineState == MachineState_Saved
14809 || aMachineState == MachineState_Teleported
14810 || aMachineState == MachineState_Aborted
14811 || aMachineState == MachineState_AbortedSaved
14812 )
14813 )
14814 {
14815 /* The EMT thread has just stopped, unlock attached media. Note that as
14816 * opposed to locking that is done from Console, we do unlocking here
14817 * because the VM process may have aborted before having a chance to
14818 * properly unlock all media it locked. */
14819
14820 unlockMedia();
14821 }
14822
14823 if (oldMachineState == MachineState_Restoring)
14824 {
14825 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14826 {
14827 /*
14828 * delete the saved state file once the machine has finished
14829 * restoring from it (note that Console sets the state from
14830 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14831 * to give the user an ability to fix an error and retry --
14832 * we keep the saved state file in this case)
14833 */
14834 deleteSavedState = true;
14835 }
14836 }
14837 else if ( ( oldMachineState == MachineState_Saved
14838 || oldMachineState == MachineState_AbortedSaved
14839 )
14840 && ( aMachineState == MachineState_PoweredOff
14841 || aMachineState == MachineState_Teleported
14842 )
14843 )
14844 {
14845 /* delete the saved state after SessionMachine::discardSavedState() is called */
14846 deleteSavedState = true;
14847 mData->mCurrentStateModified = TRUE;
14848 stsFlags |= SaveSTS_CurStateModified;
14849 }
14850 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14851 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14852
14853 if ( aMachineState == MachineState_Starting
14854 || aMachineState == MachineState_Restoring
14855 || aMachineState == MachineState_TeleportingIn
14856 )
14857 {
14858 /* set the current state modified flag to indicate that the current
14859 * state is no more identical to the state in the
14860 * current snapshot */
14861 if (!mData->mCurrentSnapshot.isNull())
14862 {
14863 mData->mCurrentStateModified = TRUE;
14864 stsFlags |= SaveSTS_CurStateModified;
14865 }
14866 }
14867
14868 if (deleteSavedState)
14869 {
14870 if (mRemoveSavedState)
14871 {
14872 Assert(!mSSData->strStateFilePath.isEmpty());
14873
14874 // it is safe to delete the saved state file if ...
14875 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14876 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14877 // ... none of the snapshots share the saved state file
14878 )
14879 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
14880 }
14881
14882 mSSData->strStateFilePath.setNull();
14883 stsFlags |= SaveSTS_StateFilePath;
14884 }
14885
14886 /* redirect to the underlying peer machine */
14887 mPeer->i_setMachineState(aMachineState);
14888
14889 if ( oldMachineState != MachineState_RestoringSnapshot
14890 && ( aMachineState == MachineState_PoweredOff
14891 || aMachineState == MachineState_Teleported
14892 || aMachineState == MachineState_Aborted
14893 || aMachineState == MachineState_AbortedSaved
14894 || aMachineState == MachineState_Saved))
14895 {
14896 /* the machine has stopped execution
14897 * (or the saved state file was adopted) */
14898 stsFlags |= SaveSTS_StateTimeStamp;
14899 }
14900
14901 if ( ( oldMachineState == MachineState_PoweredOff
14902 || oldMachineState == MachineState_Aborted
14903 || oldMachineState == MachineState_Teleported
14904 )
14905 && aMachineState == MachineState_Saved)
14906 {
14907 /* the saved state file was adopted */
14908 Assert(!mSSData->strStateFilePath.isEmpty());
14909 stsFlags |= SaveSTS_StateFilePath;
14910 }
14911
14912#ifdef VBOX_WITH_GUEST_PROPS
14913 if ( aMachineState == MachineState_PoweredOff
14914 || aMachineState == MachineState_Aborted
14915 || aMachineState == MachineState_Teleported)
14916 {
14917 /* Make sure any transient guest properties get removed from the
14918 * property store on shutdown. */
14919 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14920
14921 /* remove it from the settings representation */
14922 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14923 for (settings::GuestPropertiesList::iterator
14924 it = llGuestProperties.begin();
14925 it != llGuestProperties.end();
14926 /*nothing*/)
14927 {
14928 const settings::GuestProperty &prop = *it;
14929 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14930 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14931 {
14932 it = llGuestProperties.erase(it);
14933 fNeedsSaving = true;
14934 }
14935 else
14936 {
14937 ++it;
14938 }
14939 }
14940
14941 /* Additionally remove it from the HWData representation. Required to
14942 * keep everything in sync, as this is what the API keeps using. */
14943 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14944 for (HWData::GuestPropertyMap::iterator
14945 it = llHWGuestProperties.begin();
14946 it != llHWGuestProperties.end();
14947 /*nothing*/)
14948 {
14949 uint32_t fFlags = it->second.mFlags;
14950 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14951 {
14952 /* iterator where we need to continue after the erase call
14953 * (C++03 is a fact still, and it doesn't return the iterator
14954 * which would allow continuing) */
14955 HWData::GuestPropertyMap::iterator it2 = it;
14956 ++it2;
14957 llHWGuestProperties.erase(it);
14958 it = it2;
14959 fNeedsSaving = true;
14960 }
14961 else
14962 {
14963 ++it;
14964 }
14965 }
14966
14967 if (fNeedsSaving)
14968 {
14969 mData->mCurrentStateModified = TRUE;
14970 stsFlags |= SaveSTS_CurStateModified;
14971 }
14972 }
14973#endif /* VBOX_WITH_GUEST_PROPS */
14974
14975 hrc = i_saveStateSettings(stsFlags);
14976
14977 if ( ( oldMachineState != MachineState_PoweredOff
14978 && oldMachineState != MachineState_Aborted
14979 && oldMachineState != MachineState_Teleported
14980 )
14981 && ( aMachineState == MachineState_PoweredOff
14982 || aMachineState == MachineState_Aborted
14983 || aMachineState == MachineState_Teleported
14984 )
14985 )
14986 {
14987 /* we've been shut down for any reason */
14988 /* no special action so far */
14989 }
14990
14991 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
14992 LogFlowThisFuncLeave();
14993 return hrc;
14994}
14995
14996/**
14997 * Sends the current machine state value to the VM process.
14998 *
14999 * @note Locks this object for reading, then calls a client process.
15000 */
15001HRESULT SessionMachine::i_updateMachineStateOnClient()
15002{
15003 AutoCaller autoCaller(this);
15004 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15005
15006 ComPtr<IInternalSessionControl> directControl;
15007 {
15008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15009 AssertReturn(!!mData, E_FAIL);
15010 if (mData->mSession.mLockType == LockType_VM)
15011 directControl = mData->mSession.mDirectControl;
15012
15013 /* directControl may be already set to NULL here in #OnSessionEnd()
15014 * called too early by the direct session process while there is still
15015 * some operation (like deleting the snapshot) in progress. The client
15016 * process in this case is waiting inside Session::close() for the
15017 * "end session" process object to complete, while #uninit() called by
15018 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15019 * operation to complete. For now, we accept this inconsistent behavior
15020 * and simply do nothing here. */
15021
15022 if (mData->mSession.mState == SessionState_Unlocking)
15023 return S_OK;
15024 }
15025
15026 /* ignore notifications sent after #OnSessionEnd() is called */
15027 if (!directControl)
15028 return S_OK;
15029
15030 return directControl->UpdateMachineState(mData->mMachineState);
15031}
15032
15033
15034/*static*/
15035HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15036{
15037 va_list args;
15038 va_start(args, pcszMsg);
15039 HRESULT hrc = setErrorInternalV(aResultCode,
15040 getStaticClassIID(),
15041 getStaticComponentName(),
15042 pcszMsg, args,
15043 false /* aWarning */,
15044 true /* aLogIt */);
15045 va_end(args);
15046 return hrc;
15047}
15048
15049
15050HRESULT Machine::updateState(MachineState_T aState)
15051{
15052 NOREF(aState);
15053 ReturnComNotImplemented();
15054}
15055
15056HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15057{
15058 NOREF(aProgress);
15059 ReturnComNotImplemented();
15060}
15061
15062HRESULT Machine::endPowerUp(LONG aResult)
15063{
15064 NOREF(aResult);
15065 ReturnComNotImplemented();
15066}
15067
15068HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15069{
15070 NOREF(aProgress);
15071 ReturnComNotImplemented();
15072}
15073
15074HRESULT Machine::endPoweringDown(LONG aResult,
15075 const com::Utf8Str &aErrMsg)
15076{
15077 NOREF(aResult);
15078 NOREF(aErrMsg);
15079 ReturnComNotImplemented();
15080}
15081
15082HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15083 BOOL *aMatched,
15084 ULONG *aMaskedInterfaces)
15085{
15086 NOREF(aDevice);
15087 NOREF(aMatched);
15088 NOREF(aMaskedInterfaces);
15089 ReturnComNotImplemented();
15090
15091}
15092
15093HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15094{
15095 NOREF(aId); NOREF(aCaptureFilename);
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15100 BOOL aDone)
15101{
15102 NOREF(aId);
15103 NOREF(aDone);
15104 ReturnComNotImplemented();
15105}
15106
15107HRESULT Machine::autoCaptureUSBDevices()
15108{
15109 ReturnComNotImplemented();
15110}
15111
15112HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15113{
15114 NOREF(aDone);
15115 ReturnComNotImplemented();
15116}
15117
15118HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15119 ComPtr<IProgress> &aProgress)
15120{
15121 NOREF(aSession);
15122 NOREF(aProgress);
15123 ReturnComNotImplemented();
15124}
15125
15126HRESULT Machine::finishOnlineMergeMedium()
15127{
15128 ReturnComNotImplemented();
15129}
15130
15131HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15132 std::vector<com::Utf8Str> &aValues,
15133 std::vector<LONG64> &aTimestamps,
15134 std::vector<com::Utf8Str> &aFlags)
15135{
15136 NOREF(aNames);
15137 NOREF(aValues);
15138 NOREF(aTimestamps);
15139 NOREF(aFlags);
15140 ReturnComNotImplemented();
15141}
15142
15143HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15144 const com::Utf8Str &aValue,
15145 LONG64 aTimestamp,
15146 const com::Utf8Str &aFlags,
15147 BOOL fWasDeleted)
15148{
15149 NOREF(aName);
15150 NOREF(aValue);
15151 NOREF(aTimestamp);
15152 NOREF(aFlags);
15153 NOREF(fWasDeleted);
15154 ReturnComNotImplemented();
15155}
15156
15157HRESULT Machine::lockMedia()
15158{
15159 ReturnComNotImplemented();
15160}
15161
15162HRESULT Machine::unlockMedia()
15163{
15164 ReturnComNotImplemented();
15165}
15166
15167HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15168 ComPtr<IMediumAttachment> &aNewAttachment)
15169{
15170 NOREF(aAttachment);
15171 NOREF(aNewAttachment);
15172 ReturnComNotImplemented();
15173}
15174
15175HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15176 ULONG aCpuUser,
15177 ULONG aCpuKernel,
15178 ULONG aCpuIdle,
15179 ULONG aMemTotal,
15180 ULONG aMemFree,
15181 ULONG aMemBalloon,
15182 ULONG aMemShared,
15183 ULONG aMemCache,
15184 ULONG aPagedTotal,
15185 ULONG aMemAllocTotal,
15186 ULONG aMemFreeTotal,
15187 ULONG aMemBalloonTotal,
15188 ULONG aMemSharedTotal,
15189 ULONG aVmNetRx,
15190 ULONG aVmNetTx)
15191{
15192 NOREF(aValidStats);
15193 NOREF(aCpuUser);
15194 NOREF(aCpuKernel);
15195 NOREF(aCpuIdle);
15196 NOREF(aMemTotal);
15197 NOREF(aMemFree);
15198 NOREF(aMemBalloon);
15199 NOREF(aMemShared);
15200 NOREF(aMemCache);
15201 NOREF(aPagedTotal);
15202 NOREF(aMemAllocTotal);
15203 NOREF(aMemFreeTotal);
15204 NOREF(aMemBalloonTotal);
15205 NOREF(aMemSharedTotal);
15206 NOREF(aVmNetRx);
15207 NOREF(aVmNetTx);
15208 ReturnComNotImplemented();
15209}
15210
15211HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15212 com::Utf8Str &aResult)
15213{
15214 NOREF(aAuthParams);
15215 NOREF(aResult);
15216 ReturnComNotImplemented();
15217}
15218
15219HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15220{
15221 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15222
15223 AutoCaller autoCaller(this);
15224 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15225
15226 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15227 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15228 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15229 if (FAILED(hrc)) return hrc;
15230
15231 NOREF(aFlags);
15232 com::Utf8Str osTypeId;
15233 ComObjPtr<GuestOSType> osType = NULL;
15234
15235 /* Get the guest os type as a string from the VB. */
15236 hrc = getOSTypeId(osTypeId);
15237 if (FAILED(hrc)) return hrc;
15238
15239 /* Get the os type obj that coresponds, can be used to get
15240 * the defaults for this guest OS. */
15241 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15242 if (FAILED(hrc)) return hrc;
15243
15244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15245
15246 mPlatform->i_applyDefaults(osType);
15247
15248 /* This one covers IOAPICEnabled. */
15249 mFirmwareSettings->i_applyDefaults(osType);
15250
15251 /* Initialize default record settings. */
15252 mRecordingSettings->i_applyDefaults();
15253
15254 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15255 if (FAILED(hrc)) return hrc;
15256
15257 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15258 if (FAILED(hrc)) return hrc;
15259
15260 /* Graphics stuff. */
15261 GraphicsControllerType_T graphicsController;
15262 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15263 if (FAILED(hrc)) return hrc;
15264
15265 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15266 if (FAILED(hrc)) return hrc;
15267
15268 ULONG vramSize;
15269 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15270 if (FAILED(hrc)) return hrc;
15271
15272 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15273 if (FAILED(hrc)) return hrc;
15274
15275 BOOL fAccelerate2DVideoEnabled;
15276 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15277 if (FAILED(hrc)) return hrc;
15278
15279 hrc = mGraphicsAdapter->SetFeature(GraphicsFeature_Acceleration2DVideo, fAccelerate2DVideoEnabled);
15280 if (FAILED(hrc))
15281 {
15282 if (hrc != VBOX_E_NOT_SUPPORTED)
15283 return hrc;
15284 }
15285
15286 BOOL fAccelerate3DEnabled;
15287 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15288 if (FAILED(hrc)) return hrc;
15289
15290 hrc = mGraphicsAdapter->SetFeature(GraphicsFeature_Acceleration2DVideo, fAccelerate3DEnabled);
15291 if (FAILED(hrc))
15292 {
15293 if (hrc != VBOX_E_NOT_SUPPORTED)
15294 return hrc;
15295 }
15296
15297 /* Apply network adapters defaults */
15298 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15299 mNetworkAdapters[slot]->i_applyDefaults(osType);
15300
15301 /* Apply serial port defaults */
15302 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15303 mSerialPorts[slot]->i_applyDefaults(osType);
15304
15305 /* Apply parallel port defaults - not OS dependent*/
15306 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15307 mParallelPorts[slot]->i_applyDefaults();
15308
15309 /* This one covers the TPM type. */
15310 mTrustedPlatformModule->i_applyDefaults(osType);
15311
15312 /* This one covers secure boot. */
15313 hrc = mNvramStore->i_applyDefaults(osType);
15314 if (FAILED(hrc)) return hrc;
15315
15316 /* Audio stuff. */
15317 hrc = mAudioSettings->i_applyDefaults(osType);
15318 if (FAILED(hrc)) return hrc;
15319
15320 /* Storage Controllers */
15321 StorageControllerType_T hdStorageControllerType;
15322 StorageBus_T hdStorageBusType;
15323 StorageControllerType_T dvdStorageControllerType;
15324 StorageBus_T dvdStorageBusType;
15325 BOOL recommendedFloppy;
15326 ComPtr<IStorageController> floppyController;
15327 ComPtr<IStorageController> hdController;
15328 ComPtr<IStorageController> dvdController;
15329 Utf8Str strFloppyName, strDVDName, strHDName;
15330
15331 /* GUI auto generates controller names using bus type. Do the same*/
15332 strFloppyName = StorageController::i_controllerNameFromBusType(StorageBus_Floppy);
15333
15334 /* Floppy recommended? add one. */
15335 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15336 if (FAILED(hrc)) return hrc;
15337 if (recommendedFloppy)
15338 {
15339 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15340 if (FAILED(hrc)) return hrc;
15341 }
15342
15343 /* Setup one DVD storage controller. */
15344 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15345 if (FAILED(hrc)) return hrc;
15346
15347 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15348 if (FAILED(hrc)) return hrc;
15349
15350 strDVDName = StorageController::i_controllerNameFromBusType(dvdStorageBusType);
15351
15352 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15353 if (FAILED(hrc)) return hrc;
15354
15355 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15356 if (FAILED(hrc)) return hrc;
15357
15358 /* Setup one HDD storage controller. */
15359 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15360 if (FAILED(hrc)) return hrc;
15361
15362 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15363 if (FAILED(hrc)) return hrc;
15364
15365 strHDName = StorageController::i_controllerNameFromBusType(hdStorageBusType);
15366
15367 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15368 {
15369 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15370 if (FAILED(hrc)) return hrc;
15371
15372 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15373 if (FAILED(hrc)) return hrc;
15374 }
15375 else
15376 {
15377 /* The HD controller is the same as DVD: */
15378 hdController = dvdController;
15379 }
15380
15381 /* Limit the AHCI port count if it's used because windows has trouble with
15382 * too many ports and other guest (OS X in particular) may take extra long
15383 * boot: */
15384
15385 // pParent = static_cast<Medium*>(aP)
15386 IStorageController *temp = hdController;
15387 ComObjPtr<StorageController> storageController;
15388 storageController = static_cast<StorageController *>(temp);
15389
15390 // tempHDController = aHDController;
15391 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15392 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15393 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15394 storageController->COMSETTER(PortCount)(1);
15395
15396 /* VirtioSCSI configures only one port per default -- set two ports here, one for HDD and one for DVD drive. */
15397 if (hdStorageControllerType == StorageControllerType_VirtioSCSI)
15398 {
15399 hrc = storageController->COMSETTER(PortCount)(2);
15400 if (FAILED(hrc)) return hrc;
15401 }
15402
15403 /* USB stuff */
15404
15405 bool ohciEnabled = false;
15406
15407 ComPtr<IUSBController> usbController;
15408 BOOL recommendedUSB3;
15409 BOOL recommendedUSB;
15410 BOOL usbProxyAvailable;
15411
15412 getUSBProxyAvailable(&usbProxyAvailable);
15413 if (FAILED(hrc)) return hrc;
15414
15415 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15416 if (FAILED(hrc)) return hrc;
15417 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15418 if (FAILED(hrc)) return hrc;
15419
15420 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15421 {
15422 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15423 if (FAILED(hrc)) return hrc;
15424
15425 /* xHci includes OHCI */
15426 ohciEnabled = true;
15427 }
15428 if ( !ohciEnabled
15429 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15430 {
15431 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15432 if (FAILED(hrc)) return hrc;
15433 ohciEnabled = true;
15434
15435 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15436 if (FAILED(hrc)) return hrc;
15437 }
15438
15439 /* Set recommended human interface device types: */
15440 BOOL recommendedUSBHID;
15441 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15442 if (FAILED(hrc)) return hrc;
15443
15444 if (recommendedUSBHID)
15445 {
15446 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15447 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15448 if (!ohciEnabled && !usbDeviceFilters.isNull())
15449 {
15450 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15451 if (FAILED(hrc)) return hrc;
15452 }
15453 }
15454
15455 BOOL recommendedUSBTablet;
15456 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15457 if (FAILED(hrc)) return hrc;
15458
15459 if (recommendedUSBTablet)
15460 {
15461 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15462 if (!ohciEnabled && !usbDeviceFilters.isNull())
15463 {
15464 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15465 if (FAILED(hrc)) return hrc;
15466 }
15467 }
15468
15469 /* Enable the VMMDev testing feature for bootsector VMs: */
15470 if (osTypeId == GUEST_OS_ID_STR_X64("VBoxBS"))
15471 {
15472 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15473 if (FAILED(hrc))
15474 return hrc;
15475 }
15476
15477 return S_OK;
15478}
15479
15480#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15481/**
15482 * Task record for change encryption settins.
15483 */
15484class Machine::ChangeEncryptionTask
15485 : public Machine::Task
15486{
15487public:
15488 ChangeEncryptionTask(Machine *m,
15489 Progress *p,
15490 const Utf8Str &t,
15491 const com::Utf8Str &aCurrentPassword,
15492 const com::Utf8Str &aCipher,
15493 const com::Utf8Str &aNewPassword,
15494 const com::Utf8Str &aNewPasswordId,
15495 const BOOL aForce,
15496 const MediaList &llMedia)
15497 : Task(m, p, t),
15498 mstrNewPassword(aNewPassword),
15499 mstrCurrentPassword(aCurrentPassword),
15500 mstrCipher(aCipher),
15501 mstrNewPasswordId(aNewPasswordId),
15502 mForce(aForce),
15503 mllMedia(llMedia),
15504 m_pCryptoIf(NULL)
15505 {}
15506
15507 ~ChangeEncryptionTask()
15508 {
15509 if (mstrNewPassword.length())
15510 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15511 if (mstrCurrentPassword.length())
15512 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15513 if (m_pCryptoIf)
15514 {
15515 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15516 m_pCryptoIf = NULL;
15517 }
15518 }
15519
15520 Utf8Str mstrNewPassword;
15521 Utf8Str mstrCurrentPassword;
15522 Utf8Str mstrCipher;
15523 Utf8Str mstrNewPasswordId;
15524 BOOL mForce;
15525 MediaList mllMedia;
15526 PCVBOXCRYPTOIF m_pCryptoIf;
15527private:
15528 void handler()
15529 {
15530 try
15531 {
15532 m_pMachine->i_changeEncryptionHandler(*this);
15533 }
15534 catch (...)
15535 {
15536 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
15537 }
15538 }
15539
15540 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
15541};
15542
15543/**
15544 * Scans specified directory and fills list by files found
15545 *
15546 * @returns VBox status code.
15547 * @param lstFiles
15548 * @param strDir
15549 * @param filePattern
15550 */
15551int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
15552 const com::Utf8Str &strPattern)
15553{
15554 /* To get all entries including subdirectories. */
15555 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
15556 if (!pszFilePattern)
15557 return VERR_NO_STR_MEMORY;
15558
15559 PRTDIRENTRYEX pDirEntry = NULL;
15560 RTDIR hDir;
15561 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
15562 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
15563 if (RT_SUCCESS(vrc))
15564 {
15565 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
15566 if (pDirEntry)
15567 {
15568 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
15569 != VERR_NO_MORE_FILES)
15570 {
15571 char *pszFilePath = NULL;
15572
15573 if (vrc == VERR_BUFFER_OVERFLOW)
15574 {
15575 /* allocate new buffer. */
15576 RTMemFree(pDirEntry);
15577 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
15578 if (!pDirEntry)
15579 {
15580 vrc = VERR_NO_MEMORY;
15581 break;
15582 }
15583 /* Retry. */
15584 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
15585 if (RT_FAILURE(vrc))
15586 break;
15587 }
15588 else if (RT_FAILURE(vrc))
15589 break;
15590
15591 /* Exclude . and .. */
15592 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
15593 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
15594 continue;
15595 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
15596 {
15597 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15598 if (!pszSubDirPath)
15599 {
15600 vrc = VERR_NO_STR_MEMORY;
15601 break;
15602 }
15603 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
15604 RTMemFree(pszSubDirPath);
15605 if (RT_FAILURE(vrc))
15606 break;
15607 continue;
15608 }
15609
15610 /* We got the new entry. */
15611 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
15612 continue;
15613
15614 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
15615 continue;
15616
15617 /* Prepend the path to the libraries. */
15618 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15619 if (!pszFilePath)
15620 {
15621 vrc = VERR_NO_STR_MEMORY;
15622 break;
15623 }
15624
15625 lstFiles.push_back(pszFilePath);
15626 RTStrFree(pszFilePath);
15627 }
15628
15629 RTMemFree(pDirEntry);
15630 }
15631 else
15632 vrc = VERR_NO_MEMORY;
15633
15634 RTDirClose(hDir);
15635 }
15636 else
15637 {
15638 /* On Windows the above immediately signals that there are no
15639 * files matching, while on other platforms enumerating the
15640 * files below fails. Either way: stop searching. */
15641 }
15642
15643 if ( vrc == VERR_NO_MORE_FILES
15644 || vrc == VERR_FILE_NOT_FOUND
15645 || vrc == VERR_PATH_NOT_FOUND)
15646 vrc = VINF_SUCCESS;
15647 RTStrFree(pszFilePattern);
15648 return vrc;
15649}
15650
15651/**
15652 * Helper to set up an I/O stream to read or write a possibly encrypted file.
15653 *
15654 * @returns VBox status code.
15655 * @param pszFilename The file to open.
15656 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
15657 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
15658 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
15659 * @param fOpen The open flags for the file.
15660 * @param phVfsIos Where to store the handle to the I/O stream on success.
15661 */
15662int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
15663 const char *pszKeyStore, const char *pszPassword,
15664 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
15665{
15666 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
15667 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
15668 if (RT_SUCCESS(vrc))
15669 {
15670 if (pCryptoIf)
15671 {
15672 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
15673 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
15674 if (RT_SUCCESS(vrc))
15675 {
15676 RTVfsFileRelease(hVfsFile);
15677 hVfsFile = hVfsFileCrypto;
15678 }
15679 }
15680
15681 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
15682 RTVfsFileRelease(hVfsFile);
15683 }
15684
15685 return vrc;
15686}
15687
15688/**
15689 * Helper function processing all actions for one component (saved state files,
15690 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
15691 *
15692 * @param task
15693 * @param strDirectory
15694 * @param strFilePattern
15695 * @param strMagic
15696 * @param strKeyStore
15697 * @param strKeyId
15698 * @return
15699 */
15700HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
15701 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
15702 com::Utf8Str &strKeyId, int iCipherMode)
15703{
15704 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
15705 && task.mstrCipher.isEmpty()
15706 && task.mstrNewPassword.isEmpty()
15707 && task.mstrNewPasswordId.isEmpty();
15708 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
15709 && task.mstrCipher.isNotEmpty()
15710 && task.mstrNewPassword.isNotEmpty()
15711 && task.mstrNewPasswordId.isNotEmpty();
15712
15713 /* check if the cipher is changed which causes the reencryption*/
15714
15715 const char *pszTaskCipher = NULL;
15716 if (task.mstrCipher.isNotEmpty())
15717 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
15718
15719 if (!task.mForce && !fDecrypt && !fEncrypt)
15720 {
15721 char *pszCipher = NULL;
15722 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
15723 NULL /*pszPassword*/,
15724 NULL /*ppbKey*/,
15725 NULL /*pcbKey*/,
15726 &pszCipher);
15727 if (RT_SUCCESS(vrc))
15728 {
15729 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
15730 RTMemFree(pszCipher);
15731 }
15732 else
15733 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
15734 strFilePattern.c_str(), vrc);
15735 }
15736
15737 /* Only the password needs to be changed */
15738 if (!task.mForce && !fDecrypt && !fEncrypt)
15739 {
15740 Assert(task.m_pCryptoIf);
15741
15742 VBOXCRYPTOCTX hCryptoCtx;
15743 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
15744 if (RT_FAILURE(vrc))
15745 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
15746 strFilePattern.c_str(), vrc);
15747 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15748 if (RT_FAILURE(vrc))
15749 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
15750 strFilePattern.c_str(), vrc);
15751
15752 char *pszKeyStore = NULL;
15753 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15754 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15755 if (RT_FAILURE(vrc))
15756 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
15757 strFilePattern.c_str(), vrc);
15758 strKeyStore = pszKeyStore;
15759 RTMemFree(pszKeyStore);
15760 strKeyId = task.mstrNewPasswordId;
15761 return S_OK;
15762 }
15763
15764 /* Reencryption required */
15765 HRESULT hrc = S_OK;
15766 int vrc = VINF_SUCCESS;
15767
15768 std::list<com::Utf8Str> lstFiles;
15769 if (SUCCEEDED(hrc))
15770 {
15771 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
15772 if (RT_FAILURE(vrc))
15773 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
15774 }
15775 com::Utf8Str strNewKeyStore;
15776 if (SUCCEEDED(hrc))
15777 {
15778 if (!fDecrypt)
15779 {
15780 VBOXCRYPTOCTX hCryptoCtx;
15781 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
15782 if (RT_FAILURE(vrc))
15783 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
15784 strFilePattern.c_str(), vrc);
15785
15786 char *pszKeyStore = NULL;
15787 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15788 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15789 if (RT_FAILURE(vrc))
15790 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
15791 strFilePattern.c_str(), vrc);
15792 strNewKeyStore = pszKeyStore;
15793 RTMemFree(pszKeyStore);
15794 }
15795
15796 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15797 it != lstFiles.end();
15798 ++it)
15799 {
15800 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
15801 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
15802
15803 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
15804 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
15805
15806 vrc = i_createIoStreamForFile((*it).c_str(),
15807 fEncrypt ? NULL : task.m_pCryptoIf,
15808 fEncrypt ? NULL : strKeyStore.c_str(),
15809 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
15810 fOpenForRead, &hVfsIosOld);
15811 if (RT_SUCCESS(vrc))
15812 {
15813 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
15814 fDecrypt ? NULL : task.m_pCryptoIf,
15815 fDecrypt ? NULL : strNewKeyStore.c_str(),
15816 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
15817 fOpenForWrite, &hVfsIosNew);
15818 if (RT_FAILURE(vrc))
15819 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
15820 (*it + ".tmp").c_str(), vrc);
15821 }
15822 else
15823 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
15824
15825 if (RT_SUCCESS(vrc))
15826 {
15827 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
15828 if (RT_FAILURE(vrc))
15829 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
15830 (*it).c_str(), vrc);
15831 }
15832
15833 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
15834 RTVfsIoStrmRelease(hVfsIosOld);
15835 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
15836 RTVfsIoStrmRelease(hVfsIosNew);
15837 }
15838 }
15839
15840 if (SUCCEEDED(hrc))
15841 {
15842 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15843 it != lstFiles.end();
15844 ++it)
15845 {
15846 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
15847 if (RT_FAILURE(vrc))
15848 {
15849 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
15850 break;
15851 }
15852 }
15853 }
15854
15855 if (SUCCEEDED(hrc))
15856 {
15857 strKeyStore = strNewKeyStore;
15858 strKeyId = task.mstrNewPasswordId;
15859 }
15860
15861 return hrc;
15862}
15863
15864/**
15865 * Task thread implementation for Machine::changeEncryption(), called from
15866 * Machine::taskHandler().
15867 *
15868 * @note Locks this object for writing.
15869 *
15870 * @param task
15871 * @return
15872 */
15873void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
15874{
15875 LogFlowThisFuncEnter();
15876
15877 AutoCaller autoCaller(this);
15878 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
15879 if (FAILED(autoCaller.hrc()))
15880 {
15881 /* we might have been uninitialized because the session was accidentally
15882 * closed by the client, so don't assert */
15883 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
15884 task.m_pProgress->i_notifyComplete(hrc);
15885 LogFlowThisFuncLeave();
15886 return;
15887 }
15888
15889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15890
15891 HRESULT hrc = S_OK;
15892 com::Utf8Str strOldKeyId = mData->mstrKeyId;
15893 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
15894 try
15895 {
15896 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
15897 if (FAILED(hrc))
15898 throw hrc;
15899
15900 if (task.mstrCurrentPassword.isEmpty())
15901 {
15902 if (mData->mstrKeyStore.isNotEmpty())
15903 throw setError(VBOX_E_PASSWORD_INCORRECT,
15904 tr("The password given for the encrypted VM is incorrect"));
15905 }
15906 else
15907 {
15908 if (mData->mstrKeyStore.isEmpty())
15909 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15910 tr("The VM is not configured for encryption"));
15911 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
15912 if (hrc == VBOX_E_PASSWORD_INCORRECT)
15913 throw setError(VBOX_E_PASSWORD_INCORRECT,
15914 tr("The password to decrypt the VM is incorrect"));
15915 }
15916
15917 if (task.mstrCipher.isNotEmpty())
15918 {
15919 if ( task.mstrNewPassword.isEmpty()
15920 && task.mstrNewPasswordId.isEmpty()
15921 && task.mstrCurrentPassword.isNotEmpty())
15922 {
15923 /* An empty password and password ID will default to the current password. */
15924 task.mstrNewPassword = task.mstrCurrentPassword;
15925 }
15926 else if (task.mstrNewPassword.isEmpty())
15927 throw setError(VBOX_E_OBJECT_NOT_FOUND,
15928 tr("A password must be given for the VM encryption"));
15929 else if (task.mstrNewPasswordId.isEmpty())
15930 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15931 tr("A valid identifier for the password must be given"));
15932 }
15933 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
15934 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15935 tr("The password and password identifier must be empty if the output should be unencrypted"));
15936
15937 /*
15938 * Save config.
15939 * Must be first operation to prevent making encrypted copies
15940 * for old version of the config file.
15941 */
15942 int fSave = Machine::SaveS_Force;
15943 if (task.mstrNewPassword.isNotEmpty())
15944 {
15945 VBOXCRYPTOCTX hCryptoCtx;
15946
15947 int vrc = VINF_SUCCESS;
15948 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
15949 {
15950 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
15951 task.mstrNewPassword.c_str(), &hCryptoCtx);
15952 if (RT_FAILURE(vrc))
15953 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
15954 }
15955 else
15956 {
15957 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
15958 task.mstrCurrentPassword.c_str(),
15959 &hCryptoCtx);
15960 if (RT_FAILURE(vrc))
15961 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
15962 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15963 if (RT_FAILURE(vrc))
15964 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
15965 }
15966
15967 char *pszKeyStore;
15968 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15969 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15970 if (RT_FAILURE(vrc))
15971 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
15972 mData->mstrKeyStore = pszKeyStore;
15973 RTStrFree(pszKeyStore);
15974 mData->mstrKeyId = task.mstrNewPasswordId;
15975 size_t cbPassword = task.mstrNewPassword.length() + 1;
15976 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
15977 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
15978 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
15979 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
15980
15981 /*
15982 * Remove backuped config after saving because it can contain
15983 * unencrypted version of the config
15984 */
15985 fSave |= Machine::SaveS_RemoveBackup;
15986 }
15987 else
15988 {
15989 mData->mstrKeyId.setNull();
15990 mData->mstrKeyStore.setNull();
15991 }
15992
15993 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
15994 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
15995 Bstr bstrNewPassword(task.mstrNewPassword);
15996 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
15997 /* encrypt media */
15998 alock.release();
15999 for (MediaList::iterator it = task.mllMedia.begin();
16000 it != task.mllMedia.end();
16001 ++it)
16002 {
16003 ComPtr<IProgress> pProgress1;
16004 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16005 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16006 pProgress1.asOutParam());
16007 if (FAILED(hrc)) throw hrc;
16008 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16009 if (FAILED(hrc)) throw hrc;
16010 }
16011 alock.acquire();
16012
16013 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16014
16015 Utf8Str strFullSnapshotFolder;
16016 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16017
16018 /* .sav files (main and snapshots) */
16019 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16020 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16021 if (FAILED(hrc))
16022 /* the helper function already sets error object */
16023 throw hrc;
16024
16025 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16026
16027 /* .nvram files */
16028 com::Utf8Str strNVRAMKeyId;
16029 com::Utf8Str strNVRAMKeyStore;
16030 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16031 if (FAILED(hrc))
16032 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16033
16034 Utf8Str strMachineFolder;
16035 i_calculateFullPath(".", strMachineFolder);
16036
16037 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16038 if (FAILED(hrc))
16039 /* the helper function already sets error object */
16040 throw hrc;
16041
16042 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16043 if (FAILED(hrc))
16044 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16045
16046 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16047
16048 /* .log files */
16049 com::Utf8Str strLogFolder;
16050 i_getLogFolder(strLogFolder);
16051 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16052 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16053 if (FAILED(hrc))
16054 /* the helper function already sets error object */
16055 throw hrc;
16056
16057 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16058
16059 i_saveSettings(NULL, alock, fSave);
16060 }
16061 catch (HRESULT hrcXcpt)
16062 {
16063 hrc = hrcXcpt;
16064 mData->mstrKeyId = strOldKeyId;
16065 mData->mstrKeyStore = strOldKeyStore;
16066 }
16067
16068 task.m_pProgress->i_notifyComplete(hrc);
16069
16070 LogFlowThisFuncLeave();
16071}
16072#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16073
16074HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16075 const com::Utf8Str &aCipher,
16076 const com::Utf8Str &aNewPassword,
16077 const com::Utf8Str &aNewPasswordId,
16078 BOOL aForce,
16079 ComPtr<IProgress> &aProgress)
16080{
16081 LogFlowFuncEnter();
16082
16083#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16084 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16085 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16086#else
16087 /* make the VM accessible */
16088 if (!mData->mAccessible)
16089 {
16090 if ( aCurrentPassword.isEmpty()
16091 || mData->mstrKeyId.isEmpty())
16092 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16093
16094 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16095 if (FAILED(hrc))
16096 return hrc;
16097 }
16098
16099 AutoLimitedCaller autoCaller(this);
16100 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16101
16102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16103
16104 /* define media to be change encryption */
16105
16106 MediaList llMedia;
16107 for (MediumAttachmentList::iterator
16108 it = mMediumAttachments->begin();
16109 it != mMediumAttachments->end();
16110 ++it)
16111 {
16112 ComObjPtr<MediumAttachment> &pAttach = *it;
16113 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16114
16115 if (!pMedium.isNull())
16116 {
16117 AutoCaller mac(pMedium);
16118 if (FAILED(mac.hrc())) return mac.hrc();
16119 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16120 DeviceType_T devType = pMedium->i_getDeviceType();
16121 if (devType == DeviceType_HardDisk)
16122 {
16123 /*
16124 * We need to move to last child because the Medium::changeEncryption
16125 * encrypts all chain of specified medium with its parents.
16126 * Also we perform cheking of back reference and children for
16127 * all media in the chain to raise error before we start any action.
16128 * So, we first move into root parent and then we will move to last child
16129 * keeping latter in the list for encryption.
16130 */
16131
16132 /* move to root parent */
16133 ComObjPtr<Medium> pTmpMedium = pMedium;
16134 while (pTmpMedium.isNotNull())
16135 {
16136 AutoCaller mediumAC(pTmpMedium);
16137 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16138 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16139
16140 /* Cannot encrypt media which are attached to more than one virtual machine. */
16141 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16142 if (cBackRefs > 1)
16143 return setError(VBOX_E_INVALID_OBJECT_STATE,
16144 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16145 pTmpMedium->i_getName().c_str(), cBackRefs);
16146
16147 size_t cChildren = pTmpMedium->i_getChildren().size();
16148 if (cChildren > 1)
16149 return setError(VBOX_E_INVALID_OBJECT_STATE,
16150 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16151 pTmpMedium->i_getName().c_str(), cChildren);
16152
16153 pTmpMedium = pTmpMedium->i_getParent();
16154 }
16155 /* move to last child */
16156 pTmpMedium = pMedium;
16157 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16158 {
16159 AutoCaller mediumAC(pTmpMedium);
16160 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16161 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16162
16163 /* Cannot encrypt media which are attached to more than one virtual machine. */
16164 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16165 if (cBackRefs > 1)
16166 return setError(VBOX_E_INVALID_OBJECT_STATE,
16167 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16168 pTmpMedium->i_getName().c_str(), cBackRefs);
16169
16170 size_t cChildren = pTmpMedium->i_getChildren().size();
16171 if (cChildren > 1)
16172 return setError(VBOX_E_INVALID_OBJECT_STATE,
16173 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16174 pTmpMedium->i_getName().c_str(), cChildren);
16175
16176 pTmpMedium = pTmpMedium->i_getChildren().front();
16177 }
16178 llMedia.push_back(pTmpMedium);
16179 }
16180 }
16181 }
16182
16183 ComObjPtr<Progress> pProgress;
16184 pProgress.createObject();
16185 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16186 static_cast<IMachine*>(this) /* aInitiator */,
16187 tr("Change encryption"),
16188 TRUE /* fCancellable */,
16189 (ULONG)(4 + + llMedia.size()), // cOperations
16190 tr("Change encryption of the mediuma"));
16191 if (FAILED(hrc))
16192 return hrc;
16193
16194 /* create and start the task on a separate thread (note that it will not
16195 * start working until we release alock) */
16196 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16197 aCurrentPassword, aCipher, aNewPassword,
16198 aNewPasswordId, aForce, llMedia);
16199 hrc = pTask->createThread();
16200 pTask = NULL;
16201 if (FAILED(hrc))
16202 return hrc;
16203
16204 pProgress.queryInterfaceTo(aProgress.asOutParam());
16205
16206 LogFlowFuncLeave();
16207
16208 return S_OK;
16209#endif
16210}
16211
16212HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16213 com::Utf8Str &aPasswordId)
16214{
16215#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16216 RT_NOREF(aCipher, aPasswordId);
16217 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16218#else
16219 AutoLimitedCaller autoCaller(this);
16220 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16221
16222 PCVBOXCRYPTOIF pCryptoIf = NULL;
16223 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16224 if (FAILED(hrc)) return hrc; /* Error is set */
16225
16226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16227
16228 if (mData->mstrKeyStore.isNotEmpty())
16229 {
16230 char *pszCipher = NULL;
16231 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16232 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16233 if (RT_SUCCESS(vrc))
16234 {
16235 aCipher = getCipherStringWithoutMode(pszCipher);
16236 RTStrFree(pszCipher);
16237 aPasswordId = mData->mstrKeyId;
16238 }
16239 else
16240 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16241 tr("Failed to query the encryption settings with %Rrc"),
16242 vrc);
16243 }
16244 else
16245 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16246
16247 mParent->i_releaseCryptoIf(pCryptoIf);
16248
16249 return hrc;
16250#endif
16251}
16252
16253HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16254{
16255#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16256 RT_NOREF(aPassword);
16257 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16258#else
16259 AutoLimitedCaller autoCaller(this);
16260 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16261
16262 PCVBOXCRYPTOIF pCryptoIf = NULL;
16263 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16264 if (FAILED(hrc)) return hrc; /* Error is set */
16265
16266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16267
16268 if (mData->mstrKeyStore.isNotEmpty())
16269 {
16270 char *pszCipher = NULL;
16271 uint8_t *pbDek = NULL;
16272 size_t cbDek = 0;
16273 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16274 &pbDek, &cbDek, &pszCipher);
16275 if (RT_SUCCESS(vrc))
16276 {
16277 RTStrFree(pszCipher);
16278 RTMemSaferFree(pbDek, cbDek);
16279 }
16280 else
16281 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16282 tr("The password supplied for the encrypted machine is incorrect"));
16283 }
16284 else
16285 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16286
16287 mParent->i_releaseCryptoIf(pCryptoIf);
16288
16289 return hrc;
16290#endif
16291}
16292
16293HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16294 const com::Utf8Str &aPassword)
16295{
16296#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16297 RT_NOREF(aId, aPassword);
16298 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16299#else
16300 AutoLimitedCaller autoCaller(this);
16301 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16302
16303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16304
16305 size_t cbPassword = aPassword.length() + 1;
16306 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16307
16308 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16309
16310 if ( mData->mAccessible
16311 && mData->mSession.mState == SessionState_Locked
16312 && mData->mSession.mLockType == LockType_VM
16313 && mData->mSession.mDirectControl != NULL)
16314 {
16315 /* get the console from the direct session */
16316 ComPtr<IConsole> console;
16317 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16318 ComAssertComRC(hrc);
16319 /* send passsword to console */
16320 console->AddEncryptionPassword(Bstr(aId).raw(),
16321 Bstr(aPassword).raw(),
16322 TRUE);
16323 }
16324
16325 if (mData->mstrKeyId == aId)
16326 {
16327 HRESULT hrc = checkEncryptionPassword(aPassword);
16328 if (FAILED(hrc))
16329 return hrc;
16330
16331 if (SUCCEEDED(hrc))
16332 {
16333 /*
16334 * Encryption is used and password is correct,
16335 * Reinit the machine if required.
16336 */
16337 BOOL fAccessible;
16338 alock.release();
16339 getAccessible(&fAccessible);
16340 alock.acquire();
16341 }
16342 }
16343
16344 /*
16345 * Add the password into the NvramStore only after
16346 * the machine becomes accessible and the NvramStore
16347 * contains key id and key store.
16348 */
16349 if (mNvramStore.isNotNull())
16350 mNvramStore->i_addPassword(aId, aPassword);
16351
16352 return S_OK;
16353#endif
16354}
16355
16356HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16357 const std::vector<com::Utf8Str> &aPasswords)
16358{
16359#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16360 RT_NOREF(aIds, aPasswords);
16361 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16362#else
16363 if (aIds.size() != aPasswords.size())
16364 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16365
16366 HRESULT hrc = S_OK;
16367 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16368 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16369
16370 return hrc;
16371#endif
16372}
16373
16374HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16375{
16376#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16377 RT_NOREF(autoCaller, aId);
16378 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16379#else
16380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16381
16382 if ( mData->mAccessible
16383 && mData->mSession.mState == SessionState_Locked
16384 && mData->mSession.mLockType == LockType_VM
16385 && mData->mSession.mDirectControl != NULL)
16386 {
16387 /* get the console from the direct session */
16388 ComPtr<IConsole> console;
16389 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16390 ComAssertComRC(hrc);
16391 /* send passsword to console */
16392 console->RemoveEncryptionPassword(Bstr(aId).raw());
16393 }
16394
16395 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16396 {
16397 if (Global::IsOnlineOrTransient(mData->mMachineState))
16398 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16399 alock.release();
16400 autoCaller.release();
16401 /* return because all passwords are purged when machine becomes inaccessible; */
16402 return i_setInaccessible();
16403 }
16404
16405 if (mNvramStore.isNotNull())
16406 mNvramStore->i_removePassword(aId);
16407 mData->mpKeyStore->deleteSecretKey(aId);
16408 return S_OK;
16409#endif
16410}
16411
16412HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16413{
16414#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16415 RT_NOREF(autoCaller);
16416 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16417#else
16418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16419
16420 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16421 {
16422 if (Global::IsOnlineOrTransient(mData->mMachineState))
16423 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16424 alock.release();
16425 autoCaller.release();
16426 /* return because all passwords are purged when machine becomes inaccessible; */
16427 return i_setInaccessible();
16428 }
16429
16430 mNvramStore->i_removeAllPasswords();
16431 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16432 return S_OK;
16433#endif
16434}
16435
16436#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16437HRESULT Machine::i_setInaccessible()
16438{
16439 if (!mData->mAccessible)
16440 return S_OK;
16441
16442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16443 VirtualBox *pParent = mParent;
16444 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16445 Guid id(i_getId());
16446
16447 alock.release();
16448
16449 uninit();
16450 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16451
16452 alock.acquire();
16453 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16454 return hrc;
16455}
16456#endif
16457
16458/* This isn't handled entirely by the wrapper generator yet. */
16459#ifdef VBOX_WITH_XPCOM
16460NS_DECL_CLASSINFO(SessionMachine)
16461NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16462
16463NS_DECL_CLASSINFO(SnapshotMachine)
16464NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16465#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