VirtualBox

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

最後變更 在這個檔案從108422是 108046,由 vboxsync 提交於 6 週 前

doc/manual,include/VBox,Frontends/{VBoxManage,VirtualBox/src},Main/{include,SharedFolder,Console,Machine,VirtualBox,VirtualBox.xidl}: Added global shared folders and adjusted fetching and handling of folders between shared folder types bugref:3544

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

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