VirtualBox

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

Last change on this file since 105016 was 105016, checked in by vboxsync, 5 months ago

doc/manual,include/VBox,Frontends/VBoxManage,HostServices/SharedFolders,
Main/{include,SharedFolder,Console,Machine,VirtualBox.xidl}: Add a
new attribute to ISharedFolder for specifying a symbolic link creation
policy to restrict the source pathname when creating symbolic links
within a guest. The symbolic link policies are represented by a new
enumeration of type SymlinkPolicy_T which includes values for no
restrictions ('any'), symlink sources only within the subtree of the
share ('subtree'), symlink sources as any relative path ('relative'),
and no symlinks allowed ('forbidden'). The symlink policy can only be
applied to permanent shared folders at this stage. bugref:10619

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 581.4 KB
Line 
1/* $Id: MachineImpl.cpp 105016 2024-06-25 10:28:21Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "StorageControllerImpl.h"
61#include "MachineImplMoveVM.h"
62#include "ExtPackManagerImpl.h"
63#include "MachineLaunchVMCommonWorker.h"
64#include "CryptoUtils.h"
65
66// generated header
67#include "VBoxEvents.h"
68
69#ifdef VBOX_WITH_USB
70# include "USBProxyService.h"
71#endif
72
73#include "AutoCaller.h"
74#include "HashedPw.h"
75#include "Performance.h"
76#include "StringifyEnums.h"
77
78#include <iprt/asm.h>
79#include <iprt/path.h>
80#include <iprt/dir.h>
81#include <iprt/env.h>
82#include <iprt/lockvalidator.h>
83#include <iprt/memsafer.h>
84#include <iprt/process.h>
85#include <iprt/cpp/utils.h>
86#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
87#include <iprt/sha.h>
88#include <iprt/string.h>
89#include <iprt/ctype.h>
90
91#include <VBox/com/array.h>
92#include <VBox/com/list.h>
93#include <VBox/VBoxCryptoIf.h>
94
95#include <VBox/err.h>
96#include <VBox/param.h>
97#include <VBox/settings.h>
98#include <VBox/VMMDev.h>
99#include <VBox/vmm/ssm.h>
100
101#ifdef VBOX_WITH_GUEST_PROPS
102# include <VBox/HostServices/GuestPropertySvc.h>
103# include <VBox/com/array.h>
104#endif
105
106#ifdef VBOX_WITH_SHARED_CLIPBOARD
107# include <VBox/HostServices/VBoxClipboardSvc.h>
108#endif
109
110#include "VBox/com/MultiResult.h"
111
112#include <algorithm>
113
114#ifdef VBOX_WITH_DTRACE_R3_MAIN
115# include "dtrace/VBoxAPI.h"
116#endif
117
118#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
119# define HOSTSUFF_EXE ".exe"
120#else /* !RT_OS_WINDOWS */
121# define HOSTSUFF_EXE ""
122#endif /* !RT_OS_WINDOWS */
123
124// defines / prototypes
125/////////////////////////////////////////////////////////////////////////////
126
127#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
128# define BUF_DATA_SIZE _64K
129
130enum CipherMode
131{
132 CipherModeGcm = 0,
133 CipherModeCtr,
134 CipherModeXts,
135 CipherModeMax
136};
137
138enum AesSize
139{
140 Aes128 = 0,
141 Aes256,
142 AesMax
143};
144
145const char *g_apszCipher[AesMax][CipherModeMax] =
146{
147 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
148 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
149};
150const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
151
152static const char *getCipherString(const char *pszAlgo, const int iMode)
153{
154 if (iMode >= CipherModeMax)
155 return pszAlgo;
156
157 for (int i = 0; i < AesMax; i++)
158 {
159 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
160 return g_apszCipher[i][iMode];
161 }
162 return pszAlgo;
163}
164
165static const char *getCipherStringWithoutMode(const char *pszAlgo)
166{
167 for (int i = 0; i < AesMax; i++)
168 {
169 for (int j = 0; j < CipherModeMax; j++)
170 {
171 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
172 return g_apszCipherAlgo[i];
173 }
174 }
175 return pszAlgo;
176}
177#endif
178
179/////////////////////////////////////////////////////////////////////////////
180// Machine::Data structure
181/////////////////////////////////////////////////////////////////////////////
182
183Machine::Data::Data()
184{
185 mRegistered = FALSE;
186 pMachineConfigFile = NULL;
187 /* Contains hints on what has changed when the user is using the VM (config
188 * changes, running the VM, ...). This is used to decide if a config needs
189 * to be written to disk. */
190 flModifications = 0;
191 /* VM modification usually also trigger setting the current state to
192 * "Modified". Although this is not always the case. An e.g. is the VM
193 * initialization phase or when snapshot related data is changed. The
194 * actually behavior is controlled by the following flag. */
195 m_fAllowStateModification = false;
196 mAccessible = FALSE;
197 /* mUuid is initialized in Machine::init() */
198
199 mMachineState = MachineState_PoweredOff;
200 RTTimeNow(&mLastStateChange);
201
202 mMachineStateDeps = 0;
203 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
204 mMachineStateChangePending = 0;
205
206 mCurrentStateModified = TRUE;
207 mGuestPropertiesModified = FALSE;
208
209 mSession.mPID = NIL_RTPROCESS;
210 mSession.mLockType = LockType_Null;
211 mSession.mState = SessionState_Unlocked;
212
213#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
214 mpKeyStore = NULL;
215#endif
216}
217
218Machine::Data::~Data()
219{
220 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
221 {
222 RTSemEventMultiDestroy(mMachineStateDepsSem);
223 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
224 }
225 if (pMachineConfigFile)
226 {
227 delete pMachineConfigFile;
228 pMachineConfigFile = NULL;
229 }
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine::HWData structure
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::HWData::HWData()
237{
238 /* default values for a newly created machine for x86. */
239 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
240 mMemorySize = 128;
241 mCPUCount = 1;
242 mCPUHotPlugEnabled = false;
243 mMemoryBalloonSize = 0;
244 mPageFusionEnabled = false;
245 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
246 mCpuIdPortabilityLevel = 0;
247 mCpuProfile = "host";
248
249 /* default boot order: floppy - DVD - HDD */
250 mBootOrder[0] = DeviceType_Floppy;
251 mBootOrder[1] = DeviceType_DVD;
252 mBootOrder[2] = DeviceType_HardDisk;
253 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
254 mBootOrder[i] = DeviceType_Null;
255
256 mClipboardMode = ClipboardMode_Disabled;
257 mClipboardFileTransfersEnabled = FALSE;
258
259 mDnDMode = DnDMode_Disabled;
260
261 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
262 mPointingHIDType = PointingHIDType_PS2Mouse;
263 mParavirtProvider = ParavirtProvider_Default;
264 mEmulatedUSBCardReaderEnabled = FALSE;
265
266 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
267 mCPUAttached[i] = false;
268
269 mIOCacheEnabled = true;
270 mIOCacheSize = 5; /* 5MB */
271}
272
273Machine::HWData::~HWData()
274{
275}
276
277/////////////////////////////////////////////////////////////////////////////
278// Machine class
279/////////////////////////////////////////////////////////////////////////////
280
281// constructor / destructor
282/////////////////////////////////////////////////////////////////////////////
283
284Machine::Machine() :
285#ifdef VBOX_WITH_RESOURCE_USAGE_API
286 mCollectorGuest(NULL),
287#endif
288 mPeer(NULL),
289 mParent(NULL),
290 mSerialPorts(),
291 mParallelPorts(),
292 uRegistryNeedsSaving(0)
293{}
294
295Machine::~Machine()
296{}
297
298HRESULT Machine::FinalConstruct()
299{
300 LogFlowThisFunc(("\n"));
301 return BaseFinalConstruct();
302}
303
304void Machine::FinalRelease()
305{
306 LogFlowThisFunc(("\n"));
307 uninit();
308 BaseFinalRelease();
309}
310
311/**
312 * Initializes a new machine instance; this init() variant creates a new, empty machine.
313 * This gets called from VirtualBox::CreateMachine().
314 *
315 * @param aParent Associated parent object.
316 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
317 * @param strName Name for the machine.
318 * @param aArchitecture Architecture to use for the machine.
319 * If a valid guest OS type is set via \a aOsType, the guest OS' type will be used instead then.
320 * @param llGroups list of groups for the machine.
321 * @param strOsType OS Type string (stored as is if aOsType is NULL).
322 * @param aOsType OS Type of this machine or NULL.
323 * @param aId UUID for the new machine.
324 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
325 * @param fDirectoryIncludesUUID
326 * Whether the use a special VM directory naming scheme (includes the UUID).
327 * @param aCipher The cipher to encrypt the VM with.
328 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
329 * @param aPassword The password to encrypt the VM with.
330 *
331 * @return Success indicator. if not S_OK, the machine object is invalid.
332 */
333HRESULT Machine::init(VirtualBox *aParent,
334 const Utf8Str &strConfigFile,
335 const Utf8Str &strName,
336 PlatformArchitecture_T aArchitecture,
337 const StringsList &llGroups,
338 const Utf8Str &strOsType,
339 GuestOSType *aOsType,
340 const Guid &aId,
341 bool fForceOverwrite,
342 bool fDirectoryIncludesUUID,
343 const com::Utf8Str &aCipher,
344 const com::Utf8Str &aPasswordId,
345 const com::Utf8Str &aPassword)
346{
347 LogFlowThisFuncEnter();
348 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
349
350#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
351 RT_NOREF(aCipher);
352 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
353 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
354#endif
355
356 /* Enclose the state transition NotReady->InInit->Ready */
357 AutoInitSpan autoInitSpan(this);
358 AssertReturn(autoInitSpan.isOk(), E_FAIL);
359
360 HRESULT hrc = initImpl(aParent, strConfigFile);
361 if (FAILED(hrc)) return hrc;
362
363#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
364 com::Utf8Str strSsmKeyId;
365 com::Utf8Str strSsmKeyStore;
366 com::Utf8Str strNVRAMKeyId;
367 com::Utf8Str strNVRAMKeyStore;
368
369 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
370 {
371 /* Resolve the cryptographic interface. */
372 PCVBOXCRYPTOIF pCryptoIf = NULL;
373 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
374 if (SUCCEEDED(hrc))
375 {
376 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
377 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
378 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
379
380 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
381 {
382 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
383 if (!pszCipher)
384 {
385 hrc = setError(VBOX_E_NOT_SUPPORTED,
386 tr("The cipher '%s' is not supported"), aCipher.c_str());
387 break;
388 }
389
390 VBOXCRYPTOCTX hCryptoCtx;
391 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
392 if (RT_FAILURE(vrc))
393 {
394 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
395 break;
396 }
397
398 char *pszKeyStore;
399 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
400 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
401 AssertRC(vrc2);
402
403 if (RT_FAILURE(vrc))
404 {
405 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
406 break;
407 }
408
409 *(astrKeyStore[i]) = pszKeyStore;
410 RTMemFree(pszKeyStore);
411 *(astrKeyId[i]) = aPasswordId;
412 }
413
414 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
415 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
416
417 if (FAILED(hrc))
418 return hrc; /* Error is set. */
419 }
420 else
421 return hrc; /* Error is set. */
422 }
423#endif
424
425 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
426 if (FAILED(hrc)) return hrc;
427
428 if (SUCCEEDED(hrc))
429 {
430 // create an empty machine config
431 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
432
433 hrc = initDataAndChildObjects();
434 }
435
436 if (SUCCEEDED(hrc))
437 {
438#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
439 mSSData->strStateKeyId = strSsmKeyId;
440 mSSData->strStateKeyStore = strSsmKeyStore;
441#endif
442
443 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
444 mData->mAccessible = TRUE;
445
446 unconst(mData->mUuid) = aId;
447
448 mUserData->s.strName = strName;
449
450 if (llGroups.size())
451 mUserData->s.llGroups = llGroups;
452
453 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
454 // the "name sync" flag determines whether the machine directory gets renamed along
455 // with the machine file; say so if the settings file name is the same as the
456 // settings file parent directory (machine directory)
457 mUserData->s.fNameSync = i_isInOwnDir();
458
459 // initialize the default snapshots folder
460 hrc = COMSETTER(SnapshotFolder)(NULL);
461 AssertComRC(hrc);
462
463 /* Use the platform architecture which was handed-in by default. */
464 PlatformArchitecture_T enmPlatformArch = aArchitecture;
465
466 if (aOsType)
467 {
468 /* Store OS type */
469 mUserData->s.strOsType = aOsType->i_id();
470
471 /* Use the platform architecture of the found guest OS type. */
472 enmPlatformArch = aOsType->i_platformArchitecture();
473 }
474 else if (!strOsType.isEmpty())
475 {
476 /* Store OS type */
477 mUserData->s.strOsType = strOsType;
478 }
479
480 /* Set the platform architecture first before applying the defaults below. */
481 hrc = mPlatform->i_initArchitecture(enmPlatformArch);
482 if (FAILED(hrc)) return hrc;
483
484 /* Apply platform defaults. */
485 mPlatform->i_applyDefaults(aOsType);
486 i_platformPropertiesUpdate();
487
488 /* Apply firmware defaults. */
489 mFirmwareSettings->i_applyDefaults(aOsType);
490
491 /* Apply TPM defaults. */
492 mTrustedPlatformModule->i_applyDefaults(aOsType);
493
494 /* Apply recording defaults. */
495 mRecordingSettings->i_applyDefaults();
496
497 /* Apply network adapters defaults */
498 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
499 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
500
501 /* Apply serial port defaults */
502 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
503 mSerialPorts[slot]->i_applyDefaults(aOsType);
504
505 /* Apply parallel port defaults */
506 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
507 mParallelPorts[slot]->i_applyDefaults();
508
509 /* Enable the VMMDev testing feature for bootsector VMs: */
510 if (aOsType && aOsType->i_id() == GUEST_OS_ID_STR_X64("VBoxBS"))
511 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
512
513#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
514 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
515#endif
516 if (SUCCEEDED(hrc))
517 {
518 /* At this point the changing of the current state modification
519 * flag is allowed. */
520 i_allowStateModification();
521
522 /* commit all changes made during the initialization */
523 i_commit();
524 }
525 }
526
527 /* Confirm a successful initialization when it's the case */
528 if (SUCCEEDED(hrc))
529 {
530#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
531 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
532 {
533 size_t cbPassword = aPassword.length() + 1;
534 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
535 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
536 }
537#endif
538
539 if (mData->mAccessible)
540 autoInitSpan.setSucceeded();
541 else
542 autoInitSpan.setLimited();
543 }
544
545 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
546 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
547 mData->mRegistered,
548 mData->mAccessible,
549 hrc));
550
551 LogFlowThisFuncLeave();
552
553 return hrc;
554}
555
556/**
557 * Initializes a new instance with data from machine XML (formerly Init_Registered).
558 * Gets called in two modes:
559 *
560 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
561 * UUID is specified and we mark the machine as "registered";
562 *
563 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
564 * and the machine remains unregistered until RegisterMachine() is called.
565 *
566 * @param aParent Associated parent object
567 * @param strConfigFile Local file system path to the VM settings file (can
568 * be relative to the VirtualBox config directory).
569 * @param aId UUID of the machine or NULL (see above).
570 * @param strPassword Password for decrypting the config
571 *
572 * @return Success indicator. if not S_OK, the machine object is invalid
573 */
574HRESULT Machine::initFromSettings(VirtualBox *aParent,
575 const Utf8Str &strConfigFile,
576 const Guid *aId,
577 const com::Utf8Str &strPassword)
578{
579 LogFlowThisFuncEnter();
580 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
581
582 PCVBOXCRYPTOIF pCryptoIf = NULL;
583#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
584 if (strPassword.isNotEmpty())
585 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
586#else
587 if (strPassword.isNotEmpty())
588 {
589 /* Get at the crpytographic interface. */
590 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
591 if (FAILED(hrc))
592 return hrc; /* Error is set. */
593 }
594#endif
595
596 /* Enclose the state transition NotReady->InInit->Ready */
597 AutoInitSpan autoInitSpan(this);
598 AssertReturn(autoInitSpan.isOk(), E_FAIL);
599
600 HRESULT hrc = initImpl(aParent, strConfigFile);
601 if (FAILED(hrc)) return hrc;
602
603 if (aId)
604 {
605 // loading a registered VM:
606 unconst(mData->mUuid) = *aId;
607 mData->mRegistered = TRUE;
608 // now load the settings from XML:
609 hrc = i_registeredInit();
610 // this calls initDataAndChildObjects() and loadSettings()
611 }
612 else
613 {
614 // opening an unregistered VM (VirtualBox::OpenMachine()):
615 hrc = initDataAndChildObjects();
616 if (SUCCEEDED(hrc))
617 {
618 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
619 mData->mAccessible = TRUE;
620
621 try
622 {
623 // load and parse machine XML; this will throw on XML or logic errors
624 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
625 pCryptoIf,
626 strPassword.c_str());
627
628 // reject VM UUID duplicates, they can happen if someone
629 // tries to register an already known VM config again
630 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
631 true /* fPermitInaccessible */,
632 false /* aDoSetError */,
633 NULL) != VBOX_E_OBJECT_NOT_FOUND)
634 {
635 throw setError(E_FAIL,
636 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
637 mData->m_strConfigFile.c_str());
638 }
639
640 // use UUID from machine config
641 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
642
643#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
644 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
645 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
646 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
647 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
648#endif
649
650 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
651 {
652 // We just set the inaccessible state and fill the error info allowing the caller
653 // to register the machine with encrypted config even if the password is incorrect
654 mData->mAccessible = FALSE;
655
656 /* fetch the current error info */
657 mData->mAccessError = com::ErrorInfo();
658
659 setError(VBOX_E_PASSWORD_INCORRECT,
660 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
661 mData->pMachineConfigFile->uuid.raw());
662 }
663 else
664 {
665#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
666 if (strPassword.isNotEmpty())
667 {
668 size_t cbKey = strPassword.length() + 1; /* Include terminator */
669 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
670 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
671 }
672#endif
673
674 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
675 if (FAILED(hrc)) throw hrc;
676
677 /* At this point the changing of the current state modification
678 * flag is allowed. */
679 i_allowStateModification();
680
681 i_commit();
682 }
683 }
684 catch (HRESULT err)
685 {
686 /* we assume that error info is set by the thrower */
687 hrc = err;
688 }
689 catch (...)
690 {
691 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
692 }
693 }
694 }
695
696 /* Confirm a successful initialization when it's the case */
697 if (SUCCEEDED(hrc))
698 {
699 if (mData->mAccessible)
700 autoInitSpan.setSucceeded();
701 else
702 {
703 autoInitSpan.setLimited();
704
705 // uninit media from this machine's media registry, or else
706 // reloading the settings will fail
707 mParent->i_unregisterMachineMedia(i_getId());
708 }
709 }
710
711#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
712 if (pCryptoIf)
713 {
714 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
715 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
716 }
717#endif
718
719 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
720 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
721
722 LogFlowThisFuncLeave();
723
724 return hrc;
725}
726
727/**
728 * Initializes a new instance from a machine config that is already in memory
729 * (import OVF case). Since we are importing, the UUID in the machine
730 * config is ignored and we always generate a fresh one.
731 *
732 * @param aParent Associated parent object.
733 * @param strName Name for the new machine; this overrides what is specified in config.
734 * @param strSettingsFilename File name of .vbox file.
735 * @param config Machine configuration loaded and parsed from XML.
736 *
737 * @return Success indicator. if not S_OK, the machine object is invalid
738 */
739HRESULT Machine::init(VirtualBox *aParent,
740 const Utf8Str &strName,
741 const Utf8Str &strSettingsFilename,
742 const settings::MachineConfigFile &config)
743{
744 LogFlowThisFuncEnter();
745
746 /* Enclose the state transition NotReady->InInit->Ready */
747 AutoInitSpan autoInitSpan(this);
748 AssertReturn(autoInitSpan.isOk(), E_FAIL);
749
750 HRESULT hrc = initImpl(aParent, strSettingsFilename);
751 if (FAILED(hrc)) return hrc;
752
753 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
754 if (FAILED(hrc)) return hrc;
755
756 hrc = initDataAndChildObjects();
757 if (SUCCEEDED(hrc))
758 {
759 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
760 mData->mAccessible = TRUE;
761
762 // create empty machine config for instance data
763 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
764
765 // generate fresh UUID, ignore machine config
766 unconst(mData->mUuid).create();
767
768 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
769
770 // override VM name as well, it may be different
771 mUserData->s.strName = strName;
772
773 if (SUCCEEDED(hrc))
774 {
775 /* At this point the changing of the current state modification
776 * flag is allowed. */
777 i_allowStateModification();
778
779 /* commit all changes made during the initialization */
780 i_commit();
781 }
782 }
783
784 /* Confirm a successful initialization when it's the case */
785 if (SUCCEEDED(hrc))
786 {
787 if (mData->mAccessible)
788 autoInitSpan.setSucceeded();
789 else
790 {
791 /* Ignore all errors from unregistering, they would destroy
792- * the more interesting error information we already have,
793- * pinpointing the issue with the VM config. */
794 ErrorInfoKeeper eik;
795
796 autoInitSpan.setLimited();
797
798 // uninit media from this machine's media registry, or else
799 // reloading the settings will fail
800 mParent->i_unregisterMachineMedia(i_getId());
801 }
802 }
803
804 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
805 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
806
807 LogFlowThisFuncLeave();
808
809 return hrc;
810}
811
812/**
813 * Shared code between the various init() implementations.
814 *
815 * @returns HRESULT
816 * @param aParent The VirtualBox object.
817 * @param strConfigFile Settings file.
818 */
819HRESULT Machine::initImpl(VirtualBox *aParent,
820 const Utf8Str &strConfigFile)
821{
822 LogFlowThisFuncEnter();
823
824 AssertReturn(aParent, E_INVALIDARG);
825 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
826
827 HRESULT hrc = S_OK;
828
829 /* share the parent weakly */
830 unconst(mParent) = aParent;
831
832 /* allocate the essential machine data structure (the rest will be
833 * allocated later by initDataAndChildObjects() */
834 mData.allocate();
835
836 /* memorize the config file name (as provided) */
837 mData->m_strConfigFile = strConfigFile;
838
839 /* get the full file name */
840 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
841 if (RT_FAILURE(vrc1))
842 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
843 tr("Invalid machine settings file name '%s' (%Rrc)"),
844 strConfigFile.c_str(),
845 vrc1);
846
847#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
848 /** @todo Only create when the machine is going to be encrypted. */
849 /* Non-pageable memory is not accessible for non-VM process */
850 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
851 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
852#endif
853
854 LogFlowThisFuncLeave();
855
856 return hrc;
857}
858
859/**
860 * Tries to create a machine settings file in the path stored in the machine
861 * instance data. Used when a new machine is created to fail gracefully if
862 * the settings file could not be written (e.g. because machine dir is read-only).
863 * @return
864 */
865HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
866{
867 HRESULT hrc = S_OK;
868
869 // when we create a new machine, we must be able to create the settings file
870 RTFILE f = NIL_RTFILE;
871 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
872 if ( RT_SUCCESS(vrc)
873 || vrc == VERR_SHARING_VIOLATION
874 )
875 {
876 if (RT_SUCCESS(vrc))
877 RTFileClose(f);
878 if (!fForceOverwrite)
879 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
880 else
881 {
882 /* try to delete the config file, as otherwise the creation
883 * of a new settings file will fail. */
884 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
885 }
886 }
887 else if ( vrc != VERR_FILE_NOT_FOUND
888 && vrc != VERR_PATH_NOT_FOUND
889 )
890 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
891 mData->m_strConfigFileFull.c_str(), vrc);
892 return hrc;
893}
894
895/**
896 * Initializes the registered machine by loading the settings file.
897 * This method is separated from #init() in order to make it possible to
898 * retry the operation after VirtualBox startup instead of refusing to
899 * startup the whole VirtualBox server in case if the settings file of some
900 * registered VM is invalid or inaccessible.
901 *
902 * @note Must be always called from this object's write lock
903 * (unless called from #init() that doesn't need any locking).
904 * @note Locks the mUSBController method for writing.
905 * @note Subclasses must not call this method.
906 */
907HRESULT Machine::i_registeredInit()
908{
909 AssertReturn(!i_isSessionMachine(), E_FAIL);
910 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
911 AssertReturn(mData->mUuid.isValid(), E_FAIL);
912 AssertReturn(!mData->mAccessible, E_FAIL);
913
914 HRESULT hrc = initDataAndChildObjects();
915 if (SUCCEEDED(hrc))
916 {
917 /* Temporarily reset the registered flag in order to let setters
918 * potentially called from loadSettings() succeed (isMutable() used in
919 * all setters will return FALSE for a Machine instance if mRegistered
920 * is TRUE). */
921 mData->mRegistered = FALSE;
922
923 PCVBOXCRYPTOIF pCryptoIf = NULL;
924 SecretKey *pKey = NULL;
925 const char *pszPassword = NULL;
926#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
927 /* Resolve password and cryptographic support interface if machine is encrypted. */
928 if (mData->mstrKeyId.isNotEmpty())
929 {
930 /* Get at the crpytographic interface. */
931 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
932 if (SUCCEEDED(hrc))
933 {
934 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
935 if (RT_SUCCESS(vrc))
936 pszPassword = (const char *)pKey->getKeyBuffer();
937 else
938 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
939 mData->mstrKeyId.c_str(), vrc);
940 }
941 }
942#else
943 RT_NOREF(pKey);
944#endif
945
946 if (SUCCEEDED(hrc))
947 {
948 try
949 {
950 // load and parse machine XML; this will throw on XML or logic errors
951 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
952 pCryptoIf, pszPassword);
953
954 if (mData->mUuid != mData->pMachineConfigFile->uuid)
955 throw setError(E_FAIL,
956 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
957 mData->pMachineConfigFile->uuid.raw(),
958 mData->m_strConfigFileFull.c_str(),
959 mData->mUuid.toString().c_str(),
960 mParent->i_settingsFilePath().c_str());
961
962#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
963 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
964 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
965 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
966 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
967
968 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
969 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
970 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
971 mData->pMachineConfigFile->uuid.raw());
972 else
973#endif
974 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
975 if (FAILED(hrc)) throw hrc;
976 }
977 catch (HRESULT err)
978 {
979 /* we assume that error info is set by the thrower */
980 hrc = err;
981 }
982 catch (...)
983 {
984 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
985 }
986
987 /* Restore the registered flag (even on failure) */
988 mData->mRegistered = TRUE;
989 }
990
991#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
992 if (pCryptoIf)
993 mParent->i_releaseCryptoIf(pCryptoIf);
994 if (pKey)
995 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
996#endif
997 }
998
999 if (SUCCEEDED(hrc))
1000 {
1001 /* Set mAccessible to TRUE only if we successfully locked and loaded
1002 * the settings file */
1003 mData->mAccessible = TRUE;
1004
1005 /* commit all changes made during loading the settings file */
1006 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1007 /// @todo r=klaus for some reason the settings loading logic backs up
1008 // the settings, and therefore a commit is needed. Should probably be changed.
1009 }
1010 else
1011 {
1012 /* If the machine is registered, then, instead of returning a
1013 * failure, we mark it as inaccessible and set the result to
1014 * success to give it a try later */
1015
1016 /* fetch the current error info */
1017 mData->mAccessError = com::ErrorInfo();
1018 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1019
1020 /* rollback all changes */
1021 i_rollback(false /* aNotify */);
1022
1023 // uninit media from this machine's media registry, or else
1024 // reloading the settings will fail
1025 mParent->i_unregisterMachineMedia(i_getId());
1026
1027 /* uninitialize the common part to make sure all data is reset to
1028 * default (null) values */
1029 uninitDataAndChildObjects();
1030
1031 hrc = S_OK;
1032 }
1033
1034 return hrc;
1035}
1036
1037/**
1038 * Uninitializes the instance.
1039 * Called either from FinalRelease() or by the parent when it gets destroyed.
1040 *
1041 * @note The caller of this method must make sure that this object
1042 * a) doesn't have active callers on the current thread and b) is not locked
1043 * by the current thread; otherwise uninit() will hang either a) due to
1044 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1045 * a dead-lock caused by this thread waiting for all callers on the other
1046 * threads are done but preventing them from doing so by holding a lock.
1047 */
1048void Machine::uninit()
1049{
1050 LogFlowThisFuncEnter();
1051
1052 Assert(!isWriteLockOnCurrentThread());
1053
1054 Assert(!uRegistryNeedsSaving);
1055 if (uRegistryNeedsSaving)
1056 {
1057 AutoCaller autoCaller(this);
1058 if (SUCCEEDED(autoCaller.hrc()))
1059 {
1060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1061 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1062 }
1063 }
1064
1065 /* Enclose the state transition Ready->InUninit->NotReady */
1066 AutoUninitSpan autoUninitSpan(this);
1067 if (autoUninitSpan.uninitDone())
1068 return;
1069
1070 Assert(!i_isSnapshotMachine());
1071 Assert(!i_isSessionMachine());
1072 Assert(!!mData);
1073
1074 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1075 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1076
1077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 if (!mData->mSession.mMachine.isNull())
1080 {
1081 /* Theoretically, this can only happen if the VirtualBox server has been
1082 * terminated while there were clients running that owned open direct
1083 * sessions. Since in this case we are definitely called by
1084 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1085 * won't happen on the client watcher thread (because it has a
1086 * VirtualBox caller for the duration of the
1087 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1088 * cannot happen until the VirtualBox caller is released). This is
1089 * important, because SessionMachine::uninit() cannot correctly operate
1090 * after we return from this method (it expects the Machine instance is
1091 * still valid). We'll call it ourselves below.
1092 */
1093 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1094 (SessionMachine*)mData->mSession.mMachine));
1095
1096 if (Global::IsOnlineOrTransient(mData->mMachineState))
1097 {
1098 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1099 /* set machine state using SessionMachine reimplementation */
1100 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1101 }
1102
1103 /*
1104 * Uninitialize SessionMachine using public uninit() to indicate
1105 * an unexpected uninitialization.
1106 */
1107 mData->mSession.mMachine->uninit();
1108 /* SessionMachine::uninit() must set mSession.mMachine to null */
1109 Assert(mData->mSession.mMachine.isNull());
1110 }
1111
1112 // uninit media from this machine's media registry, if they're still there
1113 Guid uuidMachine(i_getId());
1114
1115 /* the lock is no more necessary (SessionMachine is uninitialized) */
1116 alock.release();
1117
1118 /* XXX This will fail with
1119 * "cannot be closed because it is still attached to 1 virtual machines"
1120 * because at this point we did not call uninitDataAndChildObjects() yet
1121 * and therefore also removeBackReference() for all these media was not called! */
1122
1123 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1124 mParent->i_unregisterMachineMedia(uuidMachine);
1125
1126 // has machine been modified?
1127 if (mData->flModifications)
1128 {
1129 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1130 i_rollback(false /* aNotify */);
1131 }
1132
1133 if (mData->mAccessible)
1134 uninitDataAndChildObjects();
1135
1136#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1137 if (mData->mpKeyStore != NULL)
1138 delete mData->mpKeyStore;
1139#endif
1140
1141 /* free the essential data structure last */
1142 mData.free();
1143
1144 LogFlowThisFuncLeave();
1145}
1146
1147// Wrapped IMachine properties
1148/////////////////////////////////////////////////////////////////////////////
1149HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1150{
1151 /* mParent is constant during life time, no need to lock */
1152 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1153 aParent = pVirtualBox;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1159{
1160 /* mPlatform is constant during life time, no need to lock */
1161 ComObjPtr<Platform> pPlatform(mPlatform);
1162 aPlatform = pPlatform;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::getAccessible(BOOL *aAccessible)
1168{
1169 /* In some cases (medium registry related), it is necessary to be able to
1170 * go through the list of all machines. Happens when an inaccessible VM
1171 * has a sensible medium registry. */
1172 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT hrc = S_OK;
1176
1177 if (!mData->mAccessible)
1178 {
1179 /* try to initialize the VM once more if not accessible */
1180
1181 AutoReinitSpan autoReinitSpan(this);
1182 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1183
1184#ifdef DEBUG
1185 LogFlowThisFunc(("Dumping media backreferences\n"));
1186 mParent->i_dumpAllBackRefs();
1187#endif
1188
1189 if (mData->pMachineConfigFile)
1190 {
1191 // reset the XML file to force loadSettings() (called from i_registeredInit())
1192 // to parse it again; the file might have changed
1193 delete mData->pMachineConfigFile;
1194 mData->pMachineConfigFile = NULL;
1195 }
1196
1197 hrc = i_registeredInit();
1198
1199 if (SUCCEEDED(hrc) && mData->mAccessible)
1200 {
1201 autoReinitSpan.setSucceeded();
1202
1203 /* make sure interesting parties will notice the accessibility
1204 * state change */
1205 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1206 mParent->i_onMachineDataChanged(mData->mUuid);
1207 }
1208 }
1209
1210 if (SUCCEEDED(hrc))
1211 *aAccessible = mData->mAccessible;
1212
1213 LogFlowThisFuncLeave();
1214
1215 return hrc;
1216}
1217
1218HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1223 {
1224 /* return shortly */
1225 aAccessError = NULL;
1226 return S_OK;
1227 }
1228
1229 HRESULT hrc = S_OK;
1230
1231 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1232 hrc = errorInfo.createObject();
1233 if (SUCCEEDED(hrc))
1234 {
1235 errorInfo->init(mData->mAccessError.getResultCode(),
1236 mData->mAccessError.getInterfaceID().ref(),
1237 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1238 Utf8Str(mData->mAccessError.getText()));
1239 aAccessError = errorInfo;
1240 }
1241
1242 return hrc;
1243}
1244
1245HRESULT Machine::getName(com::Utf8Str &aName)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 aName = mUserData->s.strName;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setName(const com::Utf8Str &aName)
1255{
1256 // prohibit setting a UUID only as the machine name, or else it can
1257 // never be found by findMachine()
1258 Guid test(aName);
1259
1260 if (test.isValid())
1261 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1262
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1266 if (FAILED(hrc)) return hrc;
1267
1268 i_setModified(IsModified_MachineData);
1269 mUserData.backup();
1270 mUserData->s.strName = aName;
1271
1272 return S_OK;
1273}
1274
1275HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1276{
1277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 aDescription = mUserData->s.strDescription;
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1285{
1286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 // this can be done in principle in any state as it doesn't affect the VM
1289 // significantly, but play safe by not messing around while complex
1290 // activities are going on
1291 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1292 if (FAILED(hrc)) return hrc;
1293
1294 i_setModified(IsModified_MachineData);
1295 mUserData.backup();
1296 mUserData->s.strDescription = aDescription;
1297
1298 return S_OK;
1299}
1300
1301HRESULT Machine::getId(com::Guid &aId)
1302{
1303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1304
1305 aId = mData->mUuid;
1306
1307 return S_OK;
1308}
1309
1310HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1311{
1312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1313 aGroups.resize(mUserData->s.llGroups.size());
1314 size_t i = 0;
1315 for (StringsList::const_iterator
1316 it = mUserData->s.llGroups.begin();
1317 it != mUserData->s.llGroups.end();
1318 ++it, ++i)
1319 aGroups[i] = (*it);
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1325{
1326 StringsList llGroups;
1327 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1328 if (FAILED(hrc))
1329 return hrc;
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1334 if (FAILED(hrc)) return hrc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mUserData.backup();
1338 mUserData->s.llGroups = llGroups;
1339
1340 mParent->i_onMachineGroupsChanged(mData->mUuid);
1341 return S_OK;
1342}
1343
1344HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1345{
1346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1347
1348 aOSTypeId = mUserData->s.strOsType;
1349
1350 return S_OK;
1351}
1352
1353HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1354{
1355 /* look up the object by Id to check it is valid */
1356 ComObjPtr<GuestOSType> pGuestOSType;
1357 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1358
1359 /* when setting, always use the "etalon" value for consistency -- lookup
1360 * by ID is case-insensitive and the input value may have different case */
1361 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1362
1363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1364
1365 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1366 if (FAILED(hrc)) return hrc;
1367
1368 i_setModified(IsModified_MachineData);
1369 mUserData.backup();
1370 mUserData->s.strOsType = osTypeId;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1385{
1386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1387
1388 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1389 if (FAILED(hrc)) return hrc;
1390
1391 i_setModified(IsModified_MachineData);
1392 mHWData.backup();
1393 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1394
1395 return S_OK;
1396}
1397
1398HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1399{
1400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 *aPointingHIDType = mHWData->mPointingHIDType;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1408{
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1412 if (FAILED(hrc)) return hrc;
1413
1414 i_setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mPointingHIDType = aPointingHIDType;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 aParavirtDebug = mHWData->mParavirtDebug;
1426 return S_OK;
1427}
1428
1429HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1430{
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(hrc)) return hrc;
1435
1436 /** @todo Parse/validate options? */
1437 if (aParavirtDebug != mHWData->mParavirtDebug)
1438 {
1439 i_setModified(IsModified_MachineData);
1440 mHWData.backup();
1441 mHWData->mParavirtDebug = aParavirtDebug;
1442 }
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1448{
1449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 *aParavirtProvider = mHWData->mParavirtProvider;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1457{
1458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1461 if (FAILED(hrc)) return hrc;
1462
1463 if (aParavirtProvider != mHWData->mParavirtProvider)
1464 {
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mParavirtProvider = aParavirtProvider;
1468 }
1469
1470 return S_OK;
1471}
1472
1473HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1474{
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 *aParavirtProvider = mHWData->mParavirtProvider;
1478 switch (mHWData->mParavirtProvider)
1479 {
1480 case ParavirtProvider_None:
1481 case ParavirtProvider_HyperV:
1482 case ParavirtProvider_KVM:
1483 case ParavirtProvider_Minimal:
1484 break;
1485
1486 /* Resolve dynamic provider types to the effective types. */
1487 default:
1488 {
1489 ComObjPtr<GuestOSType> pGuestOSType;
1490 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1491 pGuestOSType);
1492 if (FAILED(hrc2) || pGuestOSType.isNull())
1493 {
1494 *aParavirtProvider = ParavirtProvider_None;
1495 break;
1496 }
1497
1498 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1499 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1500
1501 switch (mHWData->mParavirtProvider)
1502 {
1503 case ParavirtProvider_Legacy:
1504 {
1505 if (fOsXGuest)
1506 *aParavirtProvider = ParavirtProvider_Minimal;
1507 else
1508 *aParavirtProvider = ParavirtProvider_None;
1509 break;
1510 }
1511
1512 case ParavirtProvider_Default:
1513 {
1514 Assert(strlen(GUEST_OS_ID_STR_X64("")) > 0);
1515 if (fOsXGuest)
1516 *aParavirtProvider = ParavirtProvider_Minimal;
1517 else if ( mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows11")
1518 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows10")
1519 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows10")
1520 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows81")
1521 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows81")
1522 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows8")
1523 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows8")
1524 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows7")
1525 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows7")
1526 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("WindowsVista")
1527 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("WindowsVista")
1528 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1529 || mUserData->s.strOsType.startsWith("Windows201"))
1530 && mUserData->s.strOsType.endsWith(GUEST_OS_ID_STR_X64("")))
1531 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2012")
1532 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2012")
1533 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2008")
1534 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2008"))
1535 *aParavirtProvider = ParavirtProvider_HyperV;
1536 else if ( guestTypeFamilyId == "Linux"
1537 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux22") // Linux22 and Linux24{_64} excluded as they're too old
1538 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux24") // to have any KVM paravirtualization support.
1539 && mUserData->s.strOsType != GUEST_OS_ID_STR_X64("Linux24"))
1540 *aParavirtProvider = ParavirtProvider_KVM;
1541 else
1542 *aParavirtProvider = ParavirtProvider_None;
1543 break;
1544 }
1545
1546 default: AssertFailedBreak(); /* Shut up MSC. */
1547 }
1548 break;
1549 }
1550 }
1551
1552 Assert( *aParavirtProvider == ParavirtProvider_None
1553 || *aParavirtProvider == ParavirtProvider_Minimal
1554 || *aParavirtProvider == ParavirtProvider_HyperV
1555 || *aParavirtProvider == ParavirtProvider_KVM);
1556 return S_OK;
1557}
1558
1559HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1560{
1561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 aHardwareVersion = mHWData->mHWVersion;
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1569{
1570 /* check known version */
1571 Utf8Str hwVersion = aHardwareVersion;
1572 if ( hwVersion.compare("1") != 0
1573 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1574 return setError(E_INVALIDARG,
1575 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(hrc)) return hrc;
1581
1582 i_setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 mHWData->mHWVersion = aHardwareVersion;
1585
1586 return S_OK;
1587}
1588
1589HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1590{
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 if (!mHWData->mHardwareUUID.isZero())
1594 aHardwareUUID = mHWData->mHardwareUUID;
1595 else
1596 aHardwareUUID = mData->mUuid;
1597
1598 return S_OK;
1599}
1600
1601HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1602{
1603 if (!aHardwareUUID.isValid())
1604 return E_INVALIDARG;
1605
1606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1609 if (FAILED(hrc)) return hrc;
1610
1611 i_setModified(IsModified_MachineData);
1612 mHWData.backup();
1613 if (aHardwareUUID == mData->mUuid)
1614 mHWData->mHardwareUUID.clear();
1615 else
1616 mHWData->mHardwareUUID = aHardwareUUID;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aMemorySize = mHWData->mMemorySize;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setMemorySize(ULONG aMemorySize)
1631{
1632 /* check RAM limits */
1633 if ( aMemorySize < MM_RAM_MIN_IN_MB
1634 || aMemorySize > MM_RAM_MAX_IN_MB
1635 )
1636 return setError(E_INVALIDARG,
1637 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1638 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1639
1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1643 if (FAILED(hrc)) return hrc;
1644
1645 i_setModified(IsModified_MachineData);
1646 mHWData.backup();
1647 mHWData->mMemorySize = aMemorySize;
1648
1649 return S_OK;
1650}
1651
1652HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 *aCPUCount = mHWData->mCPUCount;
1657
1658 return S_OK;
1659}
1660
1661HRESULT Machine::setCPUCount(ULONG aCPUCount)
1662{
1663 /* check CPU limits */
1664 if ( aCPUCount < SchemaDefs::MinCPUCount
1665 || aCPUCount > SchemaDefs::MaxCPUCount
1666 )
1667 return setError(E_INVALIDARG,
1668 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1669 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1670
1671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1672
1673 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1674 if (mHWData->mCPUHotPlugEnabled)
1675 {
1676 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1677 {
1678 if (mHWData->mCPUAttached[idx])
1679 return setError(E_INVALIDARG,
1680 tr("There is still a CPU attached to socket %lu."
1681 "Detach the CPU before removing the socket"),
1682 aCPUCount, idx+1);
1683 }
1684 }
1685
1686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1687 if (FAILED(hrc)) return hrc;
1688
1689 i_setModified(IsModified_MachineData);
1690 mHWData.backup();
1691 mHWData->mCPUCount = aCPUCount;
1692
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1701
1702 return S_OK;
1703}
1704
1705HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1706{
1707 /* check throttle limits */
1708 if ( aCPUExecutionCap < 1
1709 || aCPUExecutionCap > 100
1710 )
1711 return setError(E_INVALIDARG,
1712 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1713 aCPUExecutionCap, 1, 100);
1714
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1718 if (FAILED(hrc)) return hrc;
1719
1720 alock.release();
1721 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1722 alock.acquire();
1723 if (FAILED(hrc)) return hrc;
1724
1725 i_setModified(IsModified_MachineData);
1726 mHWData.backup();
1727 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1728
1729 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1730 if (Global::IsOnline(mData->mMachineState))
1731 i_saveSettings(NULL, alock);
1732
1733 return S_OK;
1734}
1735
1736HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1737{
1738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1746{
1747 HRESULT hrc = S_OK;
1748
1749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1750
1751 hrc = i_checkStateDependency(MutableStateDep);
1752 if (FAILED(hrc)) return hrc;
1753
1754 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1755 {
1756 if (aCPUHotPlugEnabled)
1757 {
1758 i_setModified(IsModified_MachineData);
1759 mHWData.backup();
1760
1761 /* Add the amount of CPUs currently attached */
1762 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1763 mHWData->mCPUAttached[i] = true;
1764 }
1765 else
1766 {
1767 /*
1768 * We can disable hotplug only if the amount of maximum CPUs is equal
1769 * to the amount of attached CPUs
1770 */
1771 unsigned cCpusAttached = 0;
1772 unsigned iHighestId = 0;
1773
1774 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1775 {
1776 if (mHWData->mCPUAttached[i])
1777 {
1778 cCpusAttached++;
1779 iHighestId = i;
1780 }
1781 }
1782
1783 if ( (cCpusAttached != mHWData->mCPUCount)
1784 || (iHighestId >= mHWData->mCPUCount))
1785 return setError(E_INVALIDARG,
1786 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1787
1788 i_setModified(IsModified_MachineData);
1789 mHWData.backup();
1790 }
1791 }
1792
1793 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1794
1795 return hrc;
1796}
1797
1798HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1808{
1809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1812 if (SUCCEEDED(hrc))
1813 {
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1817 }
1818 return hrc;
1819}
1820
1821HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824 aCPUProfile = mHWData->mCpuProfile;
1825 return S_OK;
1826}
1827
1828HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1829{
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1832 if (SUCCEEDED(hrc))
1833 {
1834 i_setModified(IsModified_MachineData);
1835 mHWData.backup();
1836 /* Empty equals 'host'. */
1837 if (aCPUProfile.isNotEmpty())
1838 mHWData->mCpuProfile = aCPUProfile;
1839 else
1840 mHWData->mCpuProfile = "host";
1841 }
1842 return hrc;
1843}
1844
1845HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1846{
1847#ifdef VBOX_WITH_USB_CARDREADER
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1851
1852 return S_OK;
1853#else
1854 NOREF(aEmulatedUSBCardReaderEnabled);
1855 return E_NOTIMPL;
1856#endif
1857}
1858
1859HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1860{
1861#ifdef VBOX_WITH_USB_CARDREADER
1862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1865 if (FAILED(hrc)) return hrc;
1866
1867 i_setModified(IsModified_MachineData);
1868 mHWData.backup();
1869 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1870
1871 return S_OK;
1872#else
1873 NOREF(aEmulatedUSBCardReaderEnabled);
1874 return E_NOTIMPL;
1875#endif
1876}
1877
1878/** @todo this method should not be public */
1879HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1880{
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1884
1885 return S_OK;
1886}
1887
1888/**
1889 * Set the memory balloon size.
1890 *
1891 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1892 * we have to make sure that we never call IGuest from here.
1893 */
1894HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1895{
1896 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1897#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1898 /* check limits */
1899 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1900 return setError(E_INVALIDARG,
1901 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1902 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1903
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1907 if (FAILED(hrc)) return hrc;
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1912
1913 return S_OK;
1914#else
1915 NOREF(aMemoryBalloonSize);
1916 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1917#endif
1918}
1919
1920HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1921{
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1929{
1930#ifdef VBOX_WITH_PAGE_SHARING
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1934 if (FAILED(hrc)) return hrc;
1935
1936 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1937 i_setModified(IsModified_MachineData);
1938 mHWData.backup();
1939 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1940 return S_OK;
1941#else
1942 NOREF(aPageFusionEnabled);
1943 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1944#endif
1945}
1946
1947HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1948{
1949 /* mFirmwareSettings is constant during life time, no need to lock */
1950 aFirmwareSettings = mFirmwareSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1956{
1957 /* mTrustedPlatformModule is constant during life time, no need to lock */
1958 aTrustedPlatformModule = mTrustedPlatformModule;
1959
1960 return S_OK;
1961}
1962
1963HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1964{
1965 /* mNvramStore is constant during life time, no need to lock */
1966 aNvramStore = mNvramStore;
1967
1968 return S_OK;
1969}
1970
1971HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1972{
1973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 aRecordingSettings = mRecordingSettings;
1976
1977 return S_OK;
1978}
1979
1980HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1981{
1982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 aGraphicsAdapter = mGraphicsAdapter;
1985
1986 return S_OK;
1987}
1988
1989HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
1994
1995 return S_OK;
1996}
1997
1998HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
1999{
2000 /** @todo (r=dmik):
2001 * 1. Allow to change the name of the snapshot folder containing snapshots
2002 * 2. Rename the folder on disk instead of just changing the property
2003 * value (to be smart and not to leave garbage). Note that it cannot be
2004 * done here because the change may be rolled back. Thus, the right
2005 * place is #saveSettings().
2006 */
2007
2008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
2010 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2011 if (FAILED(hrc)) return hrc;
2012
2013 if (!mData->mCurrentSnapshot.isNull())
2014 return setError(E_FAIL,
2015 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2016
2017 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2018
2019 if (strSnapshotFolder.isEmpty())
2020 strSnapshotFolder = "Snapshots";
2021 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2022 if (RT_FAILURE(vrc))
2023 return setErrorBoth(E_FAIL, vrc,
2024 tr("Invalid snapshot folder '%s' (%Rrc)"),
2025 strSnapshotFolder.c_str(), vrc);
2026
2027 i_setModified(IsModified_MachineData);
2028 mUserData.backup();
2029
2030 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2031
2032 return S_OK;
2033}
2034
2035HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2036{
2037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2038
2039 aMediumAttachments.resize(mMediumAttachments->size());
2040 size_t i = 0;
2041 for (MediumAttachmentList::const_iterator
2042 it = mMediumAttachments->begin();
2043 it != mMediumAttachments->end();
2044 ++it, ++i)
2045 aMediumAttachments[i] = *it;
2046
2047 return S_OK;
2048}
2049
2050HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2051{
2052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 Assert(!!mVRDEServer);
2055
2056 aVRDEServer = mVRDEServer;
2057
2058 return S_OK;
2059}
2060
2061HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2062{
2063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 aAudioSettings = mAudioSettings;
2066
2067 return S_OK;
2068}
2069
2070HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2071{
2072#ifdef VBOX_WITH_VUSB
2073 clearError();
2074 MultiResult hrcMult(S_OK);
2075
2076# ifdef VBOX_WITH_USB
2077 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2078 if (FAILED(hrcMult)) return hrcMult;
2079# endif
2080
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 aUSBControllers.resize(mUSBControllers->size());
2084 size_t i = 0;
2085 for (USBControllerList::const_iterator
2086 it = mUSBControllers->begin();
2087 it != mUSBControllers->end();
2088 ++it, ++i)
2089 aUSBControllers[i] = *it;
2090
2091 return S_OK;
2092#else
2093 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2094 * extended error info to indicate that USB is simply not available
2095 * (w/o treating it as a failure), for example, as in OSE */
2096 NOREF(aUSBControllers);
2097 ReturnComNotImplemented();
2098#endif /* VBOX_WITH_VUSB */
2099}
2100
2101HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2102{
2103#ifdef VBOX_WITH_VUSB
2104 clearError();
2105 MultiResult hrcMult(S_OK);
2106
2107# ifdef VBOX_WITH_USB
2108 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2109 if (FAILED(hrcMult)) return hrcMult;
2110# endif
2111
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 aUSBDeviceFilters = mUSBDeviceFilters;
2115 return hrcMult;
2116#else
2117 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2118 * extended error info to indicate that USB is simply not available
2119 * (w/o treating it as a failure), for example, as in OSE */
2120 NOREF(aUSBDeviceFilters);
2121 ReturnComNotImplemented();
2122#endif /* VBOX_WITH_VUSB */
2123}
2124
2125HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2126{
2127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 aSettingsFilePath = mData->m_strConfigFileFull;
2130
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2135{
2136 RT_NOREF(aSettingsFilePath);
2137 ReturnComNotImplemented();
2138}
2139
2140HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2141{
2142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2145 if (FAILED(hrc)) return hrc;
2146
2147 if (!mData->pMachineConfigFile->fileExists())
2148 // this is a new machine, and no config file exists yet:
2149 *aSettingsModified = TRUE;
2150 else
2151 *aSettingsModified = (mData->flModifications != 0);
2152
2153 return S_OK;
2154}
2155
2156HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2157{
2158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 *aSessionState = mData->mSession.mState;
2161
2162 return S_OK;
2163}
2164
2165HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2166{
2167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 aSessionName = mData->mSession.mName;
2170
2171 return S_OK;
2172}
2173
2174HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2175{
2176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2177
2178 *aSessionPID = mData->mSession.mPID;
2179
2180 return S_OK;
2181}
2182
2183HRESULT Machine::getState(MachineState_T *aState)
2184{
2185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 *aState = mData->mMachineState;
2188 Assert(mData->mMachineState != MachineState_Null);
2189
2190 return S_OK;
2191}
2192
2193HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2194{
2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2198
2199 return S_OK;
2200}
2201
2202HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2203{
2204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2205
2206 aStateFilePath = mSSData->strStateFilePath;
2207
2208 return S_OK;
2209}
2210
2211HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2212{
2213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 i_getLogFolder(aLogFolder);
2216
2217 return S_OK;
2218}
2219
2220HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2221{
2222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2223
2224 aCurrentSnapshot = mData->mCurrentSnapshot;
2225
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2234 ? 0
2235 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2236
2237 return S_OK;
2238}
2239
2240HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2241{
2242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 /* Note: for machines with no snapshots, we always return FALSE
2245 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2246 * reasons :) */
2247
2248 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2249 ? FALSE
2250 : mData->mCurrentStateModified;
2251
2252 return S_OK;
2253}
2254
2255HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2256{
2257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 aSharedFolders.resize(mHWData->mSharedFolders.size());
2260 size_t i = 0;
2261 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2262 it = mHWData->mSharedFolders.begin();
2263 it != mHWData->mSharedFolders.end();
2264 ++it, ++i)
2265 aSharedFolders[i] = *it;
2266
2267 return S_OK;
2268}
2269
2270HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2271{
2272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 *aClipboardMode = mHWData->mClipboardMode;
2275
2276 return S_OK;
2277}
2278
2279HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2280{
2281 HRESULT hrc = S_OK;
2282
2283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2284
2285 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2286 if (FAILED(hrc)) return hrc;
2287
2288 alock.release();
2289 hrc = i_onClipboardModeChange(aClipboardMode);
2290 alock.acquire();
2291 if (FAILED(hrc)) return hrc;
2292
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mClipboardMode = aClipboardMode;
2296
2297 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2298 if (Global::IsOnline(mData->mMachineState))
2299 i_saveSettings(NULL, alock);
2300
2301 return S_OK;
2302}
2303
2304HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2305{
2306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2307
2308 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2309
2310 return S_OK;
2311}
2312
2313HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2314{
2315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2316
2317 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2318 if (FAILED(hrc)) return hrc;
2319
2320 alock.release();
2321 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2322 alock.acquire();
2323 if (FAILED(hrc)) return hrc;
2324
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2328
2329 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2330 if (Global::IsOnline(mData->mMachineState))
2331 i_saveSettings(NULL, alock);
2332
2333 return S_OK;
2334}
2335
2336HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2337{
2338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2339
2340 *aDnDMode = mHWData->mDnDMode;
2341
2342 return S_OK;
2343}
2344
2345HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2346{
2347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2348
2349 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2350 if (FAILED(hrc)) return hrc;
2351
2352 alock.release();
2353 hrc = i_onDnDModeChange(aDnDMode);
2354
2355 alock.acquire();
2356 if (FAILED(hrc)) return hrc;
2357
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mDnDMode = aDnDMode;
2361
2362 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2363 if (Global::IsOnline(mData->mMachineState))
2364 i_saveSettings(NULL, alock);
2365
2366 return S_OK;
2367}
2368
2369HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2370{
2371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 aStorageControllers.resize(mStorageControllers->size());
2374 size_t i = 0;
2375 for (StorageControllerList::const_iterator
2376 it = mStorageControllers->begin();
2377 it != mStorageControllers->end();
2378 ++it, ++i)
2379 aStorageControllers[i] = *it;
2380
2381 return S_OK;
2382}
2383
2384HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2385{
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 *aEnabled = mUserData->s.fTeleporterEnabled;
2389
2390 return S_OK;
2391}
2392
2393HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2394{
2395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2396
2397 /* Only allow it to be set to true when PoweredOff or Aborted.
2398 (Clearing it is always permitted.) */
2399 if ( aTeleporterEnabled
2400 && mData->mRegistered
2401 && ( !i_isSessionMachine()
2402 || ( mData->mMachineState != MachineState_PoweredOff
2403 && mData->mMachineState != MachineState_Teleported
2404 && mData->mMachineState != MachineState_Aborted
2405 )
2406 )
2407 )
2408 return setError(VBOX_E_INVALID_VM_STATE,
2409 tr("The machine is not powered off (state is %s)"),
2410 Global::stringifyMachineState(mData->mMachineState));
2411
2412 i_setModified(IsModified_MachineData);
2413 mUserData.backup();
2414 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2415
2416 return S_OK;
2417}
2418
2419HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2420{
2421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2424
2425 return S_OK;
2426}
2427
2428HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2429{
2430 if (aTeleporterPort >= _64K)
2431 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2432
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2436 if (FAILED(hrc)) return hrc;
2437
2438 i_setModified(IsModified_MachineData);
2439 mUserData.backup();
2440 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2441
2442 return S_OK;
2443}
2444
2445HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2446{
2447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2448
2449 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2450
2451 return S_OK;
2452}
2453
2454HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2455{
2456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2459 if (FAILED(hrc)) return hrc;
2460
2461 i_setModified(IsModified_MachineData);
2462 mUserData.backup();
2463 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2464
2465 return S_OK;
2466}
2467
2468HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2469{
2470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2471 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2472
2473 return S_OK;
2474}
2475
2476HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2477{
2478 /*
2479 * Hash the password first.
2480 */
2481 com::Utf8Str aT = aTeleporterPassword;
2482
2483 if (!aT.isEmpty())
2484 {
2485 if (VBoxIsPasswordHashed(&aT))
2486 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2487 VBoxHashPassword(&aT);
2488 }
2489
2490 /*
2491 * Do the update.
2492 */
2493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2494 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2495 if (SUCCEEDED(hrc))
2496 {
2497 i_setModified(IsModified_MachineData);
2498 mUserData.backup();
2499 mUserData->s.strTeleporterPassword = aT;
2500 }
2501
2502 return hrc;
2503}
2504
2505HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2515{
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2519 if (FAILED(hrc)) return hrc;
2520
2521 i_setModified(IsModified_MachineData);
2522 mHWData.backup();
2523 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2524
2525 return S_OK;
2526}
2527
2528HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2529{
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 *aIOCacheSize = mHWData->mIOCacheSize;
2533
2534 return S_OK;
2535}
2536
2537HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2538{
2539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2540
2541 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2542 if (FAILED(hrc)) return hrc;
2543
2544 i_setModified(IsModified_MachineData);
2545 mHWData.backup();
2546 mHWData->mIOCacheSize = aIOCacheSize;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2556 aKeyId = mSSData->strStateKeyId;
2557#else
2558 aKeyId = com::Utf8Str::Empty;
2559#endif
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2569 aKeyStore = mSSData->strStateKeyStore;
2570#else
2571 aKeyStore = com::Utf8Str::Empty;
2572#endif
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2582 aKeyId = mData->mstrLogKeyId;
2583#else
2584 aKeyId = com::Utf8Str::Empty;
2585#endif
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2595 aKeyStore = mData->mstrLogKeyStore;
2596#else
2597 aKeyStore = com::Utf8Str::Empty;
2598#endif
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2604{
2605 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2606
2607 return S_OK;
2608}
2609
2610
2611/**
2612 * @note Locks objects!
2613 */
2614HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2615 LockType_T aLockType)
2616{
2617 /* check the session state */
2618 SessionState_T state;
2619 HRESULT hrc = aSession->COMGETTER(State)(&state);
2620 if (FAILED(hrc)) return hrc;
2621
2622 if (state != SessionState_Unlocked)
2623 return setError(VBOX_E_INVALID_OBJECT_STATE,
2624 tr("The given session is busy"));
2625
2626 // get the client's IInternalSessionControl interface
2627 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2628 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2629 E_INVALIDARG);
2630
2631 // session name (only used in some code paths)
2632 Utf8Str strSessionName;
2633
2634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 if (!mData->mRegistered)
2637 return setError(E_UNEXPECTED,
2638 tr("The machine '%s' is not registered"),
2639 mUserData->s.strName.c_str());
2640
2641 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2642
2643 SessionState_T oldState = mData->mSession.mState;
2644 /* Hack: in case the session is closing and there is a progress object
2645 * which allows waiting for the session to be closed, take the opportunity
2646 * and do a limited wait (max. 1 second). This helps a lot when the system
2647 * is busy and thus session closing can take a little while. */
2648 if ( mData->mSession.mState == SessionState_Unlocking
2649 && mData->mSession.mProgress)
2650 {
2651 alock.release();
2652 mData->mSession.mProgress->WaitForCompletion(1000);
2653 alock.acquire();
2654 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2655 }
2656
2657 // try again now
2658 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2659 // (i.e. session machine exists)
2660 && (aLockType == LockType_Shared) // caller wants a shared link to the
2661 // existing session that holds the write lock:
2662 )
2663 {
2664 // OK, share the session... we are now dealing with three processes:
2665 // 1) VBoxSVC (where this code runs);
2666 // 2) process C: the caller's client process (who wants a shared session);
2667 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2668
2669 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2670 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2671 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2672 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2673 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2674
2675 /*
2676 * Release the lock before calling the client process. It's safe here
2677 * since the only thing to do after we get the lock again is to add
2678 * the remote control to the list (which doesn't directly influence
2679 * anything).
2680 */
2681 alock.release();
2682
2683 // get the console of the session holding the write lock (this is a remote call)
2684 ComPtr<IConsole> pConsoleW;
2685 if (mData->mSession.mLockType == LockType_VM)
2686 {
2687 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2688 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2689 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2690 if (FAILED(hrc))
2691 // the failure may occur w/o any error info (from RPC), so provide one
2692 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2693 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2694 }
2695
2696 // share the session machine and W's console with the caller's session
2697 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2698 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2699 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2700
2701 if (FAILED(hrc))
2702 // the failure may occur w/o any error info (from RPC), so provide one
2703 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2704 alock.acquire();
2705
2706 // need to revalidate the state after acquiring the lock again
2707 if (mData->mSession.mState != SessionState_Locked)
2708 {
2709 pSessionControl->Uninitialize();
2710 return setError(VBOX_E_INVALID_SESSION_STATE,
2711 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2712 mUserData->s.strName.c_str());
2713 }
2714
2715 // add the caller's session to the list
2716 mData->mSession.mRemoteControls.push_back(pSessionControl);
2717 }
2718 else if ( mData->mSession.mState == SessionState_Locked
2719 || mData->mSession.mState == SessionState_Unlocking
2720 )
2721 {
2722 // sharing not permitted, or machine still unlocking:
2723 return setError(VBOX_E_INVALID_OBJECT_STATE,
2724 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2725 mUserData->s.strName.c_str());
2726 }
2727 else
2728 {
2729 // machine is not locked: then write-lock the machine (create the session machine)
2730
2731 // must not be busy
2732 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2733
2734 // get the caller's session PID
2735 RTPROCESS pid = NIL_RTPROCESS;
2736 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2737 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2738 Assert(pid != NIL_RTPROCESS);
2739
2740 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2741
2742 if (fLaunchingVMProcess)
2743 {
2744 if (mData->mSession.mPID == NIL_RTPROCESS)
2745 {
2746 // two or more clients racing for a lock, the one which set the
2747 // session state to Spawning will win, the others will get an
2748 // error as we can't decide here if waiting a little would help
2749 // (only for shared locks this would avoid an error)
2750 return setError(VBOX_E_INVALID_OBJECT_STATE,
2751 tr("The machine '%s' already has a lock request pending"),
2752 mUserData->s.strName.c_str());
2753 }
2754
2755 // this machine is awaiting for a spawning session to be opened:
2756 // then the calling process must be the one that got started by
2757 // LaunchVMProcess()
2758
2759 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2760 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2761
2762#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2763 /* Hardened windows builds spawns three processes when a VM is
2764 launched, the 3rd one is the one that will end up here. */
2765 RTPROCESS pidParent;
2766 int vrc = RTProcQueryParent(pid, &pidParent);
2767 if (RT_SUCCESS(vrc))
2768 vrc = RTProcQueryParent(pidParent, &pidParent);
2769 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2770 || vrc == VERR_ACCESS_DENIED)
2771 {
2772 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2773 mData->mSession.mPID = pid;
2774 }
2775#endif
2776
2777 if (mData->mSession.mPID != pid)
2778 return setError(E_ACCESSDENIED,
2779 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2780 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2781 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2782 }
2783
2784 // create the mutable SessionMachine from the current machine
2785 ComObjPtr<SessionMachine> sessionMachine;
2786 sessionMachine.createObject();
2787 hrc = sessionMachine->init(this);
2788 AssertComRC(hrc);
2789
2790 /* NOTE: doing return from this function after this point but
2791 * before the end is forbidden since it may call SessionMachine::uninit()
2792 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2793 * lock while still holding the Machine lock in alock so that a deadlock
2794 * is possible due to the wrong lock order. */
2795
2796 if (SUCCEEDED(hrc))
2797 {
2798 /*
2799 * Set the session state to Spawning to protect against subsequent
2800 * attempts to open a session and to unregister the machine after
2801 * we release the lock.
2802 */
2803 SessionState_T origState = mData->mSession.mState;
2804 mData->mSession.mState = SessionState_Spawning;
2805
2806#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2807 /* Get the client token ID to be passed to the client process */
2808 Utf8Str strTokenId;
2809 sessionMachine->i_getTokenId(strTokenId);
2810 Assert(!strTokenId.isEmpty());
2811#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2812 /* Get the client token to be passed to the client process */
2813 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2814 /* The token is now "owned" by pToken, fix refcount */
2815 if (!pToken.isNull())
2816 pToken->Release();
2817#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2818
2819 /*
2820 * Release the lock before calling the client process -- it will call
2821 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2822 * because the state is Spawning, so that LaunchVMProcess() and
2823 * LockMachine() calls will fail. This method, called before we
2824 * acquire the lock again, will fail because of the wrong PID.
2825 *
2826 * Note that mData->mSession.mRemoteControls accessed outside
2827 * the lock may not be modified when state is Spawning, so it's safe.
2828 */
2829 alock.release();
2830
2831 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2832#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2833 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2834#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2835 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2836 /* Now the token is owned by the client process. */
2837 pToken.setNull();
2838#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2839 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2840
2841 /* The failure may occur w/o any error info (from RPC), so provide one */
2842 if (FAILED(hrc))
2843 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2844
2845 // get session name, either to remember or to compare against
2846 // the already known session name.
2847 {
2848 Bstr bstrSessionName;
2849 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2850 if (SUCCEEDED(hrc2))
2851 strSessionName = bstrSessionName;
2852 }
2853
2854 if ( SUCCEEDED(hrc)
2855 && fLaunchingVMProcess
2856 )
2857 {
2858 /* complete the remote session initialization */
2859
2860 /* get the console from the direct session */
2861 ComPtr<IConsole> console;
2862 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2863 ComAssertComRC(hrc);
2864
2865 if (SUCCEEDED(hrc) && !console)
2866 {
2867 ComAssert(!!console);
2868 hrc = E_FAIL;
2869 }
2870
2871 /* assign machine & console to the remote session */
2872 if (SUCCEEDED(hrc))
2873 {
2874 /*
2875 * after LaunchVMProcess(), the first and the only
2876 * entry in remoteControls is that remote session
2877 */
2878 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2879 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2880 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2881
2882 /* The failure may occur w/o any error info (from RPC), so provide one */
2883 if (FAILED(hrc))
2884 setError(VBOX_E_VM_ERROR,
2885 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2886 }
2887
2888 if (FAILED(hrc))
2889 pSessionControl->Uninitialize();
2890 }
2891
2892 /* acquire the lock again */
2893 alock.acquire();
2894
2895 /* Restore the session state */
2896 mData->mSession.mState = origState;
2897 }
2898
2899 // finalize spawning anyway (this is why we don't return on errors above)
2900 if (fLaunchingVMProcess)
2901 {
2902 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2903 /* Note that the progress object is finalized later */
2904 /** @todo Consider checking mData->mSession.mProgress for cancellation
2905 * around here. */
2906
2907 /* We don't reset mSession.mPID here because it is necessary for
2908 * SessionMachine::uninit() to reap the child process later. */
2909
2910 if (FAILED(hrc))
2911 {
2912 /* Close the remote session, remove the remote control from the list
2913 * and reset session state to Closed (@note keep the code in sync
2914 * with the relevant part in checkForSpawnFailure()). */
2915
2916 Assert(mData->mSession.mRemoteControls.size() == 1);
2917 if (mData->mSession.mRemoteControls.size() == 1)
2918 {
2919 ErrorInfoKeeper eik;
2920 mData->mSession.mRemoteControls.front()->Uninitialize();
2921 }
2922
2923 mData->mSession.mRemoteControls.clear();
2924 mData->mSession.mState = SessionState_Unlocked;
2925 }
2926 }
2927 else
2928 {
2929 /* memorize PID of the directly opened session */
2930 if (SUCCEEDED(hrc))
2931 mData->mSession.mPID = pid;
2932 }
2933
2934 if (SUCCEEDED(hrc))
2935 {
2936 mData->mSession.mLockType = aLockType;
2937 /* memorize the direct session control and cache IUnknown for it */
2938 mData->mSession.mDirectControl = pSessionControl;
2939 mData->mSession.mState = SessionState_Locked;
2940 if (!fLaunchingVMProcess)
2941 mData->mSession.mName = strSessionName;
2942 /* associate the SessionMachine with this Machine */
2943 mData->mSession.mMachine = sessionMachine;
2944
2945 /* request an IUnknown pointer early from the remote party for later
2946 * identity checks (it will be internally cached within mDirectControl
2947 * at least on XPCOM) */
2948 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2949 NOREF(unk);
2950
2951#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2952 if (aLockType == LockType_VM)
2953 {
2954 /* get the console from the direct session */
2955 ComPtr<IConsole> console;
2956 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2957 ComAssertComRC(hrc);
2958 /* send passswords to console */
2959 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2960 it != mData->mpKeyStore->end();
2961 ++it)
2962 {
2963 SecretKey *pKey = it->second;
2964 pKey->retain();
2965 console->AddEncryptionPassword(Bstr(it->first).raw(),
2966 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2967 TRUE);
2968 pKey->release();
2969 }
2970
2971 }
2972#endif
2973 }
2974
2975 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2976 * would break the lock order */
2977 alock.release();
2978
2979 /* uninitialize the created session machine on failure */
2980 if (FAILED(hrc))
2981 sessionMachine->uninit();
2982 }
2983
2984 if (SUCCEEDED(hrc))
2985 {
2986 /*
2987 * tell the client watcher thread to update the set of
2988 * machines that have open sessions
2989 */
2990 mParent->i_updateClientWatcher();
2991
2992 if (oldState != SessionState_Locked)
2993 /* fire an event */
2994 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
2995 }
2996
2997 return hrc;
2998}
2999
3000/**
3001 * @note Locks objects!
3002 */
3003HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3004 const com::Utf8Str &aName,
3005 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3006 ComPtr<IProgress> &aProgress)
3007{
3008 Utf8Str strFrontend(aName);
3009 /* "emergencystop" doesn't need the session, so skip the checks/interface
3010 * retrieval. This code doesn't quite fit in here, but introducing a
3011 * special API method would be even more effort, and would require explicit
3012 * support by every API client. It's better to hide the feature a bit. */
3013 if (strFrontend != "emergencystop")
3014 CheckComArgNotNull(aSession);
3015
3016 HRESULT hrc = S_OK;
3017 if (strFrontend.isEmpty())
3018 {
3019 Bstr bstrFrontend;
3020 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3021 if (FAILED(hrc))
3022 return hrc;
3023 strFrontend = bstrFrontend;
3024 if (strFrontend.isEmpty())
3025 {
3026 ComPtr<ISystemProperties> systemProperties;
3027 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3028 if (FAILED(hrc))
3029 return hrc;
3030 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3031 if (FAILED(hrc))
3032 return hrc;
3033 strFrontend = bstrFrontend;
3034 }
3035 /* paranoia - emergencystop is not a valid default */
3036 if (strFrontend == "emergencystop")
3037 strFrontend = Utf8Str::Empty;
3038 }
3039 /* default frontend: Qt GUI */
3040 if (strFrontend.isEmpty())
3041 strFrontend = "GUI/Qt";
3042
3043 if (strFrontend != "emergencystop")
3044 {
3045 /* check the session state */
3046 SessionState_T state;
3047 hrc = aSession->COMGETTER(State)(&state);
3048 if (FAILED(hrc))
3049 return hrc;
3050
3051 if (state != SessionState_Unlocked)
3052 return setError(VBOX_E_INVALID_OBJECT_STATE,
3053 tr("The given session is busy"));
3054
3055 /* get the IInternalSessionControl interface */
3056 ComPtr<IInternalSessionControl> control(aSession);
3057 ComAssertMsgRet(!control.isNull(),
3058 ("No IInternalSessionControl interface"),
3059 E_INVALIDARG);
3060
3061 /* get the teleporter enable state for the progress object init. */
3062 BOOL fTeleporterEnabled;
3063 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3064 if (FAILED(hrc))
3065 return hrc;
3066
3067 /* create a progress object */
3068 ComObjPtr<ProgressProxy> progress;
3069 progress.createObject();
3070 hrc = progress->init(mParent,
3071 static_cast<IMachine*>(this),
3072 Bstr(tr("Starting VM")).raw(),
3073 TRUE /* aCancelable */,
3074 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3075 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3076 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3077 2 /* uFirstOperationWeight */,
3078 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3079 if (SUCCEEDED(hrc))
3080 {
3081 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3082 if (SUCCEEDED(hrc))
3083 {
3084 aProgress = progress;
3085
3086 /* signal the client watcher thread */
3087 mParent->i_updateClientWatcher();
3088
3089 /* fire an event */
3090 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3091 }
3092 }
3093 }
3094 else
3095 {
3096 /* no progress object - either instant success or failure */
3097 aProgress = NULL;
3098
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 if (mData->mSession.mState != SessionState_Locked)
3102 return setError(VBOX_E_INVALID_OBJECT_STATE,
3103 tr("The machine '%s' is not locked by a session"),
3104 mUserData->s.strName.c_str());
3105
3106 /* must have a VM process associated - do not kill normal API clients
3107 * with an open session */
3108 if (!Global::IsOnline(mData->mMachineState))
3109 return setError(VBOX_E_INVALID_OBJECT_STATE,
3110 tr("The machine '%s' does not have a VM process"),
3111 mUserData->s.strName.c_str());
3112
3113 /* forcibly terminate the VM process */
3114 if (mData->mSession.mPID != NIL_RTPROCESS)
3115 RTProcTerminate(mData->mSession.mPID);
3116
3117 /* signal the client watcher thread, as most likely the client has
3118 * been terminated */
3119 mParent->i_updateClientWatcher();
3120 }
3121
3122 return hrc;
3123}
3124
3125HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3126{
3127 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3128 return setError(E_INVALIDARG,
3129 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3130 aPosition, SchemaDefs::MaxBootPosition);
3131
3132 if (aDevice == DeviceType_USB)
3133 return setError(E_NOTIMPL,
3134 tr("Booting from USB device is currently not supported"));
3135
3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3139 if (FAILED(hrc)) return hrc;
3140
3141 i_setModified(IsModified_MachineData);
3142 mHWData.backup();
3143 mHWData->mBootOrder[aPosition - 1] = aDevice;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3149{
3150 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3151 return setError(E_INVALIDARG,
3152 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3153 aPosition, SchemaDefs::MaxBootPosition);
3154
3155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3156
3157 *aDevice = mHWData->mBootOrder[aPosition - 1];
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3163 LONG aControllerPort,
3164 LONG aDevice,
3165 DeviceType_T aType,
3166 const ComPtr<IMedium> &aMedium)
3167{
3168 IMedium *aM = aMedium;
3169 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3170 aName.c_str(), aControllerPort, aDevice, aType, aM));
3171
3172 // request the host lock first, since might be calling Host methods for getting host drives;
3173 // next, protect the media tree all the while we're in here, as well as our member variables
3174 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3175 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3176
3177 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3178 if (FAILED(hrc)) return hrc;
3179
3180 /// @todo NEWMEDIA implicit machine registration
3181 if (!mData->mRegistered)
3182 return setError(VBOX_E_INVALID_OBJECT_STATE,
3183 tr("Cannot attach storage devices to an unregistered machine"));
3184
3185 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3186
3187 /* Check for an existing controller. */
3188 ComObjPtr<StorageController> ctl;
3189 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3190 if (FAILED(hrc)) return hrc;
3191
3192 StorageControllerType_T ctrlType;
3193 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3194 if (FAILED(hrc))
3195 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3196
3197 bool fSilent = false;
3198
3199 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3200 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3201 if ( mData->mMachineState == MachineState_Paused
3202 && strReconfig == "1")
3203 fSilent = true;
3204
3205 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3206 bool fHotplug = false;
3207 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3208 fHotplug = true;
3209
3210 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3211 return setError(VBOX_E_INVALID_VM_STATE,
3212 tr("Controller '%s' does not support hot-plugging"),
3213 aName.c_str());
3214
3215 // check that the port and device are not out of range
3216 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3217 if (FAILED(hrc)) return hrc;
3218
3219 /* check if the device slot is already busy */
3220 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3221 aName,
3222 aControllerPort,
3223 aDevice);
3224 if (pAttachTemp)
3225 {
3226 Medium *pMedium = pAttachTemp->i_getMedium();
3227 if (pMedium)
3228 {
3229 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3230 return setError(VBOX_E_OBJECT_IN_USE,
3231 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3232 pMedium->i_getLocationFull().c_str(),
3233 aControllerPort,
3234 aDevice,
3235 aName.c_str());
3236 }
3237 else
3238 return setError(VBOX_E_OBJECT_IN_USE,
3239 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3240 aControllerPort, aDevice, aName.c_str());
3241 }
3242
3243 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3244 if (aMedium && medium.isNull())
3245 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3246
3247 AutoCaller mediumCaller(medium);
3248 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3249
3250 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3251
3252 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3253 if ( pAttachTemp
3254 && !medium.isNull()
3255 && ( medium->i_getType() != MediumType_Readonly
3256 || medium->i_getDeviceType() != DeviceType_DVD)
3257 )
3258 return setError(VBOX_E_OBJECT_IN_USE,
3259 tr("Medium '%s' is already attached to this virtual machine"),
3260 medium->i_getLocationFull().c_str());
3261
3262 if (!medium.isNull())
3263 {
3264 MediumType_T mtype = medium->i_getType();
3265 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3266 // For DVDs it's not written to the config file, so needs no global config
3267 // version bump. For floppies it's a new attribute "type", which is ignored
3268 // by older VirtualBox version, so needs no global config version bump either.
3269 // For hard disks this type is not accepted.
3270 if (mtype == MediumType_MultiAttach)
3271 {
3272 // This type is new with VirtualBox 4.0 and therefore requires settings
3273 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3274 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3275 // two reasons: The medium type is a property of the media registry tree, which
3276 // can reside in the global config file (for pre-4.0 media); we would therefore
3277 // possibly need to bump the global config version. We don't want to do that though
3278 // because that might make downgrading to pre-4.0 impossible.
3279 // As a result, we can only use these two new types if the medium is NOT in the
3280 // global registry:
3281 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3282 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3283 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3284 )
3285 return setError(VBOX_E_INVALID_OBJECT_STATE,
3286 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3287 "to machines that were created with VirtualBox 4.0 or later"),
3288 medium->i_getLocationFull().c_str());
3289 }
3290 }
3291
3292 bool fIndirect = false;
3293 if (!medium.isNull())
3294 fIndirect = medium->i_isReadOnly();
3295 bool associate = true;
3296
3297 do
3298 {
3299 if ( aType == DeviceType_HardDisk
3300 && mMediumAttachments.isBackedUp())
3301 {
3302 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3303
3304 /* check if the medium was attached to the VM before we started
3305 * changing attachments in which case the attachment just needs to
3306 * be restored */
3307 pAttachTemp = i_findAttachment(oldAtts, medium);
3308 if (pAttachTemp)
3309 {
3310 AssertReturn(!fIndirect, E_FAIL);
3311
3312 /* see if it's the same bus/channel/device */
3313 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3314 {
3315 /* the simplest case: restore the whole attachment
3316 * and return, nothing else to do */
3317 mMediumAttachments->push_back(pAttachTemp);
3318
3319 /* Reattach the medium to the VM. */
3320 if (fHotplug || fSilent)
3321 {
3322 mediumLock.release();
3323 treeLock.release();
3324 alock.release();
3325
3326 MediumLockList *pMediumLockList(new MediumLockList());
3327
3328 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3329 medium /* pToLockWrite */,
3330 false /* fMediumLockWriteAll */,
3331 NULL,
3332 *pMediumLockList);
3333 alock.acquire();
3334 if (FAILED(hrc))
3335 delete pMediumLockList;
3336 else
3337 {
3338 Assert(mData->mSession.mLockedMedia.IsLocked());
3339 mData->mSession.mLockedMedia.Unlock();
3340 alock.release();
3341 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3342 mData->mSession.mLockedMedia.Lock();
3343 alock.acquire();
3344 }
3345 alock.release();
3346
3347 if (SUCCEEDED(hrc))
3348 {
3349 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3350 /* Remove lock list in case of error. */
3351 if (FAILED(hrc))
3352 {
3353 mData->mSession.mLockedMedia.Unlock();
3354 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3355 mData->mSession.mLockedMedia.Lock();
3356 }
3357 }
3358 }
3359
3360 return S_OK;
3361 }
3362
3363 /* bus/channel/device differ; we need a new attachment object,
3364 * but don't try to associate it again */
3365 associate = false;
3366 break;
3367 }
3368 }
3369
3370 /* go further only if the attachment is to be indirect */
3371 if (!fIndirect)
3372 break;
3373
3374 /* perform the so called smart attachment logic for indirect
3375 * attachments. Note that smart attachment is only applicable to base
3376 * hard disks. */
3377
3378 if (medium->i_getParent().isNull())
3379 {
3380 /* first, investigate the backup copy of the current hard disk
3381 * attachments to make it possible to re-attach existing diffs to
3382 * another device slot w/o losing their contents */
3383 if (mMediumAttachments.isBackedUp())
3384 {
3385 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3386
3387 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3388 uint32_t foundLevel = 0;
3389
3390 for (MediumAttachmentList::const_iterator
3391 it = oldAtts.begin();
3392 it != oldAtts.end();
3393 ++it)
3394 {
3395 uint32_t level = 0;
3396 MediumAttachment *pAttach = *it;
3397 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3398 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3399 if (pMedium.isNull())
3400 continue;
3401
3402 if (pMedium->i_getBase(&level) == medium)
3403 {
3404 /* skip the hard disk if its currently attached (we
3405 * cannot attach the same hard disk twice) */
3406 if (i_findAttachment(*mMediumAttachments.data(),
3407 pMedium))
3408 continue;
3409
3410 /* matched device, channel and bus (i.e. attached to the
3411 * same place) will win and immediately stop the search;
3412 * otherwise the attachment that has the youngest
3413 * descendant of medium will be used
3414 */
3415 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3416 {
3417 /* the simplest case: restore the whole attachment
3418 * and return, nothing else to do */
3419 mMediumAttachments->push_back(*it);
3420
3421 /* Reattach the medium to the VM. */
3422 if (fHotplug || fSilent)
3423 {
3424 mediumLock.release();
3425 treeLock.release();
3426 alock.release();
3427
3428 MediumLockList *pMediumLockList(new MediumLockList());
3429
3430 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3431 medium /* pToLockWrite */,
3432 false /* fMediumLockWriteAll */,
3433 NULL,
3434 *pMediumLockList);
3435 alock.acquire();
3436 if (FAILED(hrc))
3437 delete pMediumLockList;
3438 else
3439 {
3440 Assert(mData->mSession.mLockedMedia.IsLocked());
3441 mData->mSession.mLockedMedia.Unlock();
3442 alock.release();
3443 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3444 mData->mSession.mLockedMedia.Lock();
3445 alock.acquire();
3446 }
3447 alock.release();
3448
3449 if (SUCCEEDED(hrc))
3450 {
3451 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3452 /* Remove lock list in case of error. */
3453 if (FAILED(hrc))
3454 {
3455 mData->mSession.mLockedMedia.Unlock();
3456 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3457 mData->mSession.mLockedMedia.Lock();
3458 }
3459 }
3460 }
3461
3462 return S_OK;
3463 }
3464 else if ( foundIt == oldAtts.end()
3465 || level > foundLevel /* prefer younger */
3466 )
3467 {
3468 foundIt = it;
3469 foundLevel = level;
3470 }
3471 }
3472 }
3473
3474 if (foundIt != oldAtts.end())
3475 {
3476 /* use the previously attached hard disk */
3477 medium = (*foundIt)->i_getMedium();
3478 mediumCaller.attach(medium);
3479 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3480 mediumLock.attach(medium);
3481 /* not implicit, doesn't require association with this VM */
3482 fIndirect = false;
3483 associate = false;
3484 /* go right to the MediumAttachment creation */
3485 break;
3486 }
3487 }
3488
3489 /* must give up the medium lock and medium tree lock as below we
3490 * go over snapshots, which needs a lock with higher lock order. */
3491 mediumLock.release();
3492 treeLock.release();
3493
3494 /* then, search through snapshots for the best diff in the given
3495 * hard disk's chain to base the new diff on */
3496
3497 ComObjPtr<Medium> base;
3498 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3499 while (snap)
3500 {
3501 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3502
3503 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3504
3505 MediumAttachment *pAttachFound = NULL;
3506 uint32_t foundLevel = 0;
3507
3508 for (MediumAttachmentList::const_iterator
3509 it = snapAtts.begin();
3510 it != snapAtts.end();
3511 ++it)
3512 {
3513 MediumAttachment *pAttach = *it;
3514 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3515 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3516 if (pMedium.isNull())
3517 continue;
3518
3519 uint32_t level = 0;
3520 if (pMedium->i_getBase(&level) == medium)
3521 {
3522 /* matched device, channel and bus (i.e. attached to the
3523 * same place) will win and immediately stop the search;
3524 * otherwise the attachment that has the youngest
3525 * descendant of medium will be used
3526 */
3527 if ( pAttach->i_getDevice() == aDevice
3528 && pAttach->i_getPort() == aControllerPort
3529 && pAttach->i_getControllerName() == aName
3530 )
3531 {
3532 pAttachFound = pAttach;
3533 break;
3534 }
3535 else if ( !pAttachFound
3536 || level > foundLevel /* prefer younger */
3537 )
3538 {
3539 pAttachFound = pAttach;
3540 foundLevel = level;
3541 }
3542 }
3543 }
3544
3545 if (pAttachFound)
3546 {
3547 base = pAttachFound->i_getMedium();
3548 break;
3549 }
3550
3551 snap = snap->i_getParent();
3552 }
3553
3554 /* re-lock medium tree and the medium, as we need it below */
3555 treeLock.acquire();
3556 mediumLock.acquire();
3557
3558 /* found a suitable diff, use it as a base */
3559 if (!base.isNull())
3560 {
3561 medium = base;
3562 mediumCaller.attach(medium);
3563 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3564 mediumLock.attach(medium);
3565 }
3566 }
3567
3568 Utf8Str strFullSnapshotFolder;
3569 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3570
3571 ComObjPtr<Medium> diff;
3572 diff.createObject();
3573 // store this diff in the same registry as the parent
3574 Guid uuidRegistryParent;
3575 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3576 {
3577 // parent image has no registry: this can happen if we're attaching a new immutable
3578 // image that has not yet been attached (medium then points to the base and we're
3579 // creating the diff image for the immutable, and the parent is not yet registered);
3580 // put the parent in the machine registry then
3581 mediumLock.release();
3582 treeLock.release();
3583 alock.release();
3584 i_addMediumToRegistry(medium);
3585 alock.acquire();
3586 treeLock.acquire();
3587 mediumLock.acquire();
3588 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3589 }
3590 hrc = diff->init(mParent,
3591 medium->i_getPreferredDiffFormat(),
3592 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3593 uuidRegistryParent,
3594 DeviceType_HardDisk);
3595 if (FAILED(hrc)) return hrc;
3596
3597 /* Apply the normal locking logic to the entire chain. */
3598 MediumLockList *pMediumLockList(new MediumLockList());
3599 mediumLock.release();
3600 treeLock.release();
3601 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3602 diff /* pToLockWrite */,
3603 false /* fMediumLockWriteAll */,
3604 medium,
3605 *pMediumLockList);
3606 treeLock.acquire();
3607 mediumLock.acquire();
3608 if (SUCCEEDED(hrc))
3609 {
3610 mediumLock.release();
3611 treeLock.release();
3612 hrc = pMediumLockList->Lock();
3613 treeLock.acquire();
3614 mediumLock.acquire();
3615 if (FAILED(hrc))
3616 setError(hrc,
3617 tr("Could not lock medium when creating diff '%s'"),
3618 diff->i_getLocationFull().c_str());
3619 else
3620 {
3621 /* will release the lock before the potentially lengthy
3622 * operation, so protect with the special state */
3623 MachineState_T oldState = mData->mMachineState;
3624 i_setMachineState(MachineState_SettingUp);
3625
3626 mediumLock.release();
3627 treeLock.release();
3628 alock.release();
3629
3630 hrc = medium->i_createDiffStorage(diff,
3631 medium->i_getPreferredDiffVariant(),
3632 pMediumLockList,
3633 NULL /* aProgress */,
3634 true /* aWait */,
3635 false /* aNotify */);
3636
3637 alock.acquire();
3638 treeLock.acquire();
3639 mediumLock.acquire();
3640
3641 i_setMachineState(oldState);
3642 }
3643 }
3644
3645 /* Unlock the media and free the associated memory. */
3646 delete pMediumLockList;
3647
3648 if (FAILED(hrc)) return hrc;
3649
3650 /* use the created diff for the actual attachment */
3651 medium = diff;
3652 mediumCaller.attach(medium);
3653 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3654 mediumLock.attach(medium);
3655 }
3656 while (0);
3657
3658 ComObjPtr<MediumAttachment> attachment;
3659 attachment.createObject();
3660 hrc = attachment->init(this,
3661 medium,
3662 aName,
3663 aControllerPort,
3664 aDevice,
3665 aType,
3666 fIndirect,
3667 false /* fPassthrough */,
3668 false /* fTempEject */,
3669 false /* fNonRotational */,
3670 false /* fDiscard */,
3671 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3672 Utf8Str::Empty);
3673 if (FAILED(hrc)) return hrc;
3674
3675 if (associate && !medium.isNull())
3676 {
3677 // as the last step, associate the medium to the VM
3678 hrc = medium->i_addBackReference(mData->mUuid);
3679 // here we can fail because of Deleting, or being in process of creating a Diff
3680 if (FAILED(hrc)) return hrc;
3681
3682 mediumLock.release();
3683 treeLock.release();
3684 alock.release();
3685 i_addMediumToRegistry(medium);
3686 alock.acquire();
3687 treeLock.acquire();
3688 mediumLock.acquire();
3689 }
3690
3691 /* success: finally remember the attachment */
3692 i_setModified(IsModified_Storage);
3693 mMediumAttachments.backup();
3694 mMediumAttachments->push_back(attachment);
3695
3696 mediumLock.release();
3697 treeLock.release();
3698 alock.release();
3699
3700 if (fHotplug || fSilent)
3701 {
3702 if (!medium.isNull())
3703 {
3704 MediumLockList *pMediumLockList(new MediumLockList());
3705
3706 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3707 medium /* pToLockWrite */,
3708 false /* fMediumLockWriteAll */,
3709 NULL,
3710 *pMediumLockList);
3711 alock.acquire();
3712 if (FAILED(hrc))
3713 delete pMediumLockList;
3714 else
3715 {
3716 Assert(mData->mSession.mLockedMedia.IsLocked());
3717 mData->mSession.mLockedMedia.Unlock();
3718 alock.release();
3719 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3720 mData->mSession.mLockedMedia.Lock();
3721 alock.acquire();
3722 }
3723 alock.release();
3724 }
3725
3726 if (SUCCEEDED(hrc))
3727 {
3728 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3729 /* Remove lock list in case of error. */
3730 if (FAILED(hrc))
3731 {
3732 mData->mSession.mLockedMedia.Unlock();
3733 mData->mSession.mLockedMedia.Remove(attachment);
3734 mData->mSession.mLockedMedia.Lock();
3735 }
3736 }
3737 }
3738
3739 /* Save modified registries, but skip this machine as it's the caller's
3740 * job to save its settings like all other settings changes. */
3741 mParent->i_unmarkRegistryModified(i_getId());
3742 mParent->i_saveModifiedRegistries();
3743
3744 if (SUCCEEDED(hrc))
3745 {
3746 if (fIndirect && medium != aM)
3747 mParent->i_onMediumConfigChanged(medium);
3748 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3749 }
3750
3751 return hrc;
3752}
3753
3754HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3755 LONG aDevice)
3756{
3757 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3758
3759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3760
3761 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3762 if (FAILED(hrc)) return hrc;
3763
3764 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3765
3766 /* Check for an existing controller. */
3767 ComObjPtr<StorageController> ctl;
3768 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3769 if (FAILED(hrc)) return hrc;
3770
3771 StorageControllerType_T ctrlType;
3772 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3773 if (FAILED(hrc))
3774 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3775
3776 bool fSilent = false;
3777 Utf8Str strReconfig;
3778
3779 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3780 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3781 if ( mData->mMachineState == MachineState_Paused
3782 && strReconfig == "1")
3783 fSilent = true;
3784
3785 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3786 bool fHotplug = false;
3787 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3788 fHotplug = true;
3789
3790 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3791 return setError(VBOX_E_INVALID_VM_STATE,
3792 tr("Controller '%s' does not support hot-plugging"),
3793 aName.c_str());
3794
3795 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3796 aName,
3797 aControllerPort,
3798 aDevice);
3799 if (!pAttach)
3800 return setError(VBOX_E_OBJECT_NOT_FOUND,
3801 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3802 aDevice, aControllerPort, aName.c_str());
3803
3804 if (fHotplug && !pAttach->i_getHotPluggable())
3805 return setError(VBOX_E_NOT_SUPPORTED,
3806 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3807 aDevice, aControllerPort, aName.c_str());
3808
3809 /*
3810 * The VM has to detach the device before we delete any implicit diffs.
3811 * If this fails we can roll back without loosing data.
3812 */
3813 if (fHotplug || fSilent)
3814 {
3815 alock.release();
3816 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3817 alock.acquire();
3818 }
3819 if (FAILED(hrc)) return hrc;
3820
3821 /* If we are here everything went well and we can delete the implicit now. */
3822 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3823
3824 alock.release();
3825
3826 /* Save modified registries, but skip this machine as it's the caller's
3827 * job to save its settings like all other settings changes. */
3828 mParent->i_unmarkRegistryModified(i_getId());
3829 mParent->i_saveModifiedRegistries();
3830
3831 if (SUCCEEDED(hrc))
3832 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3833
3834 return hrc;
3835}
3836
3837HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3838 LONG aDevice, BOOL aPassthrough)
3839{
3840 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3841 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3842
3843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3844
3845 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3846 if (FAILED(hrc)) return hrc;
3847
3848 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3849
3850 /* Check for an existing controller. */
3851 ComObjPtr<StorageController> ctl;
3852 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3853 if (FAILED(hrc)) return hrc;
3854
3855 StorageControllerType_T ctrlType;
3856 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3857 if (FAILED(hrc))
3858 return setError(E_FAIL,
3859 tr("Could not get type of controller '%s'"),
3860 aName.c_str());
3861
3862 bool fSilent = false;
3863 Utf8Str strReconfig;
3864
3865 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3866 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3867 if ( mData->mMachineState == MachineState_Paused
3868 && strReconfig == "1")
3869 fSilent = true;
3870
3871 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3872 bool fHotplug = false;
3873 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3874 fHotplug = true;
3875
3876 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3877 return setError(VBOX_E_INVALID_VM_STATE,
3878 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3879 aName.c_str());
3880
3881 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3882 aName,
3883 aControllerPort,
3884 aDevice);
3885 if (!pAttach)
3886 return setError(VBOX_E_OBJECT_NOT_FOUND,
3887 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3888 aDevice, aControllerPort, aName.c_str());
3889
3890
3891 i_setModified(IsModified_Storage);
3892 mMediumAttachments.backup();
3893
3894 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3895
3896 if (pAttach->i_getType() != DeviceType_DVD)
3897 return setError(E_INVALIDARG,
3898 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3899 aDevice, aControllerPort, aName.c_str());
3900
3901 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3902
3903 pAttach->i_updatePassthrough(!!aPassthrough);
3904
3905 attLock.release();
3906 alock.release();
3907 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3908 if (SUCCEEDED(hrc) && fValueChanged)
3909 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3910
3911 return hrc;
3912}
3913
3914HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3915 LONG aDevice, BOOL aTemporaryEject)
3916{
3917
3918 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3919 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3920
3921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3922
3923 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3924 if (FAILED(hrc)) return hrc;
3925
3926 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3927 aName,
3928 aControllerPort,
3929 aDevice);
3930 if (!pAttach)
3931 return setError(VBOX_E_OBJECT_NOT_FOUND,
3932 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3933 aDevice, aControllerPort, aName.c_str());
3934
3935
3936 i_setModified(IsModified_Storage);
3937 mMediumAttachments.backup();
3938
3939 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3940
3941 if (pAttach->i_getType() != DeviceType_DVD)
3942 return setError(E_INVALIDARG,
3943 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3944 aDevice, aControllerPort, aName.c_str());
3945 pAttach->i_updateTempEject(!!aTemporaryEject);
3946
3947 return S_OK;
3948}
3949
3950HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3951 LONG aDevice, BOOL aNonRotational)
3952{
3953
3954 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3955 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3956
3957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3958
3959 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3960 if (FAILED(hrc)) return hrc;
3961
3962 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3963
3964 if (Global::IsOnlineOrTransient(mData->mMachineState))
3965 return setError(VBOX_E_INVALID_VM_STATE,
3966 tr("Invalid machine state: %s"),
3967 Global::stringifyMachineState(mData->mMachineState));
3968
3969 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3970 aName,
3971 aControllerPort,
3972 aDevice);
3973 if (!pAttach)
3974 return setError(VBOX_E_OBJECT_NOT_FOUND,
3975 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3976 aDevice, aControllerPort, aName.c_str());
3977
3978
3979 i_setModified(IsModified_Storage);
3980 mMediumAttachments.backup();
3981
3982 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3983
3984 if (pAttach->i_getType() != DeviceType_HardDisk)
3985 return setError(E_INVALIDARG,
3986 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
3987 aDevice, aControllerPort, aName.c_str());
3988 pAttach->i_updateNonRotational(!!aNonRotational);
3989
3990 return S_OK;
3991}
3992
3993HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
3994 LONG aDevice, BOOL aDiscard)
3995{
3996
3997 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
3998 aName.c_str(), aControllerPort, aDevice, aDiscard));
3999
4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4001
4002 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4003 if (FAILED(hrc)) return hrc;
4004
4005 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4006
4007 if (Global::IsOnlineOrTransient(mData->mMachineState))
4008 return setError(VBOX_E_INVALID_VM_STATE,
4009 tr("Invalid machine state: %s"),
4010 Global::stringifyMachineState(mData->mMachineState));
4011
4012 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4013 aName,
4014 aControllerPort,
4015 aDevice);
4016 if (!pAttach)
4017 return setError(VBOX_E_OBJECT_NOT_FOUND,
4018 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4019 aDevice, aControllerPort, aName.c_str());
4020
4021
4022 i_setModified(IsModified_Storage);
4023 mMediumAttachments.backup();
4024
4025 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4026
4027 if (pAttach->i_getType() != DeviceType_HardDisk)
4028 return setError(E_INVALIDARG,
4029 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4030 aDevice, aControllerPort, aName.c_str());
4031 pAttach->i_updateDiscard(!!aDiscard);
4032
4033 return S_OK;
4034}
4035
4036HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4037 LONG aDevice, BOOL aHotPluggable)
4038{
4039 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4040 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4041
4042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4043
4044 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4045 if (FAILED(hrc)) return hrc;
4046
4047 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4048
4049 if (Global::IsOnlineOrTransient(mData->mMachineState))
4050 return setError(VBOX_E_INVALID_VM_STATE,
4051 tr("Invalid machine state: %s"),
4052 Global::stringifyMachineState(mData->mMachineState));
4053
4054 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4055 aName,
4056 aControllerPort,
4057 aDevice);
4058 if (!pAttach)
4059 return setError(VBOX_E_OBJECT_NOT_FOUND,
4060 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4061 aDevice, aControllerPort, aName.c_str());
4062
4063 /* Check for an existing controller. */
4064 ComObjPtr<StorageController> ctl;
4065 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4066 if (FAILED(hrc)) return hrc;
4067
4068 StorageControllerType_T ctrlType;
4069 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4070 if (FAILED(hrc))
4071 return setError(E_FAIL,
4072 tr("Could not get type of controller '%s'"),
4073 aName.c_str());
4074
4075 if (!i_isControllerHotplugCapable(ctrlType))
4076 return setError(VBOX_E_NOT_SUPPORTED,
4077 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4078 aName.c_str());
4079
4080 /* silently ignore attempts to modify the hot-plug status of USB devices */
4081 if (ctrlType == StorageControllerType_USB)
4082 return S_OK;
4083
4084 i_setModified(IsModified_Storage);
4085 mMediumAttachments.backup();
4086
4087 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4088
4089 if (pAttach->i_getType() == DeviceType_Floppy)
4090 return setError(E_INVALIDARG,
4091 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4092 aDevice, aControllerPort, aName.c_str());
4093 pAttach->i_updateHotPluggable(!!aHotPluggable);
4094
4095 return S_OK;
4096}
4097
4098HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4099 LONG aDevice)
4100{
4101 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4102 aName.c_str(), aControllerPort, aDevice));
4103
4104 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4105}
4106
4107HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4108 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4109{
4110 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4111 aName.c_str(), aControllerPort, aDevice));
4112
4113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4114
4115 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4116 if (FAILED(hrc)) return hrc;
4117
4118 if (Global::IsOnlineOrTransient(mData->mMachineState))
4119 return setError(VBOX_E_INVALID_VM_STATE,
4120 tr("Invalid machine state: %s"),
4121 Global::stringifyMachineState(mData->mMachineState));
4122
4123 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4124 aName,
4125 aControllerPort,
4126 aDevice);
4127 if (!pAttach)
4128 return setError(VBOX_E_OBJECT_NOT_FOUND,
4129 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4130 aDevice, aControllerPort, aName.c_str());
4131
4132
4133 i_setModified(IsModified_Storage);
4134 mMediumAttachments.backup();
4135
4136 IBandwidthGroup *iB = aBandwidthGroup;
4137 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4138 if (aBandwidthGroup && group.isNull())
4139 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4140
4141 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4142
4143 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4144 if (strBandwidthGroupOld.isNotEmpty())
4145 {
4146 /* Get the bandwidth group object and release it - this must not fail. */
4147 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4148 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4149 Assert(SUCCEEDED(hrc));
4150
4151 pBandwidthGroupOld->i_release();
4152 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4153 }
4154
4155 if (!group.isNull())
4156 {
4157 group->i_reference();
4158 pAttach->i_updateBandwidthGroup(group->i_getName());
4159 }
4160
4161 return S_OK;
4162}
4163
4164HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4165 LONG aControllerPort,
4166 LONG aDevice,
4167 DeviceType_T aType)
4168{
4169 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4170 aName.c_str(), aControllerPort, aDevice, aType));
4171
4172 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4173}
4174
4175
4176HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4177 LONG aControllerPort,
4178 LONG aDevice,
4179 BOOL aForce)
4180{
4181 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4182 aName.c_str(), aControllerPort, aForce));
4183
4184 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4185}
4186
4187HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4188 LONG aControllerPort,
4189 LONG aDevice,
4190 const ComPtr<IMedium> &aMedium,
4191 BOOL aForce)
4192{
4193 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4194 aName.c_str(), aControllerPort, aDevice, aForce));
4195
4196 // request the host lock first, since might be calling Host methods for getting host drives;
4197 // next, protect the media tree all the while we're in here, as well as our member variables
4198 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4199 this->lockHandle(),
4200 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4201
4202 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4203 if (FAILED(hrc)) return hrc;
4204
4205 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4206 aName,
4207 aControllerPort,
4208 aDevice);
4209 if (pAttach.isNull())
4210 return setError(VBOX_E_OBJECT_NOT_FOUND,
4211 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4212 aDevice, aControllerPort, aName.c_str());
4213
4214 /* Remember previously mounted medium. The medium before taking the
4215 * backup is not necessarily the same thing. */
4216 ComObjPtr<Medium> oldmedium;
4217 oldmedium = pAttach->i_getMedium();
4218
4219 IMedium *iM = aMedium;
4220 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4221 if (aMedium && pMedium.isNull())
4222 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4223
4224 /* Check if potential medium is already mounted */
4225 if (pMedium == oldmedium)
4226 return S_OK;
4227
4228 AutoCaller mediumCaller(pMedium);
4229 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4230
4231 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4232 if (pMedium)
4233 {
4234 DeviceType_T mediumType = pAttach->i_getType();
4235 switch (mediumType)
4236 {
4237 case DeviceType_DVD:
4238 case DeviceType_Floppy:
4239 break;
4240
4241 default:
4242 return setError(VBOX_E_INVALID_OBJECT_STATE,
4243 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4244 aControllerPort,
4245 aDevice,
4246 aName.c_str());
4247 }
4248 }
4249
4250 i_setModified(IsModified_Storage);
4251 mMediumAttachments.backup();
4252
4253 {
4254 // The backup operation makes the pAttach reference point to the
4255 // old settings. Re-get the correct reference.
4256 pAttach = i_findAttachment(*mMediumAttachments.data(),
4257 aName,
4258 aControllerPort,
4259 aDevice);
4260 if (!oldmedium.isNull())
4261 oldmedium->i_removeBackReference(mData->mUuid);
4262 if (!pMedium.isNull())
4263 {
4264 pMedium->i_addBackReference(mData->mUuid);
4265
4266 mediumLock.release();
4267 multiLock.release();
4268 i_addMediumToRegistry(pMedium);
4269 multiLock.acquire();
4270 mediumLock.acquire();
4271 }
4272
4273 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4274 pAttach->i_updateMedium(pMedium);
4275 }
4276
4277 i_setModified(IsModified_Storage);
4278
4279 mediumLock.release();
4280 multiLock.release();
4281 hrc = i_onMediumChange(pAttach, aForce);
4282 multiLock.acquire();
4283 mediumLock.acquire();
4284
4285 /* On error roll back this change only. */
4286 if (FAILED(hrc))
4287 {
4288 if (!pMedium.isNull())
4289 pMedium->i_removeBackReference(mData->mUuid);
4290 pAttach = i_findAttachment(*mMediumAttachments.data(),
4291 aName,
4292 aControllerPort,
4293 aDevice);
4294 /* If the attachment is gone in the meantime, bail out. */
4295 if (pAttach.isNull())
4296 return hrc;
4297 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4298 if (!oldmedium.isNull())
4299 oldmedium->i_addBackReference(mData->mUuid);
4300 pAttach->i_updateMedium(oldmedium);
4301 }
4302
4303 mediumLock.release();
4304 multiLock.release();
4305
4306 /* Save modified registries, but skip this machine as it's the caller's
4307 * job to save its settings like all other settings changes. */
4308 mParent->i_unmarkRegistryModified(i_getId());
4309 mParent->i_saveModifiedRegistries();
4310
4311 return hrc;
4312}
4313HRESULT Machine::getMedium(const com::Utf8Str &aName,
4314 LONG aControllerPort,
4315 LONG aDevice,
4316 ComPtr<IMedium> &aMedium)
4317{
4318 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4319 aName.c_str(), aControllerPort, aDevice));
4320
4321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4322
4323 aMedium = NULL;
4324
4325 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4326 aName,
4327 aControllerPort,
4328 aDevice);
4329 if (pAttach.isNull())
4330 return setError(VBOX_E_OBJECT_NOT_FOUND,
4331 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4332 aDevice, aControllerPort, aName.c_str());
4333
4334 aMedium = pAttach->i_getMedium();
4335
4336 return S_OK;
4337}
4338
4339HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4340{
4341 if (aSlot < RT_ELEMENTS(mSerialPorts))
4342 {
4343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4344 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4345 return S_OK;
4346 }
4347 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4348}
4349
4350HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4351{
4352 if (aSlot < RT_ELEMENTS(mParallelPorts))
4353 {
4354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4355 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4356 return S_OK;
4357 }
4358 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4359}
4360
4361
4362HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4363{
4364 /* Do not assert if slot is out of range, just return the advertised
4365 status. testdriver/vbox.py triggers this in logVmInfo. */
4366 if (aSlot >= mNetworkAdapters.size())
4367 return setError(E_INVALIDARG,
4368 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4369 aSlot, mNetworkAdapters.size());
4370
4371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4374
4375 return S_OK;
4376}
4377
4378HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4379{
4380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4381
4382 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4383 size_t i = 0;
4384 for (settings::StringsMap::const_iterator
4385 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4386 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4387 ++it, ++i)
4388 aKeys[i] = it->first;
4389
4390 return S_OK;
4391}
4392
4393 /**
4394 * @note Locks this object for reading.
4395 */
4396HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4397 com::Utf8Str &aValue)
4398{
4399 /* start with nothing found */
4400 aValue = "";
4401
4402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4403
4404 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4405 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4406 // found:
4407 aValue = it->second; // source is a Utf8Str
4408
4409 /* return the result to caller (may be empty) */
4410 return S_OK;
4411}
4412
4413 /**
4414 * @note Locks mParent for writing + this object for writing.
4415 */
4416HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4417{
4418 /* Because control characters in aKey have caused problems in the settings
4419 * they are rejected unless the key should be deleted. */
4420 if (!aValue.isEmpty())
4421 {
4422 for (size_t i = 0; i < aKey.length(); ++i)
4423 {
4424 char ch = aKey[i];
4425 if (RTLocCIsCntrl(ch))
4426 return E_INVALIDARG;
4427 }
4428 }
4429
4430 Utf8Str strOldValue; // empty
4431
4432 // locking note: we only hold the read lock briefly to look up the old value,
4433 // then release it and call the onExtraCanChange callbacks. There is a small
4434 // chance of a race insofar as the callback might be called twice if two callers
4435 // change the same key at the same time, but that's a much better solution
4436 // than the deadlock we had here before. The actual changing of the extradata
4437 // is then performed under the write lock and race-free.
4438
4439 // look up the old value first; if nothing has changed then we need not do anything
4440 {
4441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4442
4443 // For snapshots don't even think about allowing changes, extradata
4444 // is global for a machine, so there is nothing snapshot specific.
4445 if (i_isSnapshotMachine())
4446 return setError(VBOX_E_INVALID_VM_STATE,
4447 tr("Cannot set extradata for a snapshot"));
4448
4449 // check if the right IMachine instance is used
4450 if (mData->mRegistered && !i_isSessionMachine())
4451 return setError(VBOX_E_INVALID_VM_STATE,
4452 tr("Cannot set extradata for an immutable machine"));
4453
4454 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4455 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4456 strOldValue = it->second;
4457 }
4458
4459 bool fChanged;
4460 if ((fChanged = (strOldValue != aValue)))
4461 {
4462 // ask for permission from all listeners outside the locks;
4463 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4464 // lock to copy the list of callbacks to invoke
4465 Bstr bstrError;
4466 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4467 {
4468 const char *sep = bstrError.isEmpty() ? "" : ": ";
4469 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4470 return setError(E_ACCESSDENIED,
4471 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4472 aKey.c_str(),
4473 aValue.c_str(),
4474 sep,
4475 bstrError.raw());
4476 }
4477
4478 // data is changing and change not vetoed: then write it out under the lock
4479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4480
4481 if (aValue.isEmpty())
4482 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4483 else
4484 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4485 // creates a new key if needed
4486
4487 bool fNeedsGlobalSaveSettings = false;
4488 // This saving of settings is tricky: there is no "old state" for the
4489 // extradata items at all (unlike all other settings), so the old/new
4490 // settings comparison would give a wrong result!
4491 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4492
4493 if (fNeedsGlobalSaveSettings)
4494 {
4495 // save the global settings; for that we should hold only the VirtualBox lock
4496 alock.release();
4497 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4498 mParent->i_saveSettings();
4499 }
4500 }
4501
4502 // fire notification outside the lock
4503 if (fChanged)
4504 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4505
4506 return S_OK;
4507}
4508
4509HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4510{
4511 aProgress = NULL;
4512 NOREF(aSettingsFilePath);
4513 ReturnComNotImplemented();
4514}
4515
4516HRESULT Machine::saveSettings()
4517{
4518 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4519
4520 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4521 if (FAILED(hrc)) return hrc;
4522
4523 /* the settings file path may never be null */
4524 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4525
4526 /* save all VM data excluding snapshots */
4527 bool fNeedsGlobalSaveSettings = false;
4528 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4529 mlock.release();
4530
4531 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4532 {
4533 // save the global settings; for that we should hold only the VirtualBox lock
4534 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4535 hrc = mParent->i_saveSettings();
4536 }
4537
4538 return hrc;
4539}
4540
4541
4542HRESULT Machine::discardSettings()
4543{
4544 /*
4545 * We need to take the machine list lock here as well as the machine one
4546 * or we'll get into trouble should any media stuff require rolling back.
4547 *
4548 * Details:
4549 *
4550 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4551 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4552 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
4553 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4554 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4555 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4556 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4557 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4558 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4559 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4560 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4561 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4562 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4563 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
4564 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
4565 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4566 * 0:005> k
4567 * # Child-SP RetAddr Call Site
4568 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4569 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4570 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4571 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4572 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4573 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4574 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4575 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4576 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4577 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4578 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4579 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4580 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4581 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4582 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4583 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4584 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4585 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4586 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4587 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4588 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4589 *
4590 */
4591 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4593
4594 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4595 if (FAILED(hrc)) return hrc;
4596
4597 /*
4598 * during this rollback, the session will be notified if data has
4599 * been actually changed
4600 */
4601 i_rollback(true /* aNotify */);
4602
4603 return S_OK;
4604}
4605
4606/** @note Locks objects! */
4607HRESULT Machine::unregister(AutoCaller &autoCaller,
4608 CleanupMode_T aCleanupMode,
4609 std::vector<ComPtr<IMedium> > &aMedia)
4610{
4611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 Guid id(i_getId());
4614
4615 if (mData->mSession.mState != SessionState_Unlocked)
4616 return setError(VBOX_E_INVALID_OBJECT_STATE,
4617 tr("Cannot unregister the machine '%s' while it is locked"),
4618 mUserData->s.strName.c_str());
4619
4620 // wait for state dependents to drop to zero
4621 i_ensureNoStateDependencies(alock);
4622
4623 if (!mData->mAccessible)
4624 {
4625 // inaccessible machines can only be unregistered; uninitialize ourselves
4626 // here because currently there may be no unregistered that are inaccessible
4627 // (this state combination is not supported). Note releasing the caller and
4628 // leaving the lock before calling uninit()
4629 alock.release();
4630 autoCaller.release();
4631
4632 uninit();
4633
4634 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4635 // calls VirtualBox::i_saveSettings()
4636
4637 return S_OK;
4638 }
4639
4640 HRESULT hrc = S_OK;
4641 mData->llFilesToDelete.clear();
4642
4643 if (!mSSData->strStateFilePath.isEmpty())
4644 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4645
4646 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4647 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4648 mData->llFilesToDelete.push_back(strNVRAMFile);
4649
4650 // This list collects the medium objects from all medium attachments
4651 // which we will detach from the machine and its snapshots, in a specific
4652 // order which allows for closing all media without getting "media in use"
4653 // errors, simply by going through the list from the front to the back:
4654 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4655 // and must be closed before the parent media from the snapshots, or closing the parents
4656 // will fail because they still have children);
4657 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4658 // the root ("first") snapshot of the machine.
4659 MediaList llMedia;
4660
4661 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4662 && mMediumAttachments->size()
4663 )
4664 {
4665 // we have media attachments: detach them all and add the Medium objects to our list
4666 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4667 }
4668
4669 if (mData->mFirstSnapshot)
4670 {
4671 // add the media from the medium attachments of the snapshots to
4672 // llMedia as well, after the "main" machine media;
4673 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4674 // snapshot machine, depth first.
4675
4676 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4677 MachineState_T oldState = mData->mMachineState;
4678 mData->mMachineState = MachineState_DeletingSnapshot;
4679
4680 // make a copy of the first snapshot reference so the refcount does not
4681 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4682 // (would hang due to the AutoCaller voodoo)
4683 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4684
4685 // GO!
4686 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4687
4688 mData->mMachineState = oldState;
4689 }
4690
4691 if (FAILED(hrc))
4692 {
4693 i_rollbackMedia();
4694 return hrc;
4695 }
4696
4697 // commit all the media changes made above
4698 i_commitMedia();
4699
4700 mData->mRegistered = false;
4701
4702 // machine lock no longer needed
4703 alock.release();
4704
4705 /* Make sure that the settings of the current VM are not saved, because
4706 * they are rather crippled at this point to meet the cleanup expectations
4707 * and there's no point destroying the VM config on disk just because. */
4708 mParent->i_unmarkRegistryModified(id);
4709
4710 // return media to caller
4711 aMedia.resize(llMedia.size());
4712 size_t i = 0;
4713 for (MediaList::const_iterator
4714 it = llMedia.begin();
4715 it != llMedia.end();
4716 ++it, ++i)
4717 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4718
4719 mParent->i_unregisterMachine(this, aCleanupMode, id);
4720 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4721
4722 return S_OK;
4723}
4724
4725/**
4726 * Task record for deleting a machine config.
4727 */
4728class Machine::DeleteConfigTask
4729 : public Machine::Task
4730{
4731public:
4732 DeleteConfigTask(Machine *m,
4733 Progress *p,
4734 const Utf8Str &t,
4735 const RTCList<ComPtr<IMedium> > &llMedia,
4736 const StringsList &llFilesToDelete)
4737 : Task(m, p, t),
4738 m_llMedia(llMedia),
4739 m_llFilesToDelete(llFilesToDelete)
4740 {}
4741
4742private:
4743 void handler()
4744 {
4745 try
4746 {
4747 m_pMachine->i_deleteConfigHandler(*this);
4748 }
4749 catch (...)
4750 {
4751 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4752 }
4753 }
4754
4755 RTCList<ComPtr<IMedium> > m_llMedia;
4756 StringsList m_llFilesToDelete;
4757
4758 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4759};
4760
4761/**
4762 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4763 * SessionMachine::taskHandler().
4764 *
4765 * @note Locks this object for writing.
4766 *
4767 * @param task
4768 */
4769void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4770{
4771 LogFlowThisFuncEnter();
4772
4773 AutoCaller autoCaller(this);
4774 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4775 if (FAILED(autoCaller.hrc()))
4776 {
4777 /* we might have been uninitialized because the session was accidentally
4778 * closed by the client, so don't assert */
4779 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4780 task.m_pProgress->i_notifyComplete(hrc);
4781 LogFlowThisFuncLeave();
4782 return;
4783 }
4784
4785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
4787 HRESULT hrc;
4788 try
4789 {
4790 ULONG uLogHistoryCount = 3;
4791 ComPtr<ISystemProperties> systemProperties;
4792 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4793 if (FAILED(hrc)) throw hrc;
4794
4795 if (!systemProperties.isNull())
4796 {
4797 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4798 if (FAILED(hrc)) throw hrc;
4799 }
4800
4801 MachineState_T oldState = mData->mMachineState;
4802 i_setMachineState(MachineState_SettingUp);
4803 alock.release();
4804 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4805 {
4806 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4807 {
4808 AutoCaller mac(pMedium);
4809 if (FAILED(mac.hrc())) throw mac.hrc();
4810 Utf8Str strLocation = pMedium->i_getLocationFull();
4811 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4812 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4813 if (FAILED(hrc)) throw hrc;
4814 }
4815 if (pMedium->i_isMediumFormatFile())
4816 {
4817 ComPtr<IProgress> pProgress2;
4818 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4819 if (FAILED(hrc)) throw hrc;
4820 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4821 if (FAILED(hrc)) throw hrc;
4822 }
4823
4824 /* Close the medium, deliberately without checking the return
4825 * code, and without leaving any trace in the error info, as
4826 * a failure here is a very minor issue, which shouldn't happen
4827 * as above we even managed to delete the medium. */
4828 {
4829 ErrorInfoKeeper eik;
4830 pMedium->Close();
4831 }
4832 }
4833 i_setMachineState(oldState);
4834 alock.acquire();
4835
4836 // delete the files pushed on the task list by Machine::Delete()
4837 // (this includes saved states of the machine and snapshots and
4838 // medium storage files from the IMedium list passed in, and the
4839 // machine XML file)
4840 for (StringsList::const_iterator
4841 it = task.m_llFilesToDelete.begin();
4842 it != task.m_llFilesToDelete.end();
4843 ++it)
4844 {
4845 const Utf8Str &strFile = *it;
4846 LogFunc(("Deleting file %s\n", strFile.c_str()));
4847 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4848 if (FAILED(hrc)) throw hrc;
4849 i_deleteFile(strFile);
4850 }
4851
4852 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4853 if (FAILED(hrc)) throw hrc;
4854
4855 /* delete the settings only when the file actually exists */
4856 if (mData->pMachineConfigFile->fileExists())
4857 {
4858 /* Delete any backup or uncommitted XML files. Ignore failures.
4859 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4860 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4861 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4862 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4863 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4864 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4865
4866 /* delete the Logs folder, nothing important should be left
4867 * there (we don't check for errors because the user might have
4868 * some private files there that we don't want to delete) */
4869 Utf8Str logFolder;
4870 getLogFolder(logFolder);
4871 Assert(logFolder.length());
4872 if (RTDirExists(logFolder.c_str()))
4873 {
4874 /* Delete all VBox.log[.N] files from the Logs folder
4875 * (this must be in sync with the rotation logic in
4876 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4877 * files that may have been created by the GUI. */
4878 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4879 i_deleteFile(log, true /* fIgnoreFailures */);
4880 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4881 i_deleteFile(log, true /* fIgnoreFailures */);
4882 for (ULONG i = uLogHistoryCount; i > 0; i--)
4883 {
4884 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4885 i_deleteFile(log, true /* fIgnoreFailures */);
4886 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4887 i_deleteFile(log, true /* fIgnoreFailures */);
4888 }
4889 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4890 i_deleteFile(log, true /* fIgnoreFailures */);
4891#if defined(RT_OS_WINDOWS)
4892 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4893 i_deleteFile(log, true /* fIgnoreFailures */);
4894 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4895 i_deleteFile(log, true /* fIgnoreFailures */);
4896#endif
4897
4898 RTDirRemove(logFolder.c_str());
4899 }
4900
4901 /* delete the Snapshots folder, nothing important should be left
4902 * there (we don't check for errors because the user might have
4903 * some private files there that we don't want to delete) */
4904 Utf8Str strFullSnapshotFolder;
4905 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4906 Assert(!strFullSnapshotFolder.isEmpty());
4907 if (RTDirExists(strFullSnapshotFolder.c_str()))
4908 RTDirRemove(strFullSnapshotFolder.c_str());
4909
4910 // delete the directory that contains the settings file, but only
4911 // if it matches the VM name
4912 Utf8Str settingsDir;
4913 if (i_isInOwnDir(&settingsDir))
4914 RTDirRemove(settingsDir.c_str());
4915 }
4916
4917 alock.release();
4918
4919 mParent->i_saveModifiedRegistries();
4920 }
4921 catch (HRESULT hrcXcpt)
4922 {
4923 hrc = hrcXcpt;
4924 }
4925
4926 task.m_pProgress->i_notifyComplete(hrc);
4927
4928 LogFlowThisFuncLeave();
4929}
4930
4931HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4932{
4933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4934
4935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4936 if (FAILED(hrc)) return hrc;
4937
4938 if (mData->mRegistered)
4939 return setError(VBOX_E_INVALID_VM_STATE,
4940 tr("Cannot delete settings of a registered machine"));
4941
4942 // collect files to delete
4943 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4944 // machine config file
4945 if (mData->pMachineConfigFile->fileExists())
4946 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4947 // backup of machine config file
4948 Utf8Str strTmp(mData->m_strConfigFileFull);
4949 strTmp.append("-prev");
4950 if (RTFileExists(strTmp.c_str()))
4951 llFilesToDelete.push_back(strTmp);
4952
4953 RTCList<ComPtr<IMedium> > llMedia;
4954 for (size_t i = 0; i < aMedia.size(); ++i)
4955 {
4956 IMedium *pIMedium(aMedia[i]);
4957 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4958 if (pMedium.isNull())
4959 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4960 SafeArray<BSTR> ids;
4961 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4962 if (FAILED(hrc)) return hrc;
4963 /* At this point the medium should not have any back references
4964 * anymore. If it has it is attached to another VM and *must* not
4965 * deleted. */
4966 if (ids.size() < 1)
4967 llMedia.append(pMedium);
4968 }
4969
4970 ComObjPtr<Progress> pProgress;
4971 pProgress.createObject();
4972 hrc = pProgress->init(i_getVirtualBox(),
4973 static_cast<IMachine*>(this) /* aInitiator */,
4974 tr("Deleting files"),
4975 true /* fCancellable */,
4976 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4977 tr("Collecting file inventory"));
4978 if (FAILED(hrc))
4979 return hrc;
4980
4981 /* create and start the task on a separate thread (note that it will not
4982 * start working until we release alock) */
4983 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
4984 hrc = pTask->createThread();
4985 pTask = NULL;
4986 if (FAILED(hrc))
4987 return hrc;
4988
4989 pProgress.queryInterfaceTo(aProgress.asOutParam());
4990
4991 LogFlowFuncLeave();
4992
4993 return S_OK;
4994}
4995
4996HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
4997{
4998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4999
5000 ComObjPtr<Snapshot> pSnapshot;
5001 HRESULT hrc;
5002
5003 if (aNameOrId.isEmpty())
5004 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5005 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5006 else
5007 {
5008 Guid uuid(aNameOrId);
5009 if (uuid.isValid())
5010 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5011 else
5012 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5013 }
5014 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5015
5016 return hrc;
5017}
5018
5019HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5020 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5021{
5022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5023
5024 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5025 if (FAILED(hrc)) return hrc;
5026
5027 ComObjPtr<SharedFolder> sharedFolder;
5028 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5029 if (SUCCEEDED(hrc))
5030 return setError(VBOX_E_OBJECT_IN_USE,
5031 tr("Shared folder named '%s' already exists"),
5032 aName.c_str());
5033
5034 SymlinkPolicy_T enmSymlinkPolicy = SymlinkPolicy_None;
5035 sharedFolder.createObject();
5036 hrc = sharedFolder->init(i_getMachine(),
5037 aName,
5038 aHostPath,
5039 !!aWritable,
5040 !!aAutomount,
5041 aAutoMountPoint,
5042 true /* fFailOnError */,
5043 enmSymlinkPolicy);
5044 if (FAILED(hrc)) return hrc;
5045
5046 i_setModified(IsModified_SharedFolders);
5047 mHWData.backup();
5048 mHWData->mSharedFolders.push_back(sharedFolder);
5049
5050 /* inform the direct session if any */
5051 alock.release();
5052 i_onSharedFolderChange();
5053
5054 return S_OK;
5055}
5056
5057HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5058{
5059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5060
5061 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5062 if (FAILED(hrc)) return hrc;
5063
5064 ComObjPtr<SharedFolder> sharedFolder;
5065 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5066 if (FAILED(hrc)) return hrc;
5067
5068 i_setModified(IsModified_SharedFolders);
5069 mHWData.backup();
5070 mHWData->mSharedFolders.remove(sharedFolder);
5071
5072 /* inform the direct session if any */
5073 alock.release();
5074 i_onSharedFolderChange();
5075
5076 return S_OK;
5077}
5078
5079HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5080{
5081 /* start with No */
5082 *aCanShow = FALSE;
5083
5084 ComPtr<IInternalSessionControl> directControl;
5085 {
5086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5087
5088 if (mData->mSession.mState != SessionState_Locked)
5089 return setError(VBOX_E_INVALID_VM_STATE,
5090 tr("Machine is not locked for session (session state: %s)"),
5091 Global::stringifySessionState(mData->mSession.mState));
5092
5093 if (mData->mSession.mLockType == LockType_VM)
5094 directControl = mData->mSession.mDirectControl;
5095 }
5096
5097 /* ignore calls made after #OnSessionEnd() is called */
5098 if (!directControl)
5099 return S_OK;
5100
5101 LONG64 dummy;
5102 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5103}
5104
5105HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5106{
5107 ComPtr<IInternalSessionControl> directControl;
5108 {
5109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5110
5111 if (mData->mSession.mState != SessionState_Locked)
5112 return setError(E_FAIL,
5113 tr("Machine is not locked for session (session state: %s)"),
5114 Global::stringifySessionState(mData->mSession.mState));
5115
5116 if (mData->mSession.mLockType == LockType_VM)
5117 directControl = mData->mSession.mDirectControl;
5118 }
5119
5120 /* ignore calls made after #OnSessionEnd() is called */
5121 if (!directControl)
5122 return S_OK;
5123
5124 BOOL dummy;
5125 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5126}
5127
5128#ifdef VBOX_WITH_GUEST_PROPS
5129/**
5130 * Look up a guest property in VBoxSVC's internal structures.
5131 */
5132HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5133 com::Utf8Str &aValue,
5134 LONG64 *aTimestamp,
5135 com::Utf8Str &aFlags) const
5136{
5137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5138
5139 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5140 if (it != mHWData->mGuestProperties.end())
5141 {
5142 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5143 aValue = it->second.strValue;
5144 *aTimestamp = it->second.mTimestamp;
5145 GuestPropWriteFlags(it->second.mFlags, szFlags);
5146 aFlags = Utf8Str(szFlags);
5147 }
5148
5149 return S_OK;
5150}
5151
5152/**
5153 * Query the VM that a guest property belongs to for the property.
5154 * @returns E_ACCESSDENIED if the VM process is not available or not
5155 * currently handling queries and the lookup should then be done in
5156 * VBoxSVC.
5157 */
5158HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5159 com::Utf8Str &aValue,
5160 LONG64 *aTimestamp,
5161 com::Utf8Str &aFlags) const
5162{
5163 HRESULT hrc = S_OK;
5164 Bstr bstrValue;
5165 Bstr bstrFlags;
5166
5167 ComPtr<IInternalSessionControl> directControl;
5168 {
5169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5170 if (mData->mSession.mLockType == LockType_VM)
5171 directControl = mData->mSession.mDirectControl;
5172 }
5173
5174 /* ignore calls made after #OnSessionEnd() is called */
5175 if (!directControl)
5176 hrc = E_ACCESSDENIED;
5177 else
5178 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5179 0 /* accessMode */,
5180 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5181
5182 aValue = bstrValue;
5183 aFlags = bstrFlags;
5184
5185 return hrc;
5186}
5187#endif // VBOX_WITH_GUEST_PROPS
5188
5189HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5190 com::Utf8Str &aValue,
5191 LONG64 *aTimestamp,
5192 com::Utf8Str &aFlags)
5193{
5194#ifndef VBOX_WITH_GUEST_PROPS
5195 ReturnComNotImplemented();
5196#else // VBOX_WITH_GUEST_PROPS
5197
5198 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5199
5200 if (hrc == E_ACCESSDENIED)
5201 /* The VM is not running or the service is not (yet) accessible */
5202 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5203 return hrc;
5204#endif // VBOX_WITH_GUEST_PROPS
5205}
5206
5207HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5208{
5209 LONG64 dummyTimestamp;
5210 com::Utf8Str dummyFlags;
5211 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5212
5213}
5214HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5215{
5216 com::Utf8Str dummyFlags;
5217 com::Utf8Str dummyValue;
5218 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5219}
5220
5221#ifdef VBOX_WITH_GUEST_PROPS
5222/**
5223 * Set a guest property in VBoxSVC's internal structures.
5224 */
5225HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5226 const com::Utf8Str &aFlags, bool fDelete)
5227{
5228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5229 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5230 if (FAILED(hrc)) return hrc;
5231
5232 try
5233 {
5234 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5235 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5236 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5237
5238 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5239 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5240
5241 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5242 if (it == mHWData->mGuestProperties.end())
5243 {
5244 if (!fDelete)
5245 {
5246 i_setModified(IsModified_MachineData);
5247 mHWData.backupEx();
5248
5249 RTTIMESPEC time;
5250 HWData::GuestProperty prop;
5251 prop.strValue = aValue;
5252 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5253 prop.mFlags = fFlags;
5254 mHWData->mGuestProperties[aName] = prop;
5255 }
5256 }
5257 else
5258 {
5259 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5260 {
5261 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5262 }
5263 else
5264 {
5265 i_setModified(IsModified_MachineData);
5266 mHWData.backupEx();
5267
5268 /* The backupEx() operation invalidates our iterator,
5269 * so get a new one. */
5270 it = mHWData->mGuestProperties.find(aName);
5271 Assert(it != mHWData->mGuestProperties.end());
5272
5273 if (!fDelete)
5274 {
5275 RTTIMESPEC time;
5276 it->second.strValue = aValue;
5277 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5278 it->second.mFlags = fFlags;
5279 }
5280 else
5281 mHWData->mGuestProperties.erase(it);
5282 }
5283 }
5284
5285 if (SUCCEEDED(hrc))
5286 {
5287 alock.release();
5288
5289 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5290 }
5291 }
5292 catch (std::bad_alloc &)
5293 {
5294 hrc = E_OUTOFMEMORY;
5295 }
5296
5297 return hrc;
5298}
5299
5300/**
5301 * Set a property on the VM that that property belongs to.
5302 * @returns E_ACCESSDENIED if the VM process is not available or not
5303 * currently handling queries and the setting should then be done in
5304 * VBoxSVC.
5305 */
5306HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5307 const com::Utf8Str &aFlags, bool fDelete)
5308{
5309 HRESULT hrc;
5310
5311 try
5312 {
5313 ComPtr<IInternalSessionControl> directControl;
5314 {
5315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5316 if (mData->mSession.mLockType == LockType_VM)
5317 directControl = mData->mSession.mDirectControl;
5318 }
5319
5320 Bstr dummy1; /* will not be changed (setter) */
5321 Bstr dummy2; /* will not be changed (setter) */
5322 LONG64 dummy64;
5323 if (!directControl)
5324 hrc = E_ACCESSDENIED;
5325 else
5326 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5327 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5328 fDelete ? 2 : 1 /* accessMode */,
5329 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5330 }
5331 catch (std::bad_alloc &)
5332 {
5333 hrc = E_OUTOFMEMORY;
5334 }
5335
5336 return hrc;
5337}
5338#endif // VBOX_WITH_GUEST_PROPS
5339
5340HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5341 const com::Utf8Str &aFlags)
5342{
5343#ifndef VBOX_WITH_GUEST_PROPS
5344 ReturnComNotImplemented();
5345#else // VBOX_WITH_GUEST_PROPS
5346
5347 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5348 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5349
5350 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5351 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5352
5353 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5354 if (hrc == E_ACCESSDENIED)
5355 /* The VM is not running or the service is not (yet) accessible */
5356 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5357 return hrc;
5358#endif // VBOX_WITH_GUEST_PROPS
5359}
5360
5361HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5362{
5363 return setGuestProperty(aProperty, aValue, "");
5364}
5365
5366HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5367{
5368#ifndef VBOX_WITH_GUEST_PROPS
5369 ReturnComNotImplemented();
5370#else // VBOX_WITH_GUEST_PROPS
5371 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5372 if (hrc == E_ACCESSDENIED)
5373 /* The VM is not running or the service is not (yet) accessible */
5374 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5375 return hrc;
5376#endif // VBOX_WITH_GUEST_PROPS
5377}
5378
5379#ifdef VBOX_WITH_GUEST_PROPS
5380/**
5381 * Enumerate the guest properties in VBoxSVC's internal structures.
5382 */
5383HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5384 std::vector<com::Utf8Str> &aNames,
5385 std::vector<com::Utf8Str> &aValues,
5386 std::vector<LONG64> &aTimestamps,
5387 std::vector<com::Utf8Str> &aFlags)
5388{
5389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5390 Utf8Str strPatterns(aPatterns);
5391
5392 /*
5393 * Look for matching patterns and build up a list.
5394 */
5395 HWData::GuestPropertyMap propMap;
5396 for (HWData::GuestPropertyMap::const_iterator
5397 it = mHWData->mGuestProperties.begin();
5398 it != mHWData->mGuestProperties.end();
5399 ++it)
5400 {
5401 if ( strPatterns.isEmpty()
5402 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5403 RTSTR_MAX,
5404 it->first.c_str(),
5405 RTSTR_MAX,
5406 NULL)
5407 )
5408 propMap.insert(*it);
5409 }
5410
5411 alock.release();
5412
5413 /*
5414 * And build up the arrays for returning the property information.
5415 */
5416 size_t cEntries = propMap.size();
5417
5418 aNames.resize(cEntries);
5419 aValues.resize(cEntries);
5420 aTimestamps.resize(cEntries);
5421 aFlags.resize(cEntries);
5422
5423 size_t i = 0;
5424 for (HWData::GuestPropertyMap::const_iterator
5425 it = propMap.begin();
5426 it != propMap.end();
5427 ++it, ++i)
5428 {
5429 aNames[i] = it->first;
5430 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5431 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5432
5433 aValues[i] = it->second.strValue;
5434 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5435 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5436
5437 aTimestamps[i] = it->second.mTimestamp;
5438
5439 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5440 GuestPropWriteFlags(it->second.mFlags, szFlags);
5441 aFlags[i] = szFlags;
5442 }
5443
5444 return S_OK;
5445}
5446
5447/**
5448 * Enumerate the properties managed by a VM.
5449 * @returns E_ACCESSDENIED if the VM process is not available or not
5450 * currently handling queries and the setting should then be done in
5451 * VBoxSVC.
5452 */
5453HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5454 std::vector<com::Utf8Str> &aNames,
5455 std::vector<com::Utf8Str> &aValues,
5456 std::vector<LONG64> &aTimestamps,
5457 std::vector<com::Utf8Str> &aFlags)
5458{
5459 HRESULT hrc;
5460 ComPtr<IInternalSessionControl> directControl;
5461 {
5462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5463 if (mData->mSession.mLockType == LockType_VM)
5464 directControl = mData->mSession.mDirectControl;
5465 }
5466
5467 com::SafeArray<BSTR> bNames;
5468 com::SafeArray<BSTR> bValues;
5469 com::SafeArray<LONG64> bTimestamps;
5470 com::SafeArray<BSTR> bFlags;
5471
5472 if (!directControl)
5473 hrc = E_ACCESSDENIED;
5474 else
5475 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5476 ComSafeArrayAsOutParam(bNames),
5477 ComSafeArrayAsOutParam(bValues),
5478 ComSafeArrayAsOutParam(bTimestamps),
5479 ComSafeArrayAsOutParam(bFlags));
5480 size_t i;
5481 aNames.resize(bNames.size());
5482 for (i = 0; i < bNames.size(); ++i)
5483 aNames[i] = Utf8Str(bNames[i]);
5484 aValues.resize(bValues.size());
5485 for (i = 0; i < bValues.size(); ++i)
5486 aValues[i] = Utf8Str(bValues[i]);
5487 aTimestamps.resize(bTimestamps.size());
5488 for (i = 0; i < bTimestamps.size(); ++i)
5489 aTimestamps[i] = bTimestamps[i];
5490 aFlags.resize(bFlags.size());
5491 for (i = 0; i < bFlags.size(); ++i)
5492 aFlags[i] = Utf8Str(bFlags[i]);
5493
5494 return hrc;
5495}
5496#endif // VBOX_WITH_GUEST_PROPS
5497HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5498 std::vector<com::Utf8Str> &aNames,
5499 std::vector<com::Utf8Str> &aValues,
5500 std::vector<LONG64> &aTimestamps,
5501 std::vector<com::Utf8Str> &aFlags)
5502{
5503#ifndef VBOX_WITH_GUEST_PROPS
5504 ReturnComNotImplemented();
5505#else // VBOX_WITH_GUEST_PROPS
5506
5507 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5508
5509 if (hrc == E_ACCESSDENIED)
5510 /* The VM is not running or the service is not (yet) accessible */
5511 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5512 return hrc;
5513#endif // VBOX_WITH_GUEST_PROPS
5514}
5515
5516HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5517 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5518{
5519 MediumAttachmentList atts;
5520
5521 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5522 if (FAILED(hrc)) return hrc;
5523
5524 aMediumAttachments.resize(atts.size());
5525 size_t i = 0;
5526 for (MediumAttachmentList::const_iterator
5527 it = atts.begin();
5528 it != atts.end();
5529 ++it, ++i)
5530 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5531
5532 return S_OK;
5533}
5534
5535HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5536 LONG aControllerPort,
5537 LONG aDevice,
5538 ComPtr<IMediumAttachment> &aAttachment)
5539{
5540 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5541 aName.c_str(), aControllerPort, aDevice));
5542
5543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5544
5545 aAttachment = NULL;
5546
5547 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5548 aName,
5549 aControllerPort,
5550 aDevice);
5551 if (pAttach.isNull())
5552 return setError(VBOX_E_OBJECT_NOT_FOUND,
5553 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5554 aDevice, aControllerPort, aName.c_str());
5555
5556 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5557
5558 return S_OK;
5559}
5560
5561
5562HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5563 StorageBus_T aConnectionType,
5564 ComPtr<IStorageController> &aController)
5565{
5566 if ( (aConnectionType <= StorageBus_Null)
5567 || (aConnectionType > StorageBus_VirtioSCSI))
5568 return setError(E_INVALIDARG,
5569 tr("Invalid connection type: %d"),
5570 aConnectionType);
5571
5572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5573
5574 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5575 if (FAILED(hrc)) return hrc;
5576
5577 /* try to find one with the name first. */
5578 ComObjPtr<StorageController> ctrl;
5579
5580 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5581 if (SUCCEEDED(hrc))
5582 return setError(VBOX_E_OBJECT_IN_USE,
5583 tr("Storage controller named '%s' already exists"),
5584 aName.c_str());
5585
5586 ctrl.createObject();
5587
5588 /* get a new instance number for the storage controller */
5589 ULONG ulInstance = 0;
5590 bool fBootable = true;
5591 for (StorageControllerList::const_iterator
5592 it = mStorageControllers->begin();
5593 it != mStorageControllers->end();
5594 ++it)
5595 {
5596 if ((*it)->i_getStorageBus() == aConnectionType)
5597 {
5598 ULONG ulCurInst = (*it)->i_getInstance();
5599
5600 if (ulCurInst >= ulInstance)
5601 ulInstance = ulCurInst + 1;
5602
5603 /* Only one controller of each type can be marked as bootable. */
5604 if ((*it)->i_getBootable())
5605 fBootable = false;
5606 }
5607 }
5608
5609 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5610 if (FAILED(hrc)) return hrc;
5611
5612 i_setModified(IsModified_Storage);
5613 mStorageControllers.backup();
5614 mStorageControllers->push_back(ctrl);
5615
5616 ctrl.queryInterfaceTo(aController.asOutParam());
5617
5618 /* inform the direct session if any */
5619 alock.release();
5620 i_onStorageControllerChange(i_getId(), aName);
5621
5622 return S_OK;
5623}
5624
5625HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5626 ComPtr<IStorageController> &aStorageController)
5627{
5628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5629
5630 ComObjPtr<StorageController> ctrl;
5631
5632 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5633 if (SUCCEEDED(hrc))
5634 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5635
5636 return hrc;
5637}
5638
5639HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5640 ULONG aInstance,
5641 ComPtr<IStorageController> &aStorageController)
5642{
5643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5644
5645 for (StorageControllerList::const_iterator
5646 it = mStorageControllers->begin();
5647 it != mStorageControllers->end();
5648 ++it)
5649 {
5650 if ( (*it)->i_getStorageBus() == aConnectionType
5651 && (*it)->i_getInstance() == aInstance)
5652 {
5653 (*it).queryInterfaceTo(aStorageController.asOutParam());
5654 return S_OK;
5655 }
5656 }
5657
5658 return setError(VBOX_E_OBJECT_NOT_FOUND,
5659 tr("Could not find a storage controller with instance number '%lu'"),
5660 aInstance);
5661}
5662
5663HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5664{
5665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5666
5667 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5668 if (FAILED(hrc)) return hrc;
5669
5670 ComObjPtr<StorageController> ctrl;
5671
5672 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5673 if (SUCCEEDED(hrc))
5674 {
5675 /* Ensure that only one controller of each type is marked as bootable. */
5676 if (aBootable == TRUE)
5677 {
5678 for (StorageControllerList::const_iterator
5679 it = mStorageControllers->begin();
5680 it != mStorageControllers->end();
5681 ++it)
5682 {
5683 ComObjPtr<StorageController> aCtrl = (*it);
5684
5685 if ( (aCtrl->i_getName() != aName)
5686 && aCtrl->i_getBootable() == TRUE
5687 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5688 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5689 {
5690 aCtrl->i_setBootable(FALSE);
5691 break;
5692 }
5693 }
5694 }
5695
5696 if (SUCCEEDED(hrc))
5697 {
5698 ctrl->i_setBootable(aBootable);
5699 i_setModified(IsModified_Storage);
5700 }
5701 }
5702
5703 if (SUCCEEDED(hrc))
5704 {
5705 /* inform the direct session if any */
5706 alock.release();
5707 i_onStorageControllerChange(i_getId(), aName);
5708 }
5709
5710 return hrc;
5711}
5712
5713HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5714{
5715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5716
5717 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5718 if (FAILED(hrc)) return hrc;
5719
5720 ComObjPtr<StorageController> ctrl;
5721 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5722 if (FAILED(hrc)) return hrc;
5723
5724 MediumAttachmentList llDetachedAttachments;
5725 {
5726 /* find all attached devices to the appropriate storage controller and detach them all */
5727 // make a temporary list because detachDevice invalidates iterators into
5728 // mMediumAttachments
5729 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5730
5731 for (MediumAttachmentList::const_iterator
5732 it = llAttachments2.begin();
5733 it != llAttachments2.end();
5734 ++it)
5735 {
5736 MediumAttachment *pAttachTemp = *it;
5737
5738 AutoCaller localAutoCaller(pAttachTemp);
5739 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
5740
5741 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5742
5743 if (pAttachTemp->i_getControllerName() == aName)
5744 {
5745 llDetachedAttachments.push_back(pAttachTemp);
5746 hrc = i_detachDevice(pAttachTemp, alock, NULL);
5747 if (FAILED(hrc)) return hrc;
5748 }
5749 }
5750 }
5751
5752 /* send event about detached devices before removing parent controller */
5753 for (MediumAttachmentList::const_iterator
5754 it = llDetachedAttachments.begin();
5755 it != llDetachedAttachments.end();
5756 ++it)
5757 {
5758 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5759 }
5760
5761 /* We can remove it now. */
5762 i_setModified(IsModified_Storage);
5763 mStorageControllers.backup();
5764
5765 ctrl->i_unshare();
5766
5767 mStorageControllers->remove(ctrl);
5768
5769 /* inform the direct session if any */
5770 alock.release();
5771 i_onStorageControllerChange(i_getId(), aName);
5772
5773 return S_OK;
5774}
5775
5776HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5777 ComPtr<IUSBController> &aController)
5778{
5779 if ( (aType <= USBControllerType_Null)
5780 || (aType >= USBControllerType_Last))
5781 return setError(E_INVALIDARG,
5782 tr("Invalid USB controller type: %d"),
5783 aType);
5784
5785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5786
5787 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5788 if (FAILED(hrc)) return hrc;
5789
5790 /* try to find one with the same type first. */
5791 ComObjPtr<USBController> ctrl;
5792
5793 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
5794 if (SUCCEEDED(hrc))
5795 return setError(VBOX_E_OBJECT_IN_USE,
5796 tr("USB controller named '%s' already exists"),
5797 aName.c_str());
5798
5799 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
5800 ChipsetType_T enmChipsetType;
5801 hrc = mPlatform->getChipsetType(&enmChipsetType);
5802 if (FAILED(hrc))
5803 return hrc;
5804
5805 ULONG maxInstances;
5806 hrc = mPlatformProperties->GetMaxInstancesOfUSBControllerType(enmChipsetType, aType, &maxInstances);
5807 if (FAILED(hrc))
5808 return hrc;
5809
5810 ULONG cInstances = i_getUSBControllerCountByType(aType);
5811 if (cInstances >= maxInstances)
5812 return setError(E_INVALIDARG,
5813 tr("Too many USB controllers of this type"));
5814
5815 ctrl.createObject();
5816
5817 hrc = ctrl->init(this, aName, aType);
5818 if (FAILED(hrc)) return hrc;
5819
5820 i_setModified(IsModified_USB);
5821 mUSBControllers.backup();
5822 mUSBControllers->push_back(ctrl);
5823
5824 ctrl.queryInterfaceTo(aController.asOutParam());
5825
5826 /* inform the direct session if any */
5827 alock.release();
5828 i_onUSBControllerChange();
5829
5830 return S_OK;
5831}
5832
5833HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
5834{
5835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5836
5837 ComObjPtr<USBController> ctrl;
5838
5839 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5840 if (SUCCEEDED(hrc))
5841 ctrl.queryInterfaceTo(aController.asOutParam());
5842
5843 return hrc;
5844}
5845
5846HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
5847 ULONG *aControllers)
5848{
5849 if ( (aType <= USBControllerType_Null)
5850 || (aType >= USBControllerType_Last))
5851 return setError(E_INVALIDARG,
5852 tr("Invalid USB controller type: %d"),
5853 aType);
5854
5855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5856
5857 ComObjPtr<USBController> ctrl;
5858
5859 *aControllers = i_getUSBControllerCountByType(aType);
5860
5861 return S_OK;
5862}
5863
5864HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
5865{
5866
5867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5868
5869 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5870 if (FAILED(hrc)) return hrc;
5871
5872 ComObjPtr<USBController> ctrl;
5873 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
5874 if (FAILED(hrc)) return hrc;
5875
5876 i_setModified(IsModified_USB);
5877 mUSBControllers.backup();
5878
5879 ctrl->i_unshare();
5880
5881 mUSBControllers->remove(ctrl);
5882
5883 /* inform the direct session if any */
5884 alock.release();
5885 i_onUSBControllerChange();
5886
5887 return S_OK;
5888}
5889
5890HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
5891 ULONG *aOriginX,
5892 ULONG *aOriginY,
5893 ULONG *aWidth,
5894 ULONG *aHeight,
5895 BOOL *aEnabled)
5896{
5897 uint32_t u32OriginX= 0;
5898 uint32_t u32OriginY= 0;
5899 uint32_t u32Width = 0;
5900 uint32_t u32Height = 0;
5901 uint16_t u16Flags = 0;
5902
5903#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5904 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5905#else
5906 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5907#endif
5908 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
5909 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5910 if (RT_FAILURE(vrc))
5911 {
5912#ifdef RT_OS_WINDOWS
5913 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5914 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5915 * So just assign fEnable to TRUE again.
5916 * The right fix would be to change GUI API wrappers to make sure that parameters
5917 * are changed only if API succeeds.
5918 */
5919 *aEnabled = TRUE;
5920#endif
5921 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5922 tr("Saved guest size is not available (%Rrc)"),
5923 vrc);
5924 }
5925
5926 *aOriginX = u32OriginX;
5927 *aOriginY = u32OriginY;
5928 *aWidth = u32Width;
5929 *aHeight = u32Height;
5930 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5931
5932 return S_OK;
5933}
5934
5935HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
5936 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
5937{
5938 if (aScreenId != 0)
5939 return E_NOTIMPL;
5940
5941 if ( aBitmapFormat != BitmapFormat_BGR0
5942 && aBitmapFormat != BitmapFormat_BGRA
5943 && aBitmapFormat != BitmapFormat_RGBA
5944 && aBitmapFormat != BitmapFormat_PNG)
5945 return setError(E_NOTIMPL,
5946 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
5947
5948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5949
5950 uint8_t *pu8Data = NULL;
5951 uint32_t cbData = 0;
5952 uint32_t u32Width = 0;
5953 uint32_t u32Height = 0;
5954
5955#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
5956 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
5957#else
5958 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
5959#endif
5960 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
5961 &pu8Data, &cbData, &u32Width, &u32Height);
5962 if (RT_FAILURE(vrc))
5963 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5964 tr("Saved thumbnail data is not available (%Rrc)"),
5965 vrc);
5966
5967 HRESULT hrc = S_OK;
5968
5969 *aWidth = u32Width;
5970 *aHeight = u32Height;
5971
5972 if (cbData > 0)
5973 {
5974 /* Convert pixels to the format expected by the API caller. */
5975 if (aBitmapFormat == BitmapFormat_BGR0)
5976 {
5977 /* [0] B, [1] G, [2] R, [3] 0. */
5978 aData.resize(cbData);
5979 memcpy(&aData.front(), pu8Data, cbData);
5980 }
5981 else if (aBitmapFormat == BitmapFormat_BGRA)
5982 {
5983 /* [0] B, [1] G, [2] R, [3] A. */
5984 aData.resize(cbData);
5985 for (uint32_t i = 0; i < cbData; i += 4)
5986 {
5987 aData[i] = pu8Data[i];
5988 aData[i + 1] = pu8Data[i + 1];
5989 aData[i + 2] = pu8Data[i + 2];
5990 aData[i + 3] = 0xff;
5991 }
5992 }
5993 else if (aBitmapFormat == BitmapFormat_RGBA)
5994 {
5995 /* [0] R, [1] G, [2] B, [3] A. */
5996 aData.resize(cbData);
5997 for (uint32_t i = 0; i < cbData; i += 4)
5998 {
5999 aData[i] = pu8Data[i + 2];
6000 aData[i + 1] = pu8Data[i + 1];
6001 aData[i + 2] = pu8Data[i];
6002 aData[i + 3] = 0xff;
6003 }
6004 }
6005 else if (aBitmapFormat == BitmapFormat_PNG)
6006 {
6007 uint8_t *pu8PNG = NULL;
6008 uint32_t cbPNG = 0;
6009 uint32_t cxPNG = 0;
6010 uint32_t cyPNG = 0;
6011
6012 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6013
6014 if (RT_SUCCESS(vrc))
6015 {
6016 aData.resize(cbPNG);
6017 if (cbPNG)
6018 memcpy(&aData.front(), pu8PNG, cbPNG);
6019 }
6020 else
6021 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6022
6023 RTMemFree(pu8PNG);
6024 }
6025 }
6026
6027 freeSavedDisplayScreenshot(pu8Data);
6028
6029 return hrc;
6030}
6031
6032HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6033 ULONG *aWidth,
6034 ULONG *aHeight,
6035 std::vector<BitmapFormat_T> &aBitmapFormats)
6036{
6037 if (aScreenId != 0)
6038 return E_NOTIMPL;
6039
6040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6041
6042 uint8_t *pu8Data = NULL;
6043 uint32_t cbData = 0;
6044 uint32_t u32Width = 0;
6045 uint32_t u32Height = 0;
6046
6047#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6048 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6049#else
6050 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6051#endif
6052 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6053 &pu8Data, &cbData, &u32Width, &u32Height);
6054
6055 if (RT_FAILURE(vrc))
6056 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6057 tr("Saved screenshot data is not available (%Rrc)"),
6058 vrc);
6059
6060 *aWidth = u32Width;
6061 *aHeight = u32Height;
6062 aBitmapFormats.resize(1);
6063 aBitmapFormats[0] = BitmapFormat_PNG;
6064
6065 freeSavedDisplayScreenshot(pu8Data);
6066
6067 return S_OK;
6068}
6069
6070HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6071 BitmapFormat_T aBitmapFormat,
6072 ULONG *aWidth,
6073 ULONG *aHeight,
6074 std::vector<BYTE> &aData)
6075{
6076 if (aScreenId != 0)
6077 return E_NOTIMPL;
6078
6079 if (aBitmapFormat != BitmapFormat_PNG)
6080 return E_NOTIMPL;
6081
6082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 uint8_t *pu8Data = NULL;
6085 uint32_t cbData = 0;
6086 uint32_t u32Width = 0;
6087 uint32_t u32Height = 0;
6088
6089#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6090 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6091#else
6092 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6093#endif
6094 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6095 &pu8Data, &cbData, &u32Width, &u32Height);
6096
6097 if (RT_FAILURE(vrc))
6098 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6099 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6100 vrc);
6101
6102 *aWidth = u32Width;
6103 *aHeight = u32Height;
6104
6105 aData.resize(cbData);
6106 if (cbData)
6107 memcpy(&aData.front(), pu8Data, cbData);
6108
6109 freeSavedDisplayScreenshot(pu8Data);
6110
6111 return S_OK;
6112}
6113
6114HRESULT Machine::hotPlugCPU(ULONG aCpu)
6115{
6116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6117
6118 if (!mHWData->mCPUHotPlugEnabled)
6119 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6120
6121 if (aCpu >= mHWData->mCPUCount)
6122 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6123
6124 if (mHWData->mCPUAttached[aCpu])
6125 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6126
6127 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6128 if (FAILED(hrc)) return hrc;
6129
6130 alock.release();
6131 hrc = i_onCPUChange(aCpu, false);
6132 alock.acquire();
6133 if (FAILED(hrc)) return hrc;
6134
6135 i_setModified(IsModified_MachineData);
6136 mHWData.backup();
6137 mHWData->mCPUAttached[aCpu] = true;
6138
6139 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6140 if (Global::IsOnline(mData->mMachineState))
6141 i_saveSettings(NULL, alock);
6142
6143 return S_OK;
6144}
6145
6146HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6147{
6148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 if (!mHWData->mCPUHotPlugEnabled)
6151 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6152
6153 if (aCpu >= SchemaDefs::MaxCPUCount)
6154 return setError(E_INVALIDARG,
6155 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6156 SchemaDefs::MaxCPUCount);
6157
6158 if (!mHWData->mCPUAttached[aCpu])
6159 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6160
6161 /* CPU 0 can't be detached */
6162 if (aCpu == 0)
6163 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6164
6165 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6166 if (FAILED(hrc)) return hrc;
6167
6168 alock.release();
6169 hrc = i_onCPUChange(aCpu, true);
6170 alock.acquire();
6171 if (FAILED(hrc)) return hrc;
6172
6173 i_setModified(IsModified_MachineData);
6174 mHWData.backup();
6175 mHWData->mCPUAttached[aCpu] = false;
6176
6177 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6178 if (Global::IsOnline(mData->mMachineState))
6179 i_saveSettings(NULL, alock);
6180
6181 return S_OK;
6182}
6183
6184HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6185{
6186 *aAttached = false;
6187
6188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6189
6190 /* If hotplug is enabled the CPU is always enabled. */
6191 if (!mHWData->mCPUHotPlugEnabled)
6192 {
6193 if (aCpu < mHWData->mCPUCount)
6194 *aAttached = true;
6195 }
6196 else
6197 {
6198 if (aCpu < SchemaDefs::MaxCPUCount)
6199 *aAttached = mHWData->mCPUAttached[aCpu];
6200 }
6201
6202 return S_OK;
6203}
6204
6205HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6206{
6207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 Utf8Str log = i_getLogFilename(aIdx);
6210 if (!RTFileExists(log.c_str()))
6211 log.setNull();
6212 aFilename = log;
6213
6214 return S_OK;
6215}
6216
6217HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6218{
6219 if (aSize < 0)
6220 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6221
6222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6223
6224 HRESULT hrc = S_OK;
6225 Utf8Str log = i_getLogFilename(aIdx);
6226
6227 /* do not unnecessarily hold the lock while doing something which does
6228 * not need the lock and potentially takes a long time. */
6229 alock.release();
6230
6231 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6232 * keeps the SOAP reply size under 1M for the webservice (we're using
6233 * base64 encoded strings for binary data for years now, avoiding the
6234 * expansion of each byte array element to approx. 25 bytes of XML. */
6235 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6236 aData.resize(cbData);
6237
6238 int vrc = VINF_SUCCESS;
6239 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6240
6241#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6242 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6243 {
6244 PCVBOXCRYPTOIF pCryptoIf = NULL;
6245 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6246 if (SUCCEEDED(hrc))
6247 {
6248 alock.acquire();
6249
6250 SecretKey *pKey = NULL;
6251 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6252 alock.release();
6253
6254 if (RT_SUCCESS(vrc))
6255 {
6256 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6257 if (RT_SUCCESS(vrc))
6258 {
6259 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6260 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6261 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6262 if (RT_SUCCESS(vrc))
6263 {
6264 RTVfsIoStrmRelease(hVfsIosLog);
6265 hVfsIosLog = hVfsIosLogDec;
6266 }
6267 }
6268
6269 pKey->release();
6270 }
6271
6272 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6273 }
6274 }
6275 else
6276 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6277#else
6278 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6279#endif
6280 if (RT_SUCCESS(vrc))
6281 {
6282 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6283 cbData ? &aData.front() : NULL, cbData,
6284 true /*fBlocking*/, &cbData);
6285 if (RT_SUCCESS(vrc))
6286 aData.resize(cbData);
6287 else
6288 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6289
6290 RTVfsIoStrmRelease(hVfsIosLog);
6291 }
6292 else
6293 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6294
6295 if (FAILED(hrc))
6296 aData.resize(0);
6297
6298 return hrc;
6299}
6300
6301
6302/**
6303 * Currently this method doesn't attach device to the running VM,
6304 * just makes sure it's plugged on next VM start.
6305 */
6306HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6307{
6308 // lock scope
6309 {
6310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6311
6312 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6313 if (FAILED(hrc)) return hrc;
6314
6315 ChipsetType_T aChipset = ChipsetType_PIIX3;
6316 hrc = mPlatform->COMGETTER(ChipsetType)(&aChipset);
6317 if (FAILED(hrc)) return hrc;
6318
6319 if (aChipset != ChipsetType_ICH9) /** @todo BUGBUG ASSUMES x86! */
6320 {
6321 return setError(E_INVALIDARG,
6322 tr("Host PCI attachment only supported with ICH9 chipset"));
6323 }
6324
6325 // check if device with this host PCI address already attached
6326 for (HWData::PCIDeviceAssignmentList::const_iterator
6327 it = mHWData->mPCIDeviceAssignments.begin();
6328 it != mHWData->mPCIDeviceAssignments.end();
6329 ++it)
6330 {
6331 LONG iHostAddress = -1;
6332 ComPtr<PCIDeviceAttachment> pAttach;
6333 pAttach = *it;
6334 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6335 if (iHostAddress == aHostAddress)
6336 return setError(E_INVALIDARG,
6337 tr("Device with host PCI address already attached to this VM"));
6338 }
6339
6340 ComObjPtr<PCIDeviceAttachment> pda;
6341 char name[32];
6342
6343 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6344 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6345 pda.createObject();
6346 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6347 i_setModified(IsModified_MachineData);
6348 mHWData.backup();
6349 mHWData->mPCIDeviceAssignments.push_back(pda);
6350 }
6351
6352 return S_OK;
6353}
6354
6355/**
6356 * Currently this method doesn't detach device from the running VM,
6357 * just makes sure it's not plugged on next VM start.
6358 */
6359HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6360{
6361 ComObjPtr<PCIDeviceAttachment> pAttach;
6362 bool fRemoved = false;
6363 HRESULT hrc;
6364
6365 // lock scope
6366 {
6367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6368
6369 hrc = i_checkStateDependency(MutableStateDep);
6370 if (FAILED(hrc)) return hrc;
6371
6372 for (HWData::PCIDeviceAssignmentList::const_iterator
6373 it = mHWData->mPCIDeviceAssignments.begin();
6374 it != mHWData->mPCIDeviceAssignments.end();
6375 ++it)
6376 {
6377 LONG iHostAddress = -1;
6378 pAttach = *it;
6379 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6380 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6381 {
6382 i_setModified(IsModified_MachineData);
6383 mHWData.backup();
6384 mHWData->mPCIDeviceAssignments.remove(pAttach);
6385 fRemoved = true;
6386 break;
6387 }
6388 }
6389 }
6390
6391
6392 /* Fire event outside of the lock */
6393 if (fRemoved)
6394 {
6395 Assert(!pAttach.isNull());
6396 ComPtr<IEventSource> es;
6397 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
6398 Assert(SUCCEEDED(hrc));
6399 Bstr mid;
6400 hrc = this->COMGETTER(Id)(mid.asOutParam());
6401 Assert(SUCCEEDED(hrc));
6402 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6403 }
6404
6405 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6406 tr("No host PCI device %08x attached"),
6407 aHostAddress
6408 );
6409}
6410
6411HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6412{
6413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6416 size_t i = 0;
6417 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6418 it = mHWData->mPCIDeviceAssignments.begin();
6419 it != mHWData->mPCIDeviceAssignments.end();
6420 ++it, ++i)
6421 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6422
6423 return S_OK;
6424}
6425
6426HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6427{
6428 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6429
6430 return S_OK;
6431}
6432
6433HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6434{
6435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6438
6439 return S_OK;
6440}
6441
6442HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6443{
6444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6445 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6446 if (SUCCEEDED(hrc))
6447 {
6448 hrc = mHWData.backupEx();
6449 if (SUCCEEDED(hrc))
6450 {
6451 i_setModified(IsModified_MachineData);
6452 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6453 }
6454 }
6455 return hrc;
6456}
6457
6458HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6459{
6460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6461 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6462 return S_OK;
6463}
6464
6465HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6466{
6467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6468 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6469 if (SUCCEEDED(hrc))
6470 {
6471 hrc = mHWData.backupEx();
6472 if (SUCCEEDED(hrc))
6473 {
6474 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6475 if (SUCCEEDED(hrc))
6476 i_setModified(IsModified_MachineData);
6477 }
6478 }
6479 return hrc;
6480}
6481
6482HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6483{
6484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6485
6486 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6487
6488 return S_OK;
6489}
6490
6491HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6492{
6493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6494 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6495 if (SUCCEEDED(hrc))
6496 {
6497 hrc = mHWData.backupEx();
6498 if (SUCCEEDED(hrc))
6499 {
6500 i_setModified(IsModified_MachineData);
6501 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6502 }
6503 }
6504 return hrc;
6505}
6506
6507HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6508{
6509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6512
6513 return S_OK;
6514}
6515
6516HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6517{
6518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6519
6520 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6521 if ( SUCCEEDED(hrc)
6522 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6523 {
6524 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6525 int vrc;
6526
6527 if (aAutostartEnabled)
6528 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6529 else
6530 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6531
6532 if (RT_SUCCESS(vrc))
6533 {
6534 hrc = mHWData.backupEx();
6535 if (SUCCEEDED(hrc))
6536 {
6537 i_setModified(IsModified_MachineData);
6538 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6539 }
6540 }
6541 else if (vrc == VERR_NOT_SUPPORTED)
6542 hrc = setError(VBOX_E_NOT_SUPPORTED,
6543 tr("The VM autostart feature is not supported on this platform"));
6544 else if (vrc == VERR_PATH_NOT_FOUND)
6545 hrc = setError(E_FAIL,
6546 tr("The path to the autostart database is not set"));
6547 else
6548 hrc = setError(E_UNEXPECTED,
6549 aAutostartEnabled ?
6550 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6551 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6552 mUserData->s.strName.c_str(), vrc);
6553 }
6554 return hrc;
6555}
6556
6557HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6558{
6559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6560
6561 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6562
6563 return S_OK;
6564}
6565
6566HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6567{
6568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6569 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6570 if (SUCCEEDED(hrc))
6571 {
6572 hrc = mHWData.backupEx();
6573 if (SUCCEEDED(hrc))
6574 {
6575 i_setModified(IsModified_MachineData);
6576 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6577 }
6578 }
6579 return hrc;
6580}
6581
6582HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6583{
6584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6587
6588 return S_OK;
6589}
6590
6591HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6592{
6593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6594 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6595 if ( SUCCEEDED(hrc)
6596 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6597 {
6598 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6599 int vrc;
6600
6601 if (aAutostopType != AutostopType_Disabled)
6602 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6603 else
6604 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6605
6606 if (RT_SUCCESS(vrc))
6607 {
6608 hrc = mHWData.backupEx();
6609 if (SUCCEEDED(hrc))
6610 {
6611 i_setModified(IsModified_MachineData);
6612 mHWData->mAutostart.enmAutostopType = aAutostopType;
6613 }
6614 }
6615 else if (vrc == VERR_NOT_SUPPORTED)
6616 hrc = setError(VBOX_E_NOT_SUPPORTED,
6617 tr("The VM autostop feature is not supported on this platform"));
6618 else if (vrc == VERR_PATH_NOT_FOUND)
6619 hrc = setError(E_FAIL,
6620 tr("The path to the autostart database is not set"));
6621 else
6622 hrc = setError(E_UNEXPECTED,
6623 aAutostopType != AutostopType_Disabled ?
6624 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6625 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6626 mUserData->s.strName.c_str(), vrc);
6627 }
6628 return hrc;
6629}
6630
6631HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6632{
6633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 aDefaultFrontend = mHWData->mDefaultFrontend;
6636
6637 return S_OK;
6638}
6639
6640HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6641{
6642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6643 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6644 if (SUCCEEDED(hrc))
6645 {
6646 hrc = mHWData.backupEx();
6647 if (SUCCEEDED(hrc))
6648 {
6649 i_setModified(IsModified_MachineData);
6650 mHWData->mDefaultFrontend = aDefaultFrontend;
6651 }
6652 }
6653 return hrc;
6654}
6655
6656HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6657{
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659 size_t cbIcon = mUserData->s.ovIcon.size();
6660 aIcon.resize(cbIcon);
6661 if (cbIcon)
6662 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6663 return S_OK;
6664}
6665
6666HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6667{
6668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6669 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6670 if (SUCCEEDED(hrc))
6671 {
6672 i_setModified(IsModified_MachineData);
6673 mUserData.backup();
6674 size_t cbIcon = aIcon.size();
6675 mUserData->s.ovIcon.resize(cbIcon);
6676 if (cbIcon)
6677 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6678 }
6679 return hrc;
6680}
6681
6682HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6683{
6684#ifdef VBOX_WITH_USB
6685 *aUSBProxyAvailable = true;
6686#else
6687 *aUSBProxyAvailable = false;
6688#endif
6689 return S_OK;
6690}
6691
6692HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6693{
6694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6695
6696 *aVMProcessPriority = mUserData->s.enmVMPriority;
6697
6698 return S_OK;
6699}
6700
6701HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6702{
6703 RT_NOREF(aVMProcessPriority);
6704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6705 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6706 if (SUCCEEDED(hrc))
6707 {
6708 hrc = mUserData.backupEx();
6709 if (SUCCEEDED(hrc))
6710 {
6711 i_setModified(IsModified_MachineData);
6712 mUserData->s.enmVMPriority = aVMProcessPriority;
6713 }
6714 }
6715 alock.release();
6716 if (SUCCEEDED(hrc))
6717 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6718 return hrc;
6719}
6720
6721HRESULT Machine::getVMExecutionEngine(VMExecutionEngine_T *aVMExecutionEngine)
6722{
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 *aVMExecutionEngine = mUserData->s.enmExecEngine;
6726
6727 return S_OK;
6728}
6729
6730HRESULT Machine::setVMExecutionEngine(VMExecutionEngine_T aVMExecutionEngine)
6731{
6732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6733 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6734 if (SUCCEEDED(hrc))
6735 {
6736 hrc = mUserData.backupEx();
6737 if (SUCCEEDED(hrc))
6738 {
6739 i_setModified(IsModified_MachineData);
6740 mUserData->s.enmExecEngine = aVMExecutionEngine;
6741 }
6742 }
6743 return hrc;
6744}
6745
6746HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6747 ComPtr<IProgress> &aProgress)
6748{
6749 ComObjPtr<Progress> pP;
6750 Progress *ppP = pP;
6751 IProgress *iP = static_cast<IProgress *>(ppP);
6752 IProgress **pProgress = &iP;
6753
6754 IMachine *pTarget = aTarget;
6755
6756 /* Convert the options. */
6757 RTCList<CloneOptions_T> optList;
6758 if (aOptions.size())
6759 for (size_t i = 0; i < aOptions.size(); ++i)
6760 optList.append(aOptions[i]);
6761
6762 if (optList.contains(CloneOptions_Link))
6763 {
6764 if (!i_isSnapshotMachine())
6765 return setError(E_INVALIDARG,
6766 tr("Linked clone can only be created from a snapshot"));
6767 if (aMode != CloneMode_MachineState)
6768 return setError(E_INVALIDARG,
6769 tr("Linked clone can only be created for a single machine state"));
6770 }
6771 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6772
6773 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6774
6775 HRESULT hrc = pWorker->start(pProgress);
6776
6777 pP = static_cast<Progress *>(*pProgress);
6778 pP.queryInterfaceTo(aProgress.asOutParam());
6779
6780 return hrc;
6781
6782}
6783
6784HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6785 const com::Utf8Str &aType,
6786 ComPtr<IProgress> &aProgress)
6787{
6788 LogFlowThisFuncEnter();
6789
6790 ComObjPtr<Progress> ptrProgress;
6791 HRESULT hrc = ptrProgress.createObject();
6792 if (SUCCEEDED(hrc))
6793 {
6794 com::Utf8Str strDefaultPath;
6795 if (aTargetPath.isEmpty())
6796 i_calculateFullPath(".", strDefaultPath);
6797
6798 /* Initialize our worker task */
6799 MachineMoveVM *pTask = NULL;
6800 try
6801 {
6802 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
6803 }
6804 catch (std::bad_alloc &)
6805 {
6806 return E_OUTOFMEMORY;
6807 }
6808
6809 hrc = pTask->init();//no exceptions are thrown
6810
6811 if (SUCCEEDED(hrc))
6812 {
6813 hrc = pTask->createThread();
6814 pTask = NULL; /* Consumed by createThread(). */
6815 if (SUCCEEDED(hrc))
6816 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6817 else
6818 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6819 }
6820 else
6821 delete pTask;
6822 }
6823
6824 LogFlowThisFuncLeave();
6825 return hrc;
6826
6827}
6828
6829HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6830{
6831 NOREF(aProgress);
6832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6833
6834 // This check should always fail.
6835 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6836 if (FAILED(hrc)) return hrc;
6837
6838 AssertFailedReturn(E_NOTIMPL);
6839}
6840
6841HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6842{
6843 NOREF(aSavedStateFile);
6844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6845
6846 // This check should always fail.
6847 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6848 if (FAILED(hrc)) return hrc;
6849
6850 AssertFailedReturn(E_NOTIMPL);
6851}
6852
6853HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6854{
6855 NOREF(aFRemoveFile);
6856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6857
6858 // This check should always fail.
6859 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6860 if (FAILED(hrc)) return hrc;
6861
6862 AssertFailedReturn(E_NOTIMPL);
6863}
6864
6865// public methods for internal purposes
6866/////////////////////////////////////////////////////////////////////////////
6867
6868/**
6869 * Adds the given IsModified_* flag to the dirty flags of the machine.
6870 * This must be called either during i_loadSettings or under the machine write lock.
6871 * @param fl Flag
6872 * @param fAllowStateModification If state modifications are allowed.
6873 */
6874void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6875{
6876 mData->flModifications |= fl;
6877 if (fAllowStateModification && i_isStateModificationAllowed())
6878 mData->mCurrentStateModified = true;
6879}
6880
6881/**
6882 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6883 * care of the write locking.
6884 *
6885 * @param fModification The flag to add.
6886 * @param fAllowStateModification If state modifications are allowed.
6887 */
6888void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6889{
6890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6891 i_setModified(fModification, fAllowStateModification);
6892}
6893
6894/**
6895 * Saves the registry entry of this machine to the given configuration node.
6896 *
6897 * @param data Machine registry data.
6898 *
6899 * @note locks this object for reading.
6900 */
6901HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
6902{
6903 AutoLimitedCaller autoCaller(this);
6904 AssertComRCReturnRC(autoCaller.hrc());
6905
6906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6907
6908 data.uuid = mData->mUuid;
6909 data.strSettingsFile = mData->m_strConfigFile;
6910
6911 return S_OK;
6912}
6913
6914/**
6915 * Calculates the absolute path of the given path taking the directory of the
6916 * machine settings file as the current directory.
6917 *
6918 * @param strPath Path to calculate the absolute path for.
6919 * @param aResult Where to put the result (used only on success, can be the
6920 * same Utf8Str instance as passed in @a aPath).
6921 * @return IPRT result.
6922 *
6923 * @note Locks this object for reading.
6924 */
6925int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6926{
6927 AutoCaller autoCaller(this);
6928 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
6929
6930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6931
6932 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6933
6934 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6935
6936 strSettingsDir.stripFilename();
6937 char szFolder[RTPATH_MAX];
6938 size_t cbFolder = sizeof(szFolder);
6939 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
6940 if (RT_SUCCESS(vrc))
6941 aResult = szFolder;
6942
6943 return vrc;
6944}
6945
6946/**
6947 * Copies strSource to strTarget, making it relative to the machine folder
6948 * if it is a subdirectory thereof, or simply copying it otherwise.
6949 *
6950 * @param strSource Path to evaluate and copy.
6951 * @param strTarget Buffer to receive target path.
6952 *
6953 * @note Locks this object for reading.
6954 */
6955void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
6956 Utf8Str &strTarget)
6957{
6958 AutoCaller autoCaller(this);
6959 AssertComRCReturn(autoCaller.hrc(), (void)0);
6960
6961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6962
6963 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6964 // use strTarget as a temporary buffer to hold the machine settings dir
6965 strTarget = mData->m_strConfigFileFull;
6966 strTarget.stripFilename();
6967 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6968 {
6969 // is relative: then append what's left
6970 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6971 // for empty paths (only possible for subdirs) use "." to avoid
6972 // triggering default settings for not present config attributes.
6973 if (strTarget.isEmpty())
6974 strTarget = ".";
6975 }
6976 else
6977 // is not relative: then overwrite
6978 strTarget = strSource;
6979}
6980
6981/**
6982 * Returns the full path to the machine's log folder in the
6983 * \a aLogFolder argument.
6984 */
6985void Machine::i_getLogFolder(Utf8Str &aLogFolder)
6986{
6987 AutoCaller autoCaller(this);
6988 AssertComRCReturnVoid(autoCaller.hrc());
6989
6990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6991
6992 char szTmp[RTPATH_MAX];
6993 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6994 if (RT_SUCCESS(vrc))
6995 {
6996 if (szTmp[0] && !mUserData.isNull())
6997 {
6998 char szTmp2[RTPATH_MAX];
6999 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7000 if (RT_SUCCESS(vrc))
7001 aLogFolder.printf("%s%c%s",
7002 szTmp2,
7003 RTPATH_DELIMITER,
7004 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7005 }
7006 else
7007 vrc = VERR_PATH_IS_RELATIVE;
7008 }
7009
7010 if (RT_FAILURE(vrc))
7011 {
7012 // fallback if VBOX_USER_LOGHOME is not set or invalid
7013 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7014 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7015 aLogFolder.append(RTPATH_DELIMITER);
7016 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7017 }
7018}
7019
7020/**
7021 * Returns the full path to the machine's log file for an given index.
7022 */
7023Utf8Str Machine::i_getLogFilename(ULONG idx)
7024{
7025 Utf8Str logFolder;
7026 getLogFolder(logFolder);
7027 Assert(logFolder.length());
7028
7029 Utf8Str log;
7030 if (idx == 0)
7031 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7032#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7033 else if (idx == 1)
7034 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7035 else
7036 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7037#else
7038 else
7039 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7040#endif
7041 return log;
7042}
7043
7044/**
7045 * Returns the full path to the machine's hardened log file.
7046 */
7047Utf8Str Machine::i_getHardeningLogFilename(void)
7048{
7049 Utf8Str strFilename;
7050 getLogFolder(strFilename);
7051 Assert(strFilename.length());
7052 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7053 return strFilename;
7054}
7055
7056/**
7057 * Returns the default NVRAM filename based on the location of the VM config.
7058 * Note that this is a relative path.
7059 */
7060Utf8Str Machine::i_getDefaultNVRAMFilename()
7061{
7062 AutoCaller autoCaller(this);
7063 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7064
7065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7066
7067 if (i_isSnapshotMachine())
7068 return Utf8Str::Empty;
7069
7070 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7071 strNVRAMFilePath.stripPath();
7072 strNVRAMFilePath.stripSuffix();
7073 strNVRAMFilePath += ".nvram";
7074
7075 return strNVRAMFilePath;
7076}
7077
7078/**
7079 * Returns the NVRAM filename for a new snapshot. This intentionally works
7080 * similarly to the saved state file naming. Note that this is usually
7081 * a relative path, unless the snapshot folder is absolute.
7082 */
7083Utf8Str Machine::i_getSnapshotNVRAMFilename()
7084{
7085 AutoCaller autoCaller(this);
7086 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7087
7088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 RTTIMESPEC ts;
7091 RTTimeNow(&ts);
7092 RTTIME time;
7093 RTTimeExplode(&time, &ts);
7094
7095 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7096 strNVRAMFilePath += RTPATH_DELIMITER;
7097 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7098 time.i32Year, time.u8Month, time.u8MonthDay,
7099 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7100
7101 return strNVRAMFilePath;
7102}
7103
7104/**
7105 * Returns the version of the settings file.
7106 */
7107SettingsVersion_T Machine::i_getSettingsVersion(void)
7108{
7109 AutoCaller autoCaller(this);
7110 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7111
7112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7113
7114 return mData->pMachineConfigFile->getSettingsVersion();
7115}
7116
7117/**
7118 * Composes a unique saved state filename based on the current system time. The filename is
7119 * granular to the second so this will work so long as no more than one snapshot is taken on
7120 * a machine per second.
7121 *
7122 * Before version 4.1, we used this formula for saved state files:
7123 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7124 * which no longer works because saved state files can now be shared between the saved state of the
7125 * "saved" machine and an online snapshot, and the following would cause problems:
7126 * 1) save machine
7127 * 2) create online snapshot from that machine state --> reusing saved state file
7128 * 3) save machine again --> filename would be reused, breaking the online snapshot
7129 *
7130 * So instead we now use a timestamp.
7131 *
7132 * @param strStateFilePath
7133 */
7134
7135void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7136{
7137 AutoCaller autoCaller(this);
7138 AssertComRCReturnVoid(autoCaller.hrc());
7139
7140 {
7141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7142 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7143 }
7144
7145 RTTIMESPEC ts;
7146 RTTimeNow(&ts);
7147 RTTIME time;
7148 RTTimeExplode(&time, &ts);
7149
7150 strStateFilePath += RTPATH_DELIMITER;
7151 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7152 time.i32Year, time.u8Month, time.u8MonthDay,
7153 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7154}
7155
7156/**
7157 * Returns whether at least one USB controller is present for the VM.
7158 */
7159bool Machine::i_isUSBControllerPresent()
7160{
7161 AutoCaller autoCaller(this);
7162 AssertComRCReturn(autoCaller.hrc(), false);
7163
7164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7165
7166 return (mUSBControllers->size() > 0);
7167}
7168
7169
7170/**
7171 * @note Locks this object for writing, calls the client process
7172 * (inside the lock).
7173 */
7174HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7175 const Utf8Str &strFrontend,
7176 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7177 ProgressProxy *aProgress)
7178{
7179 LogFlowThisFuncEnter();
7180
7181 AssertReturn(aControl, E_FAIL);
7182 AssertReturn(aProgress, E_FAIL);
7183 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7184
7185 AutoCaller autoCaller(this);
7186 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7187
7188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7189
7190 if (!mData->mRegistered)
7191 return setError(E_UNEXPECTED,
7192 tr("The machine '%s' is not registered"),
7193 mUserData->s.strName.c_str());
7194
7195 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7196
7197 /* The process started when launching a VM with separate UI/VM processes is always
7198 * the UI process, i.e. needs special handling as it won't claim the session. */
7199 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7200
7201 if (fSeparate)
7202 {
7203 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7204 return setError(VBOX_E_INVALID_OBJECT_STATE,
7205 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7206 mUserData->s.strName.c_str());
7207 }
7208 else
7209 {
7210 if ( mData->mSession.mState == SessionState_Locked
7211 || mData->mSession.mState == SessionState_Spawning
7212 || mData->mSession.mState == SessionState_Unlocking)
7213 return setError(VBOX_E_INVALID_OBJECT_STATE,
7214 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7215 mUserData->s.strName.c_str());
7216
7217 /* may not be busy */
7218 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7219 }
7220
7221 /* Hardening logging */
7222#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7223 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7224 {
7225 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7226 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7227 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7228 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7229 {
7230 Utf8Str strStartupLogDir = strHardeningLogFile;
7231 strStartupLogDir.stripFilename();
7232 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7233 file without stripping the file. */
7234 }
7235 strSupHardeningLogArg.append(strHardeningLogFile);
7236
7237 /* Remove legacy log filename to avoid confusion. */
7238 Utf8Str strOldStartupLogFile;
7239 getLogFolder(strOldStartupLogFile);
7240 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7241 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7242 }
7243#else
7244 Utf8Str strSupHardeningLogArg;
7245#endif
7246
7247 Utf8Str strAppOverride;
7248#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7249 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7250#endif
7251
7252 bool fUseVBoxSDS = false;
7253 Utf8Str strCanonicalName;
7254 if (false)
7255 { }
7256#ifdef VBOX_WITH_QTGUI
7257 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7258 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7259 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7260 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7261 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7262 {
7263 strCanonicalName = "GUI/Qt";
7264 fUseVBoxSDS = true;
7265 }
7266#endif
7267#ifdef VBOX_WITH_VBOXSDL
7268 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7269 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7270 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7271 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7272 {
7273 strCanonicalName = "GUI/SDL";
7274 fUseVBoxSDS = true;
7275 }
7276#endif
7277#ifdef VBOX_WITH_HEADLESS
7278 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7279 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7280 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7281 {
7282 strCanonicalName = "headless";
7283 }
7284#endif
7285 else
7286 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7287
7288 Utf8Str idStr = mData->mUuid.toString();
7289 Utf8Str const &strMachineName = mUserData->s.strName;
7290 RTPROCESS pid = NIL_RTPROCESS;
7291
7292#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7293 RT_NOREF(fUseVBoxSDS);
7294#else
7295 DWORD idCallerSession = ~(DWORD)0;
7296 if (fUseVBoxSDS)
7297 {
7298 /*
7299 * The VBoxSDS should be used for process launching the VM with
7300 * GUI only if the caller and the VBoxSDS are in different Windows
7301 * sessions and the caller in the interactive one.
7302 */
7303 fUseVBoxSDS = false;
7304
7305 /* Get windows session of the current process. The process token used
7306 due to several reasons:
7307 1. The token is absent for the current thread except someone set it
7308 for us.
7309 2. Needs to get the id of the session where the process is started.
7310 We only need to do this once, though. */
7311 static DWORD s_idCurrentSession = ~(DWORD)0;
7312 DWORD idCurrentSession = s_idCurrentSession;
7313 if (idCurrentSession == ~(DWORD)0)
7314 {
7315 HANDLE hCurrentProcessToken = NULL;
7316 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7317 {
7318 DWORD cbIgn = 0;
7319 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7320 s_idCurrentSession = idCurrentSession;
7321 else
7322 {
7323 idCurrentSession = ~(DWORD)0;
7324 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7325 }
7326 CloseHandle(hCurrentProcessToken);
7327 }
7328 else
7329 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7330 }
7331
7332 /* get the caller's session */
7333 HRESULT hrc = CoImpersonateClient();
7334 if (SUCCEEDED(hrc))
7335 {
7336 HANDLE hCallerThreadToken;
7337 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7338 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7339 &hCallerThreadToken))
7340 {
7341 SetLastError(NO_ERROR);
7342 DWORD cbIgn = 0;
7343 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7344 {
7345 /* Only need to use SDS if the session ID differs: */
7346 if (idCurrentSession != idCallerSession)
7347 {
7348 fUseVBoxSDS = false;
7349
7350 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7351 DWORD cbTokenGroups = 0;
7352 PTOKEN_GROUPS pTokenGroups = NULL;
7353 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7354 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7355 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7356 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7357 {
7358 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7359 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7360 PSID pInteractiveSid = NULL;
7361 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7362 {
7363 /* Iterate over the groups looking for the interactive SID: */
7364 fUseVBoxSDS = false;
7365 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7366 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7367 {
7368 fUseVBoxSDS = true;
7369 break;
7370 }
7371 FreeSid(pInteractiveSid);
7372 }
7373 }
7374 else
7375 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7376 RTMemTmpFree(pTokenGroups);
7377 }
7378 }
7379 else
7380 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7381 CloseHandle(hCallerThreadToken);
7382 }
7383 else
7384 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7385 CoRevertToSelf();
7386 }
7387 else
7388 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7389 }
7390 if (fUseVBoxSDS)
7391 {
7392 /* connect to VBoxSDS */
7393 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7394 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7395 if (FAILED(hrc))
7396 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7397 strMachineName.c_str());
7398
7399 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7400 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7401 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7402 service to access the files. */
7403 hrc = CoSetProxyBlanket(pVBoxSDS,
7404 RPC_C_AUTHN_DEFAULT,
7405 RPC_C_AUTHZ_DEFAULT,
7406 COLE_DEFAULT_PRINCIPAL,
7407 RPC_C_AUTHN_LEVEL_DEFAULT,
7408 RPC_C_IMP_LEVEL_IMPERSONATE,
7409 NULL,
7410 EOAC_DEFAULT);
7411 if (FAILED(hrc))
7412 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7413
7414 size_t const cEnvVars = aEnvironmentChanges.size();
7415 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7416 for (size_t i = 0; i < cEnvVars; i++)
7417 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7418
7419 ULONG uPid = 0;
7420 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7421 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7422 idCallerSession, &uPid);
7423 if (FAILED(hrc))
7424 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7425 pid = (RTPROCESS)uPid;
7426 }
7427 else
7428#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7429 {
7430 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7431 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7432 if (RT_FAILURE(vrc))
7433 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7434 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7435 }
7436
7437 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7438 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7439
7440 if (!fSeparate)
7441 {
7442 /*
7443 * Note that we don't release the lock here before calling the client,
7444 * because it doesn't need to call us back if called with a NULL argument.
7445 * Releasing the lock here is dangerous because we didn't prepare the
7446 * launch data yet, but the client we've just started may happen to be
7447 * too fast and call LockMachine() that will fail (because of PID, etc.),
7448 * so that the Machine will never get out of the Spawning session state.
7449 */
7450
7451 /* inform the session that it will be a remote one */
7452 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7453#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7454 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7455#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7456 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7457#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7458 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
7459
7460 if (FAILED(hrc))
7461 {
7462 /* restore the session state */
7463 mData->mSession.mState = SessionState_Unlocked;
7464 alock.release();
7465 mParent->i_addProcessToReap(pid);
7466 /* The failure may occur w/o any error info (from RPC), so provide one */
7467 return setError(VBOX_E_VM_ERROR,
7468 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
7469 }
7470
7471 /* attach launch data to the machine */
7472 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7473 mData->mSession.mRemoteControls.push_back(aControl);
7474 mData->mSession.mProgress = aProgress;
7475 mData->mSession.mPID = pid;
7476 mData->mSession.mState = SessionState_Spawning;
7477 Assert(strCanonicalName.isNotEmpty());
7478 mData->mSession.mName = strCanonicalName;
7479 }
7480 else
7481 {
7482 /* For separate UI process we declare the launch as completed instantly, as the
7483 * actual headless VM start may or may not come. No point in remembering anything
7484 * yet, as what matters for us is when the headless VM gets started. */
7485 aProgress->i_notifyComplete(S_OK);
7486 }
7487
7488 alock.release();
7489 mParent->i_addProcessToReap(pid);
7490
7491 LogFlowThisFuncLeave();
7492 return S_OK;
7493}
7494
7495/**
7496 * Returns @c true if the given session machine instance has an open direct
7497 * session (and optionally also for direct sessions which are closing) and
7498 * returns the session control machine instance if so.
7499 *
7500 * Note that when the method returns @c false, the arguments remain unchanged.
7501 *
7502 * @param aMachine Session machine object.
7503 * @param aControl Direct session control object (optional).
7504 * @param aRequireVM If true then only allow VM sessions.
7505 * @param aAllowClosing If true then additionally a session which is currently
7506 * being closed will also be allowed.
7507 *
7508 * @note locks this object for reading.
7509 */
7510bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7511 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7512 bool aRequireVM /*= false*/,
7513 bool aAllowClosing /*= false*/)
7514{
7515 AutoLimitedCaller autoCaller(this);
7516 AssertComRCReturn(autoCaller.hrc(), false);
7517
7518 /* just return false for inaccessible machines */
7519 if (getObjectState().getState() != ObjectState::Ready)
7520 return false;
7521
7522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7523
7524 if ( ( mData->mSession.mState == SessionState_Locked
7525 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7526 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7527 )
7528 {
7529 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7530
7531 aMachine = mData->mSession.mMachine;
7532
7533 if (aControl != NULL)
7534 *aControl = mData->mSession.mDirectControl;
7535
7536 return true;
7537 }
7538
7539 return false;
7540}
7541
7542/**
7543 * Returns @c true if the given machine has an spawning direct session.
7544 *
7545 * @note locks this object for reading.
7546 */
7547bool Machine::i_isSessionSpawning()
7548{
7549 AutoLimitedCaller autoCaller(this);
7550 AssertComRCReturn(autoCaller.hrc(), false);
7551
7552 /* just return false for inaccessible machines */
7553 if (getObjectState().getState() != ObjectState::Ready)
7554 return false;
7555
7556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7557
7558 if (mData->mSession.mState == SessionState_Spawning)
7559 return true;
7560
7561 return false;
7562}
7563
7564/**
7565 * Called from the client watcher thread to check for unexpected client process
7566 * death during Session_Spawning state (e.g. before it successfully opened a
7567 * direct session).
7568 *
7569 * On Win32 and on OS/2, this method is called only when we've got the
7570 * direct client's process termination notification, so it always returns @c
7571 * true.
7572 *
7573 * On other platforms, this method returns @c true if the client process is
7574 * terminated and @c false if it's still alive.
7575 *
7576 * @note Locks this object for writing.
7577 */
7578bool Machine::i_checkForSpawnFailure()
7579{
7580 AutoCaller autoCaller(this);
7581 if (!autoCaller.isOk())
7582 {
7583 /* nothing to do */
7584 LogFlowThisFunc(("Already uninitialized!\n"));
7585 return true;
7586 }
7587
7588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7589
7590 if (mData->mSession.mState != SessionState_Spawning)
7591 {
7592 /* nothing to do */
7593 LogFlowThisFunc(("Not spawning any more!\n"));
7594 return true;
7595 }
7596
7597 /* PID not yet initialized, skip check. */
7598 if (mData->mSession.mPID == NIL_RTPROCESS)
7599 return false;
7600
7601 HRESULT hrc = S_OK;
7602 RTPROCSTATUS status;
7603 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7604 if (vrc != VERR_PROCESS_RUNNING)
7605 {
7606 Utf8Str strExtraInfo;
7607
7608#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7609 /* If the startup logfile exists and is of non-zero length, tell the
7610 user to look there for more details to encourage them to attach it
7611 when reporting startup issues. */
7612 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7613 uint64_t cbStartupLogFile = 0;
7614 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7615 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7616 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7617#endif
7618
7619 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7620 hrc = setError(E_FAIL,
7621 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7622 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7623 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7624 hrc = setError(E_FAIL,
7625 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7626 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7627 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7628 hrc = setError(E_FAIL,
7629 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7630 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7631 else
7632 hrc = setErrorBoth(E_FAIL, vrc,
7633 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7634 i_getName().c_str(), vrc, strExtraInfo.c_str());
7635 }
7636
7637 if (FAILED(hrc))
7638 {
7639 /* Close the remote session, remove the remote control from the list
7640 * and reset session state to Closed (@note keep the code in sync with
7641 * the relevant part in LockMachine()). */
7642
7643 Assert(mData->mSession.mRemoteControls.size() == 1);
7644 if (mData->mSession.mRemoteControls.size() == 1)
7645 {
7646 ErrorInfoKeeper eik;
7647 mData->mSession.mRemoteControls.front()->Uninitialize();
7648 }
7649
7650 mData->mSession.mRemoteControls.clear();
7651 mData->mSession.mState = SessionState_Unlocked;
7652
7653 /* finalize the progress after setting the state */
7654 if (!mData->mSession.mProgress.isNull())
7655 {
7656 mData->mSession.mProgress->notifyComplete(hrc);
7657 mData->mSession.mProgress.setNull();
7658 }
7659
7660 mData->mSession.mPID = NIL_RTPROCESS;
7661
7662 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7663 return true;
7664 }
7665
7666 return false;
7667}
7668
7669/**
7670 * Checks whether the machine can be registered. If so, commits and saves
7671 * all settings.
7672 *
7673 * @note Must be called from mParent's write lock. Locks this object and
7674 * children for writing.
7675 */
7676HRESULT Machine::i_prepareRegister()
7677{
7678 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7679
7680 AutoLimitedCaller autoCaller(this);
7681 AssertComRCReturnRC(autoCaller.hrc());
7682
7683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7684
7685 /* wait for state dependents to drop to zero */
7686 i_ensureNoStateDependencies(alock);
7687
7688 if (!mData->mAccessible)
7689 return setError(VBOX_E_INVALID_OBJECT_STATE,
7690 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7691 mUserData->s.strName.c_str(),
7692 mData->mUuid.toString().c_str());
7693
7694 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7695
7696 if (mData->mRegistered)
7697 return setError(VBOX_E_INVALID_OBJECT_STATE,
7698 tr("The machine '%s' with UUID {%s} is already registered"),
7699 mUserData->s.strName.c_str(),
7700 mData->mUuid.toString().c_str());
7701
7702 HRESULT hrc = S_OK;
7703
7704 // Ensure the settings are saved. If we are going to be registered and
7705 // no config file exists yet, create it by calling i_saveSettings() too.
7706 if ( (mData->flModifications)
7707 || (!mData->pMachineConfigFile->fileExists())
7708 )
7709 {
7710 hrc = i_saveSettings(NULL, alock);
7711 // no need to check whether VirtualBox.xml needs saving too since
7712 // we can't have a machine XML file rename pending
7713 if (FAILED(hrc)) return hrc;
7714 }
7715
7716 /* more config checking goes here */
7717
7718 if (SUCCEEDED(hrc))
7719 {
7720 /* we may have had implicit modifications we want to fix on success */
7721 i_commit();
7722
7723 mData->mRegistered = true;
7724 }
7725 else
7726 {
7727 /* we may have had implicit modifications we want to cancel on failure*/
7728 i_rollback(false /* aNotify */);
7729 }
7730
7731 return hrc;
7732}
7733
7734/**
7735 * Increases the number of objects dependent on the machine state or on the
7736 * registered state. Guarantees that these two states will not change at least
7737 * until #i_releaseStateDependency() is called.
7738 *
7739 * Depending on the @a aDepType value, additional state checks may be made.
7740 * These checks will set extended error info on failure. See
7741 * #i_checkStateDependency() for more info.
7742 *
7743 * If this method returns a failure, the dependency is not added and the caller
7744 * is not allowed to rely on any particular machine state or registration state
7745 * value and may return the failed result code to the upper level.
7746 *
7747 * @param aDepType Dependency type to add.
7748 * @param aState Current machine state (NULL if not interested).
7749 * @param aRegistered Current registered state (NULL if not interested).
7750 *
7751 * @note Locks this object for writing.
7752 */
7753HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7754 MachineState_T *aState /* = NULL */,
7755 BOOL *aRegistered /* = NULL */)
7756{
7757 AutoCaller autoCaller(this);
7758 AssertComRCReturnRC(autoCaller.hrc());
7759
7760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7761
7762 HRESULT hrc = i_checkStateDependency(aDepType);
7763 if (FAILED(hrc)) return hrc;
7764
7765 {
7766 if (mData->mMachineStateChangePending != 0)
7767 {
7768 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7769 * drop to zero so don't add more. It may make sense to wait a bit
7770 * and retry before reporting an error (since the pending state
7771 * transition should be really quick) but let's just assert for
7772 * now to see if it ever happens on practice. */
7773
7774 AssertFailed();
7775
7776 return setError(E_ACCESSDENIED,
7777 tr("Machine state change is in progress. Please retry the operation later."));
7778 }
7779
7780 ++mData->mMachineStateDeps;
7781 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7782 }
7783
7784 if (aState)
7785 *aState = mData->mMachineState;
7786 if (aRegistered)
7787 *aRegistered = mData->mRegistered;
7788
7789 return S_OK;
7790}
7791
7792/**
7793 * Decreases the number of objects dependent on the machine state.
7794 * Must always complete the #i_addStateDependency() call after the state
7795 * dependency is no more necessary.
7796 */
7797void Machine::i_releaseStateDependency()
7798{
7799 AutoCaller autoCaller(this);
7800 AssertComRCReturnVoid(autoCaller.hrc());
7801
7802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7803
7804 /* releaseStateDependency() w/o addStateDependency()? */
7805 AssertReturnVoid(mData->mMachineStateDeps != 0);
7806 -- mData->mMachineStateDeps;
7807
7808 if (mData->mMachineStateDeps == 0)
7809 {
7810 /* inform i_ensureNoStateDependencies() that there are no more deps */
7811 if (mData->mMachineStateChangePending != 0)
7812 {
7813 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7814 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7815 }
7816 }
7817}
7818
7819Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7820{
7821 /* start with nothing found */
7822 Utf8Str strResult("");
7823
7824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7825
7826 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7827 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7828 // found:
7829 strResult = it->second; // source is a Utf8Str
7830
7831 return strResult;
7832}
7833
7834FirmwareType_T Machine::i_getFirmwareType() const
7835{
7836 return mFirmwareSettings->i_getFirmwareType();
7837}
7838
7839// protected methods
7840/////////////////////////////////////////////////////////////////////////////
7841
7842/**
7843 * Performs machine state checks based on the @a aDepType value. If a check
7844 * fails, this method will set extended error info, otherwise it will return
7845 * S_OK. It is supposed, that on failure, the caller will immediately return
7846 * the return value of this method to the upper level.
7847 *
7848 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7849 *
7850 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7851 * current state of this machine object allows to change settings of the
7852 * machine (i.e. the machine is not registered, or registered but not running
7853 * and not saved). It is useful to call this method from Machine setters
7854 * before performing any change.
7855 *
7856 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7857 * as for MutableStateDep except that if the machine is saved, S_OK is also
7858 * returned. This is useful in setters which allow changing machine
7859 * properties when it is in the saved state.
7860 *
7861 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7862 * if the current state of this machine object allows to change runtime
7863 * changeable settings of the machine (i.e. the machine is not registered, or
7864 * registered but either running or not running and not saved). It is useful
7865 * to call this method from Machine setters before performing any changes to
7866 * runtime changeable settings.
7867 *
7868 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7869 * the same as for MutableOrRunningStateDep except that if the machine is
7870 * saved, S_OK is also returned. This is useful in setters which allow
7871 * changing runtime and saved state changeable machine properties.
7872 *
7873 * @param aDepType Dependency type to check.
7874 *
7875 * @note Non Machine based classes should use #i_addStateDependency() and
7876 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7877 * template.
7878 *
7879 * @note This method must be called from under this object's read or write
7880 * lock.
7881 */
7882HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7883{
7884 switch (aDepType)
7885 {
7886 case AnyStateDep:
7887 {
7888 break;
7889 }
7890 case MutableStateDep:
7891 {
7892 if ( mData->mRegistered
7893 && ( !i_isSessionMachine()
7894 || ( mData->mMachineState != MachineState_Aborted
7895 && mData->mMachineState != MachineState_Teleported
7896 && mData->mMachineState != MachineState_PoweredOff
7897 )
7898 )
7899 )
7900 return setError(VBOX_E_INVALID_VM_STATE,
7901 tr("The machine is not mutable (state is %s)"),
7902 Global::stringifyMachineState(mData->mMachineState));
7903 break;
7904 }
7905 case MutableOrSavedStateDep:
7906 {
7907 if ( mData->mRegistered
7908 && ( !i_isSessionMachine()
7909 || ( mData->mMachineState != MachineState_Aborted
7910 && mData->mMachineState != MachineState_Teleported
7911 && mData->mMachineState != MachineState_Saved
7912 && mData->mMachineState != MachineState_AbortedSaved
7913 && mData->mMachineState != MachineState_PoweredOff
7914 )
7915 )
7916 )
7917 return setError(VBOX_E_INVALID_VM_STATE,
7918 tr("The machine is not mutable or saved (state is %s)"),
7919 Global::stringifyMachineState(mData->mMachineState));
7920 break;
7921 }
7922 case MutableOrRunningStateDep:
7923 {
7924 if ( mData->mRegistered
7925 && ( !i_isSessionMachine()
7926 || ( mData->mMachineState != MachineState_Aborted
7927 && mData->mMachineState != MachineState_Teleported
7928 && mData->mMachineState != MachineState_PoweredOff
7929 && !Global::IsOnline(mData->mMachineState)
7930 )
7931 )
7932 )
7933 return setError(VBOX_E_INVALID_VM_STATE,
7934 tr("The machine is not mutable or running (state is %s)"),
7935 Global::stringifyMachineState(mData->mMachineState));
7936 break;
7937 }
7938 case MutableOrSavedOrRunningStateDep:
7939 {
7940 if ( mData->mRegistered
7941 && ( !i_isSessionMachine()
7942 || ( mData->mMachineState != MachineState_Aborted
7943 && mData->mMachineState != MachineState_Teleported
7944 && mData->mMachineState != MachineState_Saved
7945 && mData->mMachineState != MachineState_AbortedSaved
7946 && mData->mMachineState != MachineState_PoweredOff
7947 && !Global::IsOnline(mData->mMachineState)
7948 )
7949 )
7950 )
7951 return setError(VBOX_E_INVALID_VM_STATE,
7952 tr("The machine is not mutable, saved or running (state is %s)"),
7953 Global::stringifyMachineState(mData->mMachineState));
7954 break;
7955 }
7956 }
7957
7958 return S_OK;
7959}
7960
7961/**
7962 * Helper to initialize all associated child objects and allocate data
7963 * structures.
7964 *
7965 * This method must be called as a part of the object's initialization procedure
7966 * (usually done in the #init() method).
7967 *
7968 * @note Must be called only from #init() or from #i_registeredInit().
7969 */
7970HRESULT Machine::initDataAndChildObjects()
7971{
7972 AutoCaller autoCaller(this);
7973 AssertComRCReturnRC(autoCaller.hrc());
7974 AssertReturn( getObjectState().getState() == ObjectState::InInit
7975 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
7976
7977 AssertReturn(!mData->mAccessible, E_FAIL);
7978
7979 /* allocate data structures */
7980 mSSData.allocate();
7981 mUserData.allocate();
7982 mHWData.allocate();
7983 mMediumAttachments.allocate();
7984 mStorageControllers.allocate();
7985 mUSBControllers.allocate();
7986
7987 /* create the platform + platform properties objects for this machine */
7988 HRESULT hrc = unconst(mPlatform).createObject();
7989 ComAssertComRCRetRC(hrc);
7990 hrc = mPlatform->init(this);
7991 ComAssertComRCRetRC(hrc);
7992 hrc = unconst(mPlatformProperties).createObject();
7993 ComAssertComRCRetRC(hrc);
7994 hrc = mPlatformProperties->init(mParent);
7995 ComAssertComRCRetRC(hrc);
7996
7997 /* initialize mOSTypeId */
7998 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
7999
8000 /* create associated firmware settings object */
8001 unconst(mFirmwareSettings).createObject();
8002 mFirmwareSettings->init(this);
8003
8004 /* create associated recording settings object */
8005 unconst(mRecordingSettings).createObject();
8006 mRecordingSettings->init(this);
8007
8008 /* create associated trusted platform module object */
8009 unconst(mTrustedPlatformModule).createObject();
8010 mTrustedPlatformModule->init(this);
8011
8012 /* create associated NVRAM store object */
8013 unconst(mNvramStore).createObject();
8014 mNvramStore->init(this);
8015
8016 /* create the graphics adapter object (always present) */
8017 unconst(mGraphicsAdapter).createObject();
8018 mGraphicsAdapter->init(this);
8019
8020 /* create an associated VRDE object (default is disabled) */
8021 unconst(mVRDEServer).createObject();
8022 mVRDEServer->init(this);
8023
8024 /* create associated serial port objects */
8025 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8026 {
8027 unconst(mSerialPorts[slot]).createObject();
8028 mSerialPorts[slot]->init(this, slot);
8029 }
8030
8031 /* create associated parallel port objects */
8032 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8033 {
8034 unconst(mParallelPorts[slot]).createObject();
8035 mParallelPorts[slot]->init(this, slot);
8036 }
8037
8038 /* create the audio settings object */
8039 unconst(mAudioSettings).createObject();
8040 mAudioSettings->init(this);
8041
8042 /* create the USB device filters object (always present) */
8043 unconst(mUSBDeviceFilters).createObject();
8044 mUSBDeviceFilters->init(this);
8045
8046 /* create associated network adapter objects */
8047 ChipsetType_T enmChipsetType;
8048 hrc = mPlatform->getChipsetType(&enmChipsetType);
8049 ComAssertComRC(hrc);
8050
8051 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType));
8052 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8053 {
8054 unconst(mNetworkAdapters[slot]).createObject();
8055 mNetworkAdapters[slot]->init(this, slot);
8056 }
8057
8058 /* create the bandwidth control */
8059 unconst(mBandwidthControl).createObject();
8060 mBandwidthControl->init(this);
8061
8062 /* create the guest debug control object */
8063 unconst(mGuestDebugControl).createObject();
8064 mGuestDebugControl->init(this);
8065
8066 return hrc;
8067}
8068
8069/**
8070 * Helper to uninitialize all associated child objects and to free all data
8071 * structures.
8072 *
8073 * This method must be called as a part of the object's uninitialization
8074 * procedure (usually done in the #uninit() method).
8075 *
8076 * @note Must be called only from #uninit() or from #i_registeredInit().
8077 */
8078void Machine::uninitDataAndChildObjects()
8079{
8080 AutoCaller autoCaller(this);
8081 AssertComRCReturnVoid(autoCaller.hrc());
8082 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8083 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8084 || getObjectState().getState() == ObjectState::InUninit
8085 || getObjectState().getState() == ObjectState::Limited);
8086
8087 /* tell all our other child objects we've been uninitialized */
8088 if (mGuestDebugControl)
8089 {
8090 mGuestDebugControl->uninit();
8091 unconst(mGuestDebugControl).setNull();
8092 }
8093
8094 if (mBandwidthControl)
8095 {
8096 mBandwidthControl->uninit();
8097 unconst(mBandwidthControl).setNull();
8098 }
8099
8100 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8101 {
8102 if (mNetworkAdapters[slot])
8103 {
8104 mNetworkAdapters[slot]->uninit();
8105 unconst(mNetworkAdapters[slot]).setNull();
8106 }
8107 }
8108
8109 if (mUSBDeviceFilters)
8110 {
8111 mUSBDeviceFilters->uninit();
8112 unconst(mUSBDeviceFilters).setNull();
8113 }
8114
8115 if (mAudioSettings)
8116 {
8117 mAudioSettings->uninit();
8118 unconst(mAudioSettings).setNull();
8119 }
8120
8121 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8122 {
8123 if (mParallelPorts[slot])
8124 {
8125 mParallelPorts[slot]->uninit();
8126 unconst(mParallelPorts[slot]).setNull();
8127 }
8128 }
8129
8130 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8131 {
8132 if (mSerialPorts[slot])
8133 {
8134 mSerialPorts[slot]->uninit();
8135 unconst(mSerialPorts[slot]).setNull();
8136 }
8137 }
8138
8139 if (mVRDEServer)
8140 {
8141 mVRDEServer->uninit();
8142 unconst(mVRDEServer).setNull();
8143 }
8144
8145 if (mGraphicsAdapter)
8146 {
8147 mGraphicsAdapter->uninit();
8148 unconst(mGraphicsAdapter).setNull();
8149 }
8150
8151 if (mPlatform)
8152 {
8153 mPlatform->uninit();
8154 unconst(mPlatform).setNull();
8155 }
8156
8157 if (mPlatformProperties)
8158 {
8159 mPlatformProperties->uninit();
8160 unconst(mPlatformProperties).setNull();
8161 }
8162
8163 if (mFirmwareSettings)
8164 {
8165 mFirmwareSettings->uninit();
8166 unconst(mFirmwareSettings).setNull();
8167 }
8168
8169 if (mRecordingSettings)
8170 {
8171 mRecordingSettings->uninit();
8172 unconst(mRecordingSettings).setNull();
8173 }
8174
8175 if (mTrustedPlatformModule)
8176 {
8177 mTrustedPlatformModule->uninit();
8178 unconst(mTrustedPlatformModule).setNull();
8179 }
8180
8181 if (mNvramStore)
8182 {
8183 mNvramStore->uninit();
8184 unconst(mNvramStore).setNull();
8185 }
8186
8187 /* Deassociate media (only when a real Machine or a SnapshotMachine
8188 * instance is uninitialized; SessionMachine instances refer to real
8189 * Machine media). This is necessary for a clean re-initialization of
8190 * the VM after successfully re-checking the accessibility state. Note
8191 * that in case of normal Machine or SnapshotMachine uninitialization (as
8192 * a result of unregistering or deleting the snapshot), outdated media
8193 * attachments will already be uninitialized and deleted, so this
8194 * code will not affect them. */
8195 if ( !mMediumAttachments.isNull()
8196 && !i_isSessionMachine()
8197 )
8198 {
8199 for (MediumAttachmentList::const_iterator
8200 it = mMediumAttachments->begin();
8201 it != mMediumAttachments->end();
8202 ++it)
8203 {
8204 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8205 if (pMedium.isNull())
8206 continue;
8207 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8208 AssertComRC(hrc);
8209 }
8210 }
8211
8212 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8213 {
8214 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8215 if (mData->mFirstSnapshot)
8216 {
8217 // Snapshots tree is protected by machine write lock.
8218 // Otherwise we assert in Snapshot::uninit()
8219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8220 mData->mFirstSnapshot->uninit();
8221 mData->mFirstSnapshot.setNull();
8222 }
8223
8224 mData->mCurrentSnapshot.setNull();
8225 }
8226
8227 /* free data structures (the essential mData structure is not freed here
8228 * since it may be still in use) */
8229 mMediumAttachments.free();
8230 mStorageControllers.free();
8231 mUSBControllers.free();
8232 mHWData.free();
8233 mUserData.free();
8234 mSSData.free();
8235}
8236
8237/**
8238 * Returns a pointer to the Machine object for this machine that acts like a
8239 * parent for complex machine data objects such as shared folders, etc.
8240 *
8241 * For primary Machine objects and for SnapshotMachine objects, returns this
8242 * object's pointer itself. For SessionMachine objects, returns the peer
8243 * (primary) machine pointer.
8244 */
8245Machine *Machine::i_getMachine()
8246{
8247 if (i_isSessionMachine())
8248 return (Machine*)mPeer;
8249 return this;
8250}
8251
8252/**
8253 * Makes sure that there are no machine state dependents. If necessary, waits
8254 * for the number of dependents to drop to zero.
8255 *
8256 * Make sure this method is called from under this object's write lock to
8257 * guarantee that no new dependents may be added when this method returns
8258 * control to the caller.
8259 *
8260 * @note Receives a lock to this object for writing. The lock will be released
8261 * while waiting (if necessary).
8262 *
8263 * @warning To be used only in methods that change the machine state!
8264 */
8265void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8266{
8267 AssertReturnVoid(isWriteLockOnCurrentThread());
8268
8269 /* Wait for all state dependents if necessary */
8270 if (mData->mMachineStateDeps != 0)
8271 {
8272 /* lazy semaphore creation */
8273 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8274 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8275
8276 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8277 mData->mMachineStateDeps));
8278
8279 ++mData->mMachineStateChangePending;
8280
8281 /* reset the semaphore before waiting, the last dependent will signal
8282 * it */
8283 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8284
8285 alock.release();
8286
8287 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8288
8289 alock.acquire();
8290
8291 -- mData->mMachineStateChangePending;
8292 }
8293}
8294
8295/**
8296 * Changes the machine state and informs callbacks.
8297 *
8298 * This method is not intended to fail so it either returns S_OK or asserts (and
8299 * returns a failure).
8300 *
8301 * @note Locks this object for writing.
8302 */
8303HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8304{
8305 LogFlowThisFuncEnter();
8306 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8307 Assert(aMachineState != MachineState_Null);
8308
8309 AutoCaller autoCaller(this);
8310 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8311
8312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8313
8314 /* wait for state dependents to drop to zero */
8315 i_ensureNoStateDependencies(alock);
8316
8317 MachineState_T const enmOldState = mData->mMachineState;
8318 if (enmOldState != aMachineState)
8319 {
8320 mData->mMachineState = aMachineState;
8321 RTTimeNow(&mData->mLastStateChange);
8322
8323#ifdef VBOX_WITH_DTRACE_R3_MAIN
8324 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8325#endif
8326 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8327 }
8328
8329 LogFlowThisFuncLeave();
8330 return S_OK;
8331}
8332
8333/**
8334 * Searches for a shared folder with the given logical name
8335 * in the collection of shared folders.
8336 *
8337 * @param aName logical name of the shared folder
8338 * @param aSharedFolder where to return the found object
8339 * @param aSetError whether to set the error info if the folder is
8340 * not found
8341 * @return
8342 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8343 *
8344 * @note
8345 * must be called from under the object's lock!
8346 */
8347HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8348 ComObjPtr<SharedFolder> &aSharedFolder,
8349 bool aSetError /* = false */)
8350{
8351 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8352 for (HWData::SharedFolderList::const_iterator
8353 it = mHWData->mSharedFolders.begin();
8354 it != mHWData->mSharedFolders.end();
8355 ++it)
8356 {
8357 SharedFolder *pSF = *it;
8358 AutoCaller autoCaller(pSF);
8359 if (pSF->i_getName() == aName)
8360 {
8361 aSharedFolder = pSF;
8362 hrc = S_OK;
8363 break;
8364 }
8365 }
8366
8367 if (aSetError && FAILED(hrc))
8368 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8369
8370 return hrc;
8371}
8372
8373/**
8374 * Initializes all machine instance data from the given settings structures
8375 * from XML. The exception is the machine UUID which needs special handling
8376 * depending on the caller's use case, so the caller needs to set that herself.
8377 *
8378 * This gets called in several contexts during machine initialization:
8379 *
8380 * -- When machine XML exists on disk already and needs to be loaded into memory,
8381 * for example, from #i_registeredInit() to load all registered machines on
8382 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8383 * attached to the machine should be part of some media registry already.
8384 *
8385 * -- During OVF import, when a machine config has been constructed from an
8386 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8387 * ensure that the media listed as attachments in the config (which have
8388 * been imported from the OVF) receive the correct registry ID.
8389 *
8390 * -- During VM cloning.
8391 *
8392 * @param config Machine settings from XML.
8393 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8394 * for each attached medium in the config.
8395 * @return
8396 */
8397HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8398 const Guid *puuidRegistry)
8399{
8400 // copy name, description, OS type, teleporter, UTC etc.
8401 mUserData->s = config.machineUserData;
8402
8403 // look up the object by Id to check it is valid
8404 ComObjPtr<GuestOSType> pGuestOSType;
8405 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8406 if (!pGuestOSType.isNull())
8407 mUserData->s.strOsType = pGuestOSType->i_id();
8408
8409#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8410 // stateFile encryption (optional)
8411 mSSData->strStateKeyId = config.strStateKeyId;
8412 mSSData->strStateKeyStore = config.strStateKeyStore;
8413 mData->mstrLogKeyId = config.strLogKeyId;
8414 mData->mstrLogKeyStore = config.strLogKeyStore;
8415#endif
8416
8417 // stateFile (optional)
8418 if (config.strStateFile.isEmpty())
8419 mSSData->strStateFilePath.setNull();
8420 else
8421 {
8422 Utf8Str stateFilePathFull(config.strStateFile);
8423 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8424 if (RT_FAILURE(vrc))
8425 return setErrorBoth(E_FAIL, vrc,
8426 tr("Invalid saved state file path '%s' (%Rrc)"),
8427 config.strStateFile.c_str(),
8428 vrc);
8429 mSSData->strStateFilePath = stateFilePathFull;
8430 }
8431
8432 // snapshot folder needs special processing so set it again
8433 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8434 if (FAILED(hrc)) return hrc;
8435
8436 /* Copy the extra data items (config may or may not be the same as
8437 * mData->pMachineConfigFile) if necessary. When loading the XML files
8438 * from disk they are the same, but not for OVF import. */
8439 if (mData->pMachineConfigFile != &config)
8440 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8441
8442 /* currentStateModified (optional, default is true) */
8443 mData->mCurrentStateModified = config.fCurrentStateModified;
8444
8445 mData->mLastStateChange = config.timeLastStateChange;
8446
8447 /*
8448 * note: all mUserData members must be assigned prior this point because
8449 * we need to commit changes in order to let mUserData be shared by all
8450 * snapshot machine instances.
8451 */
8452 mUserData.commitCopy();
8453
8454 // machine registry, if present (must be loaded before snapshots)
8455 if (config.canHaveOwnMediaRegistry())
8456 {
8457 // determine machine folder
8458 Utf8Str strMachineFolder = i_getSettingsFileFull();
8459 strMachineFolder.stripFilename();
8460 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8461 config.mediaRegistry,
8462 strMachineFolder);
8463 if (FAILED(hrc)) return hrc;
8464 }
8465
8466 /* Snapshot node (optional) */
8467 size_t cRootSnapshots;
8468 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8469 {
8470 // there must be only one root snapshot
8471 Assert(cRootSnapshots == 1);
8472 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8473
8474 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
8475 if (FAILED(hrc)) return hrc;
8476 }
8477
8478 // hardware data
8479 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
8480 config.recordingSettings);
8481 if (FAILED(hrc)) return hrc;
8482
8483 /*
8484 * NOTE: the assignment below must be the last thing to do,
8485 * otherwise it will be not possible to change the settings
8486 * somewhere in the code above because all setters will be
8487 * blocked by i_checkStateDependency(MutableStateDep).
8488 */
8489
8490 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8491 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8492 {
8493 /* no need to use i_setMachineState() during init() */
8494 mData->mMachineState = MachineState_AbortedSaved;
8495 }
8496 else if (config.fAborted)
8497 {
8498 mSSData->strStateFilePath.setNull();
8499
8500 /* no need to use i_setMachineState() during init() */
8501 mData->mMachineState = MachineState_Aborted;
8502 }
8503 else if (!mSSData->strStateFilePath.isEmpty())
8504 {
8505 /* no need to use i_setMachineState() during init() */
8506 mData->mMachineState = MachineState_Saved;
8507 }
8508
8509 // after loading settings, we are no longer different from the XML on disk
8510 mData->flModifications = 0;
8511
8512 return S_OK;
8513}
8514
8515/**
8516 * Loads all snapshots starting from the given settings.
8517 *
8518 * @param data snapshot settings.
8519 * @param aCurSnapshotId Current snapshot ID from the settings file.
8520 */
8521HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8522 const Guid &aCurSnapshotId)
8523{
8524 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8525 AssertReturn(!i_isSessionMachine(), E_FAIL);
8526
8527 HRESULT hrc = S_OK;
8528
8529 std::list<const settings::Snapshot *> llSettingsTodo;
8530 llSettingsTodo.push_back(&data);
8531 std::list<Snapshot *> llParentsTodo;
8532 llParentsTodo.push_back(NULL);
8533
8534 while (llSettingsTodo.size() > 0)
8535 {
8536 const settings::Snapshot *current = llSettingsTodo.front();
8537 llSettingsTodo.pop_front();
8538 Snapshot *pParent = llParentsTodo.front();
8539 llParentsTodo.pop_front();
8540
8541 Utf8Str strStateFile;
8542 if (!current->strStateFile.isEmpty())
8543 {
8544 /* optional */
8545 strStateFile = current->strStateFile;
8546 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8547 if (RT_FAILURE(vrc))
8548 {
8549 setErrorBoth(E_FAIL, vrc,
8550 tr("Invalid saved state file path '%s' (%Rrc)"),
8551 strStateFile.c_str(), vrc);
8552 }
8553 }
8554
8555 /* create a snapshot machine object */
8556 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8557 pSnapshotMachine.createObject();
8558 hrc = pSnapshotMachine->initFromSettings(this,
8559 current->hardware,
8560 &current->debugging,
8561 &current->autostart,
8562 current->recordingSettings,
8563 current->uuid.ref(),
8564 strStateFile);
8565 if (FAILED(hrc)) break;
8566
8567 /* create a snapshot object */
8568 ComObjPtr<Snapshot> pSnapshot;
8569 pSnapshot.createObject();
8570 /* initialize the snapshot */
8571 hrc = pSnapshot->init(mParent, // VirtualBox object
8572 current->uuid,
8573 current->strName,
8574 current->strDescription,
8575 current->timestamp,
8576 pSnapshotMachine,
8577 pParent);
8578 if (FAILED(hrc)) break;
8579
8580 /* memorize the first snapshot if necessary */
8581 if (!mData->mFirstSnapshot)
8582 {
8583 Assert(pParent == NULL);
8584 mData->mFirstSnapshot = pSnapshot;
8585 }
8586
8587 /* memorize the current snapshot when appropriate */
8588 if ( !mData->mCurrentSnapshot
8589 && pSnapshot->i_getId() == aCurSnapshotId
8590 )
8591 mData->mCurrentSnapshot = pSnapshot;
8592
8593 /* create all children */
8594 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8595 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8596 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8597 {
8598 llSettingsTodo.push_back(&*it);
8599 llParentsTodo.push_back(pSnapshot);
8600 }
8601 }
8602
8603 return hrc;
8604}
8605
8606/**
8607 * Loads settings into mHWData.
8608 *
8609 * @param puuidRegistry Registry ID.
8610 * @param puuidSnapshot Snapshot ID
8611 * @param data Reference to the hardware settings.
8612 * @param pDbg Pointer to the debugging settings.
8613 * @param pAutostart Pointer to the autostart settings
8614 * @param recording Reference to recording settings.
8615 */
8616HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8617 const Guid *puuidSnapshot,
8618 const settings::Hardware &data,
8619 const settings::Debugging *pDbg,
8620 const settings::Autostart *pAutostart,
8621 const settings::RecordingSettings &recording)
8622{
8623 AssertReturn(!i_isSessionMachine(), E_FAIL);
8624
8625 HRESULT hrc = S_OK;
8626
8627 try
8628 {
8629 ComObjPtr<GuestOSType> pGuestOSType;
8630 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8631
8632 /* The hardware version attribute (optional). */
8633 mHWData->mHWVersion = data.strVersion;
8634 mHWData->mHardwareUUID = data.uuid;
8635
8636 mHWData->mCPUCount = data.cCPUs;
8637 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8638 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8639 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8640 mHWData->mCpuProfile = data.strCpuProfile;
8641
8642 // cpu
8643 if (mHWData->mCPUHotPlugEnabled)
8644 {
8645 for (settings::CpuList::const_iterator
8646 it = data.llCpus.begin();
8647 it != data.llCpus.end();
8648 ++it)
8649 {
8650 const settings::Cpu &cpu = *it;
8651
8652 mHWData->mCPUAttached[cpu.ulId] = true;
8653 }
8654 }
8655
8656 mHWData->mMemorySize = data.ulMemorySizeMB;
8657 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8658
8659 // boot order
8660 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8661 {
8662 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8663 if (it == data.mapBootOrder.end())
8664 mHWData->mBootOrder[i] = DeviceType_Null;
8665 else
8666 mHWData->mBootOrder[i] = it->second;
8667 }
8668
8669 mHWData->mPointingHIDType = data.pointingHIDType;
8670 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8671 mHWData->mParavirtProvider = data.paravirtProvider;
8672 mHWData->mParavirtDebug = data.strParavirtDebug;
8673 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8674
8675 /* GraphicsAdapter */
8676 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8677 if (FAILED(hrc)) return hrc;
8678
8679 /* VRDEServer */
8680 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8681 if (FAILED(hrc)) return hrc;
8682
8683 /* Platform */
8684 hrc = mPlatform->i_loadSettings(data.platformSettings);
8685 if (FAILED(hrc)) return hrc;
8686
8687 i_platformPropertiesUpdate();
8688
8689 /* Firmware */
8690 hrc = mFirmwareSettings->i_loadSettings(data.firmwareSettings);
8691 if (FAILED(hrc)) return hrc;
8692
8693 /* Recording */
8694 hrc = mRecordingSettings->i_loadSettings(recording);
8695 if (FAILED(hrc)) return hrc;
8696
8697 /* Trusted Platform Module */
8698 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8699 if (FAILED(hrc)) return hrc;
8700
8701 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
8702 if (FAILED(hrc)) return hrc;
8703
8704 // Bandwidth control (must come before network adapters)
8705 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
8706 if (FAILED(hrc)) return hrc;
8707
8708 /* USB controllers */
8709 for (settings::USBControllerList::const_iterator
8710 it = data.usbSettings.llUSBControllers.begin();
8711 it != data.usbSettings.llUSBControllers.end();
8712 ++it)
8713 {
8714 const settings::USBController &settingsCtrl = *it;
8715 ComObjPtr<USBController> newCtrl;
8716
8717 newCtrl.createObject();
8718 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8719 mUSBControllers->push_back(newCtrl);
8720 }
8721
8722 /* USB device filters */
8723 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8724 if (FAILED(hrc)) return hrc;
8725
8726 // network adapters (establish array size first and apply defaults, to
8727 // ensure reading the same settings as we saved, since the list skips
8728 // adapters having defaults)
8729 size_t const newCount = PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType);
8730 size_t const oldCount = mNetworkAdapters.size();
8731 if (newCount > oldCount)
8732 {
8733 mNetworkAdapters.resize(newCount);
8734 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8735 {
8736 unconst(mNetworkAdapters[slot]).createObject();
8737 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8738 }
8739 }
8740 else if (newCount < oldCount)
8741 mNetworkAdapters.resize(newCount);
8742 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8743 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8744 for (settings::NetworkAdaptersList::const_iterator
8745 it = data.llNetworkAdapters.begin();
8746 it != data.llNetworkAdapters.end();
8747 ++it)
8748 {
8749 const settings::NetworkAdapter &nic = *it;
8750
8751 /* slot uniqueness is guaranteed by XML Schema */
8752 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8753 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8754 if (FAILED(hrc)) return hrc;
8755 }
8756
8757 // serial ports (establish defaults first, to ensure reading the same
8758 // settings as we saved, since the list skips ports having defaults)
8759 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8760 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8761 for (settings::SerialPortsList::const_iterator
8762 it = data.llSerialPorts.begin();
8763 it != data.llSerialPorts.end();
8764 ++it)
8765 {
8766 const settings::SerialPort &s = *it;
8767
8768 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8769 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8770 if (FAILED(hrc)) return hrc;
8771 }
8772
8773 // parallel ports (establish defaults first, to ensure reading the same
8774 // settings as we saved, since the list skips ports having defaults)
8775 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8776 mParallelPorts[i]->i_applyDefaults();
8777 for (settings::ParallelPortsList::const_iterator
8778 it = data.llParallelPorts.begin();
8779 it != data.llParallelPorts.end();
8780 ++it)
8781 {
8782 const settings::ParallelPort &p = *it;
8783
8784 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8785 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8786 if (FAILED(hrc)) return hrc;
8787 }
8788
8789 /* Audio settings */
8790 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
8791 if (FAILED(hrc)) return hrc;
8792
8793 /* storage controllers */
8794 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
8795 if (FAILED(hrc)) return hrc;
8796
8797 /* Shared folders */
8798 for (settings::SharedFoldersList::const_iterator
8799 it = data.llSharedFolders.begin();
8800 it != data.llSharedFolders.end();
8801 ++it)
8802 {
8803 const settings::SharedFolder &sf = *it;
8804
8805 ComObjPtr<SharedFolder> sharedFolder;
8806 /* Check for double entries. Not allowed! */
8807 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8808 if (SUCCEEDED(hrc))
8809 return setError(VBOX_E_OBJECT_IN_USE,
8810 tr("Shared folder named '%s' already exists"),
8811 sf.strName.c_str());
8812
8813 /* Create the new shared folder. Don't break on error. This will be
8814 * reported when the machine starts. */
8815 sharedFolder.createObject();
8816 hrc = sharedFolder->init(i_getMachine(),
8817 sf.strName,
8818 sf.strHostPath,
8819 RT_BOOL(sf.fWritable),
8820 RT_BOOL(sf.fAutoMount),
8821 sf.strAutoMountPoint,
8822 false /* fFailOnError */,
8823 sf.enmSymlinkPolicy);
8824 if (FAILED(hrc)) return hrc;
8825 mHWData->mSharedFolders.push_back(sharedFolder);
8826 }
8827
8828 // Clipboard
8829 mHWData->mClipboardMode = data.clipboardMode;
8830 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8831
8832 // drag'n'drop
8833 mHWData->mDnDMode = data.dndMode;
8834
8835 // guest settings
8836 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8837
8838 // IO settings
8839 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8840 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8841
8842 // Host PCI devices
8843 for (settings::HostPCIDeviceAttachmentList::const_iterator
8844 it = data.pciAttachments.begin();
8845 it != data.pciAttachments.end();
8846 ++it)
8847 {
8848 const settings::HostPCIDeviceAttachment &hpda = *it;
8849 ComObjPtr<PCIDeviceAttachment> pda;
8850
8851 pda.createObject();
8852 pda->i_loadSettings(this, hpda);
8853 mHWData->mPCIDeviceAssignments.push_back(pda);
8854 }
8855
8856 /*
8857 * (The following isn't really real hardware, but it lives in HWData
8858 * for reasons of convenience.)
8859 */
8860
8861#ifdef VBOX_WITH_GUEST_PROPS
8862 /* Guest properties (optional) */
8863
8864 /* Only load transient guest properties for configs which have saved
8865 * state, because there shouldn't be any for powered off VMs. The same
8866 * logic applies for snapshots, as offline snapshots shouldn't have
8867 * any such properties. They confuse the code in various places.
8868 * Note: can't rely on the machine state, as it isn't set yet. */
8869 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8870 /* apologies for the hacky unconst() usage, but this needs hacking
8871 * actually inconsistent settings into consistency, otherwise there
8872 * will be some corner cases where the inconsistency survives
8873 * surprisingly long without getting fixed, especially for snapshots
8874 * as there are no config changes. */
8875 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8876 for (settings::GuestPropertiesList::iterator
8877 it = llGuestProperties.begin();
8878 it != llGuestProperties.end();
8879 /*nothing*/)
8880 {
8881 const settings::GuestProperty &prop = *it;
8882 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8883 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8884 if ( fSkipTransientGuestProperties
8885 && ( fFlags & GUEST_PROP_F_TRANSIENT
8886 || fFlags & GUEST_PROP_F_TRANSRESET))
8887 {
8888 it = llGuestProperties.erase(it);
8889 continue;
8890 }
8891 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8892 mHWData->mGuestProperties[prop.strName] = property;
8893 ++it;
8894 }
8895#endif /* VBOX_WITH_GUEST_PROPS defined */
8896
8897 hrc = i_loadDebugging(pDbg);
8898 if (FAILED(hrc))
8899 return hrc;
8900
8901 mHWData->mAutostart = *pAutostart;
8902
8903 /* default frontend */
8904 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8905 }
8906 catch (std::bad_alloc &)
8907 {
8908 return E_OUTOFMEMORY;
8909 }
8910
8911 AssertComRC(hrc);
8912 return hrc;
8913}
8914
8915/**
8916 * Called from i_loadHardware() to load the debugging settings of the
8917 * machine.
8918 *
8919 * @param pDbg Pointer to the settings.
8920 */
8921HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8922{
8923 mHWData->mDebugging = *pDbg;
8924 /* no more processing currently required, this will probably change. */
8925
8926 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
8927 if (FAILED(hrc)) return hrc;
8928
8929 return S_OK;
8930}
8931
8932/**
8933 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8934 *
8935 * @param data storage settings.
8936 * @param puuidRegistry media registry ID to set media to or NULL;
8937 * see Machine::i_loadMachineDataFromSettings()
8938 * @param puuidSnapshot snapshot ID
8939 * @return
8940 */
8941HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8942 const Guid *puuidRegistry,
8943 const Guid *puuidSnapshot)
8944{
8945 AssertReturn(!i_isSessionMachine(), E_FAIL);
8946
8947 HRESULT hrc = S_OK;
8948
8949 for (settings::StorageControllersList::const_iterator
8950 it = data.llStorageControllers.begin();
8951 it != data.llStorageControllers.end();
8952 ++it)
8953 {
8954 const settings::StorageController &ctlData = *it;
8955
8956 ComObjPtr<StorageController> pCtl;
8957 /* Try to find one with the name first. */
8958 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8959 if (SUCCEEDED(hrc))
8960 return setError(VBOX_E_OBJECT_IN_USE,
8961 tr("Storage controller named '%s' already exists"),
8962 ctlData.strName.c_str());
8963
8964 pCtl.createObject();
8965 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
8966 if (FAILED(hrc)) return hrc;
8967
8968 mStorageControllers->push_back(pCtl);
8969
8970 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8971 if (FAILED(hrc)) return hrc;
8972
8973 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8974 if (FAILED(hrc)) return hrc;
8975
8976 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8977 if (FAILED(hrc)) return hrc;
8978
8979 /* Load the attached devices now. */
8980 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
8981 if (FAILED(hrc)) return hrc;
8982 }
8983
8984 return S_OK;
8985}
8986
8987/**
8988 * Called from i_loadStorageControllers for a controller's devices.
8989 *
8990 * @param aStorageController
8991 * @param data
8992 * @param puuidRegistry media registry ID to set media to or NULL; see
8993 * Machine::i_loadMachineDataFromSettings()
8994 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
8995 * @return
8996 */
8997HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
8998 const settings::StorageController &data,
8999 const Guid *puuidRegistry,
9000 const Guid *puuidSnapshot)
9001{
9002 HRESULT hrc = S_OK;
9003
9004 /* paranoia: detect duplicate attachments */
9005 for (settings::AttachedDevicesList::const_iterator
9006 it = data.llAttachedDevices.begin();
9007 it != data.llAttachedDevices.end();
9008 ++it)
9009 {
9010 const settings::AttachedDevice &ad = *it;
9011
9012 for (settings::AttachedDevicesList::const_iterator it2 = it;
9013 it2 != data.llAttachedDevices.end();
9014 ++it2)
9015 {
9016 if (it == it2)
9017 continue;
9018
9019 const settings::AttachedDevice &ad2 = *it2;
9020
9021 if ( ad.lPort == ad2.lPort
9022 && ad.lDevice == ad2.lDevice)
9023 {
9024 return setError(E_FAIL,
9025 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9026 aStorageController->i_getName().c_str(),
9027 ad.lPort,
9028 ad.lDevice,
9029 mUserData->s.strName.c_str());
9030 }
9031 }
9032 }
9033
9034 for (settings::AttachedDevicesList::const_iterator
9035 it = data.llAttachedDevices.begin();
9036 it != data.llAttachedDevices.end();
9037 ++it)
9038 {
9039 const settings::AttachedDevice &dev = *it;
9040 ComObjPtr<Medium> medium;
9041
9042 switch (dev.deviceType)
9043 {
9044 case DeviceType_Floppy:
9045 case DeviceType_DVD:
9046 if (dev.strHostDriveSrc.isNotEmpty())
9047 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9048 false /* fRefresh */, medium);
9049 else
9050 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9051 dev.uuid,
9052 false /* fRefresh */,
9053 false /* aSetError */,
9054 medium);
9055 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9056 // This is not an error. The host drive or UUID might have vanished, so just go
9057 // ahead without this removeable medium attachment
9058 hrc = S_OK;
9059 break;
9060
9061 case DeviceType_HardDisk:
9062 {
9063 /* find a hard disk by UUID */
9064 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9065 if (FAILED(hrc))
9066 {
9067 if (i_isSnapshotMachine())
9068 {
9069 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9070 // so the user knows that the bad disk is in a snapshot somewhere
9071 com::ErrorInfo info;
9072 return setError(E_FAIL,
9073 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9074 puuidSnapshot->raw(),
9075 info.getText().raw());
9076 }
9077 return hrc;
9078 }
9079
9080 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9081
9082 if (medium->i_getType() == MediumType_Immutable)
9083 {
9084 if (i_isSnapshotMachine())
9085 return setError(E_FAIL,
9086 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9087 "of the virtual machine '%s' ('%s')"),
9088 medium->i_getLocationFull().c_str(),
9089 dev.uuid.raw(),
9090 puuidSnapshot->raw(),
9091 mUserData->s.strName.c_str(),
9092 mData->m_strConfigFileFull.c_str());
9093
9094 return setError(E_FAIL,
9095 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9096 medium->i_getLocationFull().c_str(),
9097 dev.uuid.raw(),
9098 mUserData->s.strName.c_str(),
9099 mData->m_strConfigFileFull.c_str());
9100 }
9101
9102 if (medium->i_getType() == MediumType_MultiAttach)
9103 {
9104 if (i_isSnapshotMachine())
9105 return setError(E_FAIL,
9106 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9107 "of the virtual machine '%s' ('%s')"),
9108 medium->i_getLocationFull().c_str(),
9109 dev.uuid.raw(),
9110 puuidSnapshot->raw(),
9111 mUserData->s.strName.c_str(),
9112 mData->m_strConfigFileFull.c_str());
9113
9114 return setError(E_FAIL,
9115 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9116 medium->i_getLocationFull().c_str(),
9117 dev.uuid.raw(),
9118 mUserData->s.strName.c_str(),
9119 mData->m_strConfigFileFull.c_str());
9120 }
9121
9122 if ( !i_isSnapshotMachine()
9123 && medium->i_getChildren().size() != 0
9124 )
9125 return setError(E_FAIL,
9126 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9127 "because it has %d differencing child hard disks"),
9128 medium->i_getLocationFull().c_str(),
9129 dev.uuid.raw(),
9130 mUserData->s.strName.c_str(),
9131 mData->m_strConfigFileFull.c_str(),
9132 medium->i_getChildren().size());
9133
9134 if (i_findAttachment(*mMediumAttachments.data(),
9135 medium))
9136 return setError(E_FAIL,
9137 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9138 medium->i_getLocationFull().c_str(),
9139 dev.uuid.raw(),
9140 mUserData->s.strName.c_str(),
9141 mData->m_strConfigFileFull.c_str());
9142
9143 break;
9144 }
9145
9146 default:
9147 return setError(E_FAIL,
9148 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9149 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9150 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9151 }
9152
9153 if (FAILED(hrc))
9154 break;
9155
9156 /* Bandwidth groups are loaded at this point. */
9157 ComObjPtr<BandwidthGroup> pBwGroup;
9158
9159 if (!dev.strBwGroup.isEmpty())
9160 {
9161 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9162 if (FAILED(hrc))
9163 return setError(E_FAIL,
9164 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9165 medium->i_getLocationFull().c_str(),
9166 dev.strBwGroup.c_str(),
9167 mUserData->s.strName.c_str(),
9168 mData->m_strConfigFileFull.c_str());
9169 pBwGroup->i_reference();
9170 }
9171
9172 const Utf8Str controllerName = aStorageController->i_getName();
9173 ComObjPtr<MediumAttachment> pAttachment;
9174 pAttachment.createObject();
9175 hrc = pAttachment->init(this,
9176 medium,
9177 controllerName,
9178 dev.lPort,
9179 dev.lDevice,
9180 dev.deviceType,
9181 false,
9182 dev.fPassThrough,
9183 dev.fTempEject,
9184 dev.fNonRotational,
9185 dev.fDiscard,
9186 dev.fHotPluggable,
9187 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9188 if (FAILED(hrc)) break;
9189
9190 /* associate the medium with this machine and snapshot */
9191 if (!medium.isNull())
9192 {
9193 AutoCaller medCaller(medium);
9194 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9195 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9196
9197 if (i_isSnapshotMachine())
9198 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9199 else
9200 hrc = medium->i_addBackReference(mData->mUuid);
9201 /* If the medium->addBackReference fails it sets an appropriate
9202 * error message, so no need to do any guesswork here. */
9203
9204 if (puuidRegistry)
9205 // caller wants registry ID to be set on all attached media (OVF import case)
9206 medium->i_addRegistry(*puuidRegistry);
9207 }
9208
9209 if (FAILED(hrc))
9210 break;
9211
9212 /* back up mMediumAttachments to let registeredInit() properly rollback
9213 * on failure (= limited accessibility) */
9214 i_setModified(IsModified_Storage);
9215 mMediumAttachments.backup();
9216 mMediumAttachments->push_back(pAttachment);
9217 }
9218
9219 return hrc;
9220}
9221
9222/**
9223 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9224 *
9225 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9226 * @param aSnapshot where to return the found snapshot
9227 * @param aSetError true to set extended error info on failure
9228 */
9229HRESULT Machine::i_findSnapshotById(const Guid &aId,
9230 ComObjPtr<Snapshot> &aSnapshot,
9231 bool aSetError /* = false */)
9232{
9233 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9234
9235 if (!mData->mFirstSnapshot)
9236 {
9237 if (aSetError)
9238 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9239 return E_FAIL;
9240 }
9241
9242 if (aId.isZero())
9243 aSnapshot = mData->mFirstSnapshot;
9244 else
9245 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9246
9247 if (!aSnapshot)
9248 {
9249 if (aSetError)
9250 return setError(E_FAIL,
9251 tr("Could not find a snapshot with UUID {%s}"),
9252 aId.toString().c_str());
9253 return E_FAIL;
9254 }
9255
9256 return S_OK;
9257}
9258
9259/**
9260 * Returns the snapshot with the given name or fails of no such snapshot.
9261 *
9262 * @param strName snapshot name to find
9263 * @param aSnapshot where to return the found snapshot
9264 * @param aSetError true to set extended error info on failure
9265 */
9266HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9267 ComObjPtr<Snapshot> &aSnapshot,
9268 bool aSetError /* = false */)
9269{
9270 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9271
9272 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9273
9274 if (!mData->mFirstSnapshot)
9275 {
9276 if (aSetError)
9277 return setError(VBOX_E_OBJECT_NOT_FOUND,
9278 tr("This machine does not have any snapshots"));
9279 return VBOX_E_OBJECT_NOT_FOUND;
9280 }
9281
9282 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9283
9284 if (!aSnapshot)
9285 {
9286 if (aSetError)
9287 return setError(VBOX_E_OBJECT_NOT_FOUND,
9288 tr("Could not find a snapshot named '%s'"), strName.c_str());
9289 return VBOX_E_OBJECT_NOT_FOUND;
9290 }
9291
9292 return S_OK;
9293}
9294
9295/**
9296 * Returns a storage controller object with the given name.
9297 *
9298 * @param aName storage controller name to find
9299 * @param aStorageController where to return the found storage controller
9300 * @param aSetError true to set extended error info on failure
9301 */
9302HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9303 ComObjPtr<StorageController> &aStorageController,
9304 bool aSetError /* = false */)
9305{
9306 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9307
9308 for (StorageControllerList::const_iterator
9309 it = mStorageControllers->begin();
9310 it != mStorageControllers->end();
9311 ++it)
9312 {
9313 if ((*it)->i_getName() == aName)
9314 {
9315 aStorageController = (*it);
9316 return S_OK;
9317 }
9318 }
9319
9320 if (aSetError)
9321 return setError(VBOX_E_OBJECT_NOT_FOUND,
9322 tr("Could not find a storage controller named '%s'"),
9323 aName.c_str());
9324 return VBOX_E_OBJECT_NOT_FOUND;
9325}
9326
9327/**
9328 * Returns a USB controller object with the given name.
9329 *
9330 * @param aName USB controller name to find
9331 * @param aUSBController where to return the found USB controller
9332 * @param aSetError true to set extended error info on failure
9333 */
9334HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9335 ComObjPtr<USBController> &aUSBController,
9336 bool aSetError /* = false */)
9337{
9338 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9339
9340 for (USBControllerList::const_iterator
9341 it = mUSBControllers->begin();
9342 it != mUSBControllers->end();
9343 ++it)
9344 {
9345 if ((*it)->i_getName() == aName)
9346 {
9347 aUSBController = (*it);
9348 return S_OK;
9349 }
9350 }
9351
9352 if (aSetError)
9353 return setError(VBOX_E_OBJECT_NOT_FOUND,
9354 tr("Could not find a storage controller named '%s'"),
9355 aName.c_str());
9356 return VBOX_E_OBJECT_NOT_FOUND;
9357}
9358
9359/**
9360 * Returns the number of USB controller instance of the given type.
9361 *
9362 * @param enmType USB controller type.
9363 */
9364ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9365{
9366 ULONG cCtrls = 0;
9367
9368 for (USBControllerList::const_iterator
9369 it = mUSBControllers->begin();
9370 it != mUSBControllers->end();
9371 ++it)
9372 {
9373 if ((*it)->i_getControllerType() == enmType)
9374 cCtrls++;
9375 }
9376
9377 return cCtrls;
9378}
9379
9380HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9381 MediumAttachmentList &atts)
9382{
9383 AutoCaller autoCaller(this);
9384 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
9385
9386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9387
9388 for (MediumAttachmentList::const_iterator
9389 it = mMediumAttachments->begin();
9390 it != mMediumAttachments->end();
9391 ++it)
9392 {
9393 const ComObjPtr<MediumAttachment> &pAtt = *it;
9394 // should never happen, but deal with NULL pointers in the list.
9395 AssertContinue(!pAtt.isNull());
9396
9397 // getControllerName() needs caller+read lock
9398 AutoCaller autoAttCaller(pAtt);
9399 if (FAILED(autoAttCaller.hrc()))
9400 {
9401 atts.clear();
9402 return autoAttCaller.hrc();
9403 }
9404 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9405
9406 if (pAtt->i_getControllerName() == aName)
9407 atts.push_back(pAtt);
9408 }
9409
9410 return S_OK;
9411}
9412
9413
9414/**
9415 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9416 * file if the machine name was changed and about creating a new settings file
9417 * if this is a new machine.
9418 *
9419 * @note Must be never called directly but only from #saveSettings().
9420 */
9421HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9422 bool *pfSettingsFileIsNew)
9423{
9424 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9425
9426 HRESULT hrc = S_OK;
9427
9428 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9429 /// @todo need to handle primary group change, too
9430
9431 /* attempt to rename the settings file if machine name is changed */
9432 if ( mUserData->s.fNameSync
9433 && mUserData.isBackedUp()
9434 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9435 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9436 )
9437 {
9438 bool dirRenamed = false;
9439 bool fileRenamed = false;
9440
9441 Utf8Str configFile, newConfigFile;
9442 Utf8Str configFilePrev, newConfigFilePrev;
9443 Utf8Str NVRAMFile, newNVRAMFile;
9444 Utf8Str configDir, newConfigDir;
9445
9446 do
9447 {
9448 int vrc = VINF_SUCCESS;
9449
9450 Utf8Str name = mUserData.backedUpData()->s.strName;
9451 Utf8Str newName = mUserData->s.strName;
9452 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9453 if (group == "/")
9454 group.setNull();
9455 Utf8Str newGroup = mUserData->s.llGroups.front();
9456 if (newGroup == "/")
9457 newGroup.setNull();
9458
9459 configFile = mData->m_strConfigFileFull;
9460
9461 /* first, rename the directory if it matches the group and machine name */
9462 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9463 /** @todo hack, make somehow use of ComposeMachineFilename */
9464 if (mUserData->s.fDirectoryIncludesUUID)
9465 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9466 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9467 /** @todo hack, make somehow use of ComposeMachineFilename */
9468 if (mUserData->s.fDirectoryIncludesUUID)
9469 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9470 configDir = configFile;
9471 configDir.stripFilename();
9472 newConfigDir = configDir;
9473 if ( configDir.length() >= groupPlusName.length()
9474 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9475 groupPlusName.c_str()))
9476 {
9477 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9478 Utf8Str newConfigBaseDir(newConfigDir);
9479 newConfigDir.append(newGroupPlusName);
9480 /* consistency: use \ if appropriate on the platform */
9481 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9482 /* new dir and old dir cannot be equal here because of 'if'
9483 * above and because name != newName */
9484 Assert(configDir != newConfigDir);
9485 if (!fSettingsFileIsNew)
9486 {
9487 /* perform real rename only if the machine is not new */
9488 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9489 if ( vrc == VERR_FILE_NOT_FOUND
9490 || vrc == VERR_PATH_NOT_FOUND)
9491 {
9492 /* create the parent directory, then retry renaming */
9493 Utf8Str parent(newConfigDir);
9494 parent.stripFilename();
9495 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9496 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9497 }
9498 if (RT_FAILURE(vrc))
9499 {
9500 hrc = setErrorBoth(E_FAIL, vrc,
9501 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9502 configDir.c_str(),
9503 newConfigDir.c_str(),
9504 vrc);
9505 break;
9506 }
9507 /* delete subdirectories which are no longer needed */
9508 Utf8Str dir(configDir);
9509 dir.stripFilename();
9510 while (dir != newConfigBaseDir && dir != ".")
9511 {
9512 vrc = RTDirRemove(dir.c_str());
9513 if (RT_FAILURE(vrc))
9514 break;
9515 dir.stripFilename();
9516 }
9517 dirRenamed = true;
9518 }
9519 }
9520
9521 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9522
9523 /* then try to rename the settings file itself */
9524 if (newConfigFile != configFile)
9525 {
9526 /* get the path to old settings file in renamed directory */
9527 Assert(mData->m_strConfigFileFull == configFile);
9528 configFile.printf("%s%c%s",
9529 newConfigDir.c_str(),
9530 RTPATH_DELIMITER,
9531 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9532 if (!fSettingsFileIsNew)
9533 {
9534 /* perform real rename only if the machine is not new */
9535 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9536 if (RT_FAILURE(vrc))
9537 {
9538 hrc = setErrorBoth(E_FAIL, vrc,
9539 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9540 configFile.c_str(),
9541 newConfigFile.c_str(),
9542 vrc);
9543 break;
9544 }
9545 fileRenamed = true;
9546 configFilePrev = configFile;
9547 configFilePrev += "-prev";
9548 newConfigFilePrev = newConfigFile;
9549 newConfigFilePrev += "-prev";
9550 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9551 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9552 if (NVRAMFile.isNotEmpty())
9553 {
9554 // in the NVRAM file path, replace the old directory with the new directory
9555 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9556 {
9557 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9558 NVRAMFile = newConfigDir + strNVRAMFile;
9559 }
9560 newNVRAMFile = newConfigFile;
9561 newNVRAMFile.stripSuffix();
9562 newNVRAMFile += ".nvram";
9563 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9564 }
9565 }
9566 }
9567
9568 // update m_strConfigFileFull amd mConfigFile
9569 mData->m_strConfigFileFull = newConfigFile;
9570 // compute the relative path too
9571 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9572
9573 // store the old and new so that VirtualBox::i_saveSettings() can update
9574 // the media registry
9575 if ( mData->mRegistered
9576 && (configDir != newConfigDir || configFile != newConfigFile))
9577 {
9578 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9579
9580 if (pfNeedsGlobalSaveSettings)
9581 *pfNeedsGlobalSaveSettings = true;
9582 }
9583
9584 // in the saved state file path, replace the old directory with the new directory
9585 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9586 {
9587 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9588 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9589 }
9590 if (newNVRAMFile.isNotEmpty())
9591 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9592
9593 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9594 if (mData->mFirstSnapshot)
9595 {
9596 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9597 newConfigDir.c_str());
9598 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9599 newConfigDir.c_str());
9600 }
9601 }
9602 while (0);
9603
9604 if (FAILED(hrc))
9605 {
9606 /* silently try to rename everything back */
9607 if (fileRenamed)
9608 {
9609 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9610 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9611 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9612 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9613 }
9614 if (dirRenamed)
9615 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9616 }
9617
9618 if (FAILED(hrc)) return hrc;
9619 }
9620
9621 if (fSettingsFileIsNew)
9622 {
9623 /* create a virgin config file */
9624 int vrc = VINF_SUCCESS;
9625
9626 /* ensure the settings directory exists */
9627 Utf8Str path(mData->m_strConfigFileFull);
9628 path.stripFilename();
9629 if (!RTDirExists(path.c_str()))
9630 {
9631 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9632 if (RT_FAILURE(vrc))
9633 {
9634 return setErrorBoth(E_FAIL, vrc,
9635 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9636 path.c_str(),
9637 vrc);
9638 }
9639 }
9640
9641 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9642 path = mData->m_strConfigFileFull;
9643 RTFILE f = NIL_RTFILE;
9644 vrc = RTFileOpen(&f, path.c_str(),
9645 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9646 if (RT_FAILURE(vrc))
9647 return setErrorBoth(E_FAIL, vrc,
9648 tr("Could not create the settings file '%s' (%Rrc)"),
9649 path.c_str(),
9650 vrc);
9651 RTFileClose(f);
9652 }
9653 if (pfSettingsFileIsNew)
9654 *pfSettingsFileIsNew = fSettingsFileIsNew;
9655
9656 return hrc;
9657}
9658
9659/**
9660 * Saves and commits machine data, user data and hardware data.
9661 *
9662 * Note that on failure, the data remains uncommitted.
9663 *
9664 * @a aFlags may combine the following flags:
9665 *
9666 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9667 * Used when saving settings after an operation that makes them 100%
9668 * correspond to the settings from the current snapshot.
9669 * - SaveS_Force: settings will be saved without doing a deep compare of the
9670 * settings structures. This is used when this is called because snapshots
9671 * have changed to avoid the overhead of the deep compare.
9672 *
9673 * @note Must be called from under this object's write lock. Locks children for
9674 * writing.
9675 *
9676 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9677 * initialized to false and that will be set to true by this function if
9678 * the caller must invoke VirtualBox::i_saveSettings() because the global
9679 * settings have changed. This will happen if a machine rename has been
9680 * saved and the global machine and media registries will therefore need
9681 * updating.
9682 * @param alock Reference to the lock for this machine object.
9683 * @param aFlags Flags.
9684 */
9685HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9686 AutoWriteLock &alock,
9687 int aFlags /*= 0*/)
9688{
9689 LogFlowThisFuncEnter();
9690
9691 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9692
9693 /* make sure child objects are unable to modify the settings while we are
9694 * saving them */
9695 i_ensureNoStateDependencies(alock);
9696
9697 AssertReturn(!i_isSnapshotMachine(),
9698 E_FAIL);
9699
9700 if (!mData->mAccessible)
9701 return setError(VBOX_E_INVALID_VM_STATE,
9702 tr("The machine is not accessible, so cannot save settings"));
9703
9704 HRESULT hrc = S_OK;
9705 PCVBOXCRYPTOIF pCryptoIf = NULL;
9706 const char *pszPassword = NULL;
9707 SecretKey *pKey = NULL;
9708
9709#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9710 if (mData->mstrKeyId.isNotEmpty())
9711 {
9712 /* VM is going to be encrypted. */
9713 alock.release(); /** @todo Revise the locking. */
9714 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
9715 alock.acquire();
9716 if (FAILED(hrc)) return hrc; /* Error is set. */
9717
9718 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
9719 if (RT_SUCCESS(vrc))
9720 pszPassword = (const char *)pKey->getKeyBuffer();
9721 else
9722 {
9723 mParent->i_releaseCryptoIf(pCryptoIf);
9724 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
9725 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
9726 mData->mstrKeyId.c_str(), vrc);
9727 }
9728 }
9729#else
9730 RT_NOREF(pKey);
9731#endif
9732
9733 bool fNeedsWrite = false;
9734 bool fSettingsFileIsNew = false;
9735
9736 /* First, prepare to save settings. It will care about renaming the
9737 * settings directory and file if the machine name was changed and about
9738 * creating a new settings file if this is a new machine. */
9739 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
9740 if (FAILED(hrc))
9741 {
9742#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9743 if (pCryptoIf)
9744 {
9745 alock.release(); /** @todo Revise the locking. */
9746 mParent->i_releaseCryptoIf(pCryptoIf);
9747 alock.acquire();
9748 }
9749 if (pKey)
9750 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9751#endif
9752 return hrc;
9753 }
9754
9755 // keep a pointer to the current settings structures
9756 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9757 settings::MachineConfigFile *pNewConfig = NULL;
9758
9759 try
9760 {
9761 // make a fresh one to have everyone write stuff into
9762 pNewConfig = new settings::MachineConfigFile(NULL);
9763 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9764#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9765 pNewConfig->strKeyId = mData->mstrKeyId;
9766 pNewConfig->strKeyStore = mData->mstrKeyStore;
9767#endif
9768
9769 // now go and copy all the settings data from COM to the settings structures
9770 // (this calls i_saveSettings() on all the COM objects in the machine)
9771 i_copyMachineDataToSettings(*pNewConfig);
9772
9773 if (aFlags & SaveS_ResetCurStateModified)
9774 {
9775 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9776 mData->mCurrentStateModified = FALSE;
9777 fNeedsWrite = true; // always, no need to compare
9778 }
9779 else if (aFlags & SaveS_Force)
9780 {
9781 fNeedsWrite = true; // always, no need to compare
9782 }
9783 else
9784 {
9785 if (!mData->mCurrentStateModified)
9786 {
9787 // do a deep compare of the settings that we just saved with the settings
9788 // previously stored in the config file; this invokes MachineConfigFile::operator==
9789 // which does a deep compare of all the settings, which is expensive but less expensive
9790 // than writing out XML in vain
9791 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9792
9793 // could still be modified if any settings changed
9794 mData->mCurrentStateModified = fAnySettingsChanged;
9795
9796 fNeedsWrite = fAnySettingsChanged;
9797 }
9798 else
9799 fNeedsWrite = true;
9800 }
9801
9802 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9803
9804 if (fNeedsWrite)
9805 {
9806 // now spit it all out!
9807 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
9808 if (aFlags & SaveS_RemoveBackup)
9809 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
9810 }
9811
9812 mData->pMachineConfigFile = pNewConfig;
9813 delete pOldConfig;
9814 i_commit();
9815
9816 // after saving settings, we are no longer different from the XML on disk
9817 mData->flModifications = 0;
9818 }
9819 catch (HRESULT err)
9820 {
9821 // we assume that error info is set by the thrower
9822 hrc = err;
9823
9824 // delete any newly created settings file
9825 if (fSettingsFileIsNew)
9826 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
9827
9828 // restore old config
9829 delete pNewConfig;
9830 mData->pMachineConfigFile = pOldConfig;
9831 }
9832 catch (...)
9833 {
9834 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9835 }
9836
9837#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9838 if (pCryptoIf)
9839 {
9840 alock.release(); /** @todo Revise the locking. */
9841 mParent->i_releaseCryptoIf(pCryptoIf);
9842 alock.acquire();
9843 }
9844 if (pKey)
9845 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
9846#endif
9847
9848 if (fNeedsWrite)
9849 {
9850 /* Fire the data change event, even on failure (since we've already
9851 * committed all data). This is done only for SessionMachines because
9852 * mutable Machine instances are always not registered (i.e. private
9853 * to the client process that creates them) and thus don't need to
9854 * inform callbacks. */
9855 if (i_isSessionMachine())
9856 mParent->i_onMachineDataChanged(mData->mUuid);
9857 }
9858
9859 LogFlowThisFunc(("hrc=%08X\n", hrc));
9860 LogFlowThisFuncLeave();
9861 return hrc;
9862}
9863
9864/**
9865 * Implementation for saving the machine settings into the given
9866 * settings::MachineConfigFile instance. This copies machine extradata
9867 * from the previous machine config file in the instance data, if any.
9868 *
9869 * This gets called from two locations:
9870 *
9871 * -- Machine::i_saveSettings(), during the regular XML writing;
9872 *
9873 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9874 * exported to OVF and we write the VirtualBox proprietary XML
9875 * into a <vbox:Machine> tag.
9876 *
9877 * This routine fills all the fields in there, including snapshots, *except*
9878 * for the following:
9879 *
9880 * -- fCurrentStateModified. There is some special logic associated with that.
9881 *
9882 * The caller can then call MachineConfigFile::write() or do something else
9883 * with it.
9884 *
9885 * Caller must hold the machine lock!
9886 *
9887 * This throws XML errors and HRESULT, so the caller must have a catch block!
9888 */
9889void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9890{
9891 // deep copy extradata, being extra careful with self assignment (the STL
9892 // map assignment on Mac OS X clang based Xcode isn't checking)
9893 if (&config != mData->pMachineConfigFile)
9894 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9895
9896 config.uuid = mData->mUuid;
9897
9898 // copy name, description, OS type, teleport, UTC etc.
9899 config.machineUserData = mUserData->s;
9900
9901#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9902 config.strStateKeyId = mSSData->strStateKeyId;
9903 config.strStateKeyStore = mSSData->strStateKeyStore;
9904 config.strLogKeyId = mData->mstrLogKeyId;
9905 config.strLogKeyStore = mData->mstrLogKeyStore;
9906#endif
9907
9908 if ( mData->mMachineState == MachineState_Saved
9909 || mData->mMachineState == MachineState_AbortedSaved
9910 || mData->mMachineState == MachineState_Restoring
9911 // when doing certain snapshot operations we may or may not have
9912 // a saved state in the current state, so keep everything as is
9913 || ( ( mData->mMachineState == MachineState_Snapshotting
9914 || mData->mMachineState == MachineState_DeletingSnapshot
9915 || mData->mMachineState == MachineState_RestoringSnapshot)
9916 && (!mSSData->strStateFilePath.isEmpty())
9917 )
9918 )
9919 {
9920 Assert(!mSSData->strStateFilePath.isEmpty());
9921 /* try to make the file name relative to the settings file dir */
9922 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9923 }
9924 else
9925 {
9926 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9927 config.strStateFile.setNull();
9928 }
9929
9930 if (mData->mCurrentSnapshot)
9931 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9932 else
9933 config.uuidCurrentSnapshot.clear();
9934
9935 config.timeLastStateChange = mData->mLastStateChange;
9936 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
9937 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9938
9939 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
9940 if (FAILED(hrc)) throw hrc;
9941
9942 // save machine's media registry if this is VirtualBox 4.0 or later
9943 if (config.canHaveOwnMediaRegistry())
9944 {
9945 // determine machine folder
9946 Utf8Str strMachineFolder = i_getSettingsFileFull();
9947 strMachineFolder.stripFilename();
9948 mParent->i_saveMediaRegistry(config.mediaRegistry,
9949 i_getId(), // only media with registry ID == machine UUID
9950 strMachineFolder);
9951 // this throws HRESULT
9952 }
9953
9954 // save snapshots
9955 hrc = i_saveAllSnapshots(config);
9956 if (FAILED(hrc)) throw hrc;
9957}
9958
9959/**
9960 * Saves all snapshots of the machine into the given machine config file. Called
9961 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9962 * @param config
9963 * @return
9964 */
9965HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9966{
9967 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9968
9969 HRESULT hrc = S_OK;
9970
9971 try
9972 {
9973 config.llFirstSnapshot.clear();
9974
9975 if (mData->mFirstSnapshot)
9976 {
9977 // the settings use a list for "the first snapshot"
9978 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9979
9980 // get reference to the snapshot on the list and work on that
9981 // element straight in the list to avoid excessive copying later
9982 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9983 if (FAILED(hrc)) throw hrc;
9984 }
9985
9986// if (mType == IsSessionMachine)
9987// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9988
9989 }
9990 catch (HRESULT err)
9991 {
9992 /* we assume that error info is set by the thrower */
9993 hrc = err;
9994 }
9995 catch (...)
9996 {
9997 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9998 }
9999
10000 return hrc;
10001}
10002
10003/**
10004 * Saves the VM hardware configuration. It is assumed that the
10005 * given node is empty.
10006 *
10007 * @param data Reference to the settings object for the hardware config.
10008 * @param pDbg Pointer to the settings object for the debugging config
10009 * which happens to live in mHWData.
10010 * @param pAutostart Pointer to the settings object for the autostart config
10011 * which happens to live in mHWData.
10012 * @param recording Reference to reecording settings.
10013 */
10014HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10015 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10016{
10017 HRESULT hrc = S_OK;
10018
10019 try
10020 {
10021 /* The hardware version attribute (optional).
10022 Automatically upgrade from 1 to current default hardware version
10023 when there is no saved state. (ugly!) */
10024 if ( mHWData->mHWVersion == "1"
10025 && mSSData->strStateFilePath.isEmpty()
10026 )
10027 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10028
10029 data.strVersion = mHWData->mHWVersion;
10030 data.uuid = mHWData->mHardwareUUID;
10031
10032 // CPU
10033 data.cCPUs = mHWData->mCPUCount;
10034 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10035 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10036 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10037 data.strCpuProfile = mHWData->mCpuProfile;
10038
10039 data.llCpus.clear();
10040 if (data.fCpuHotPlug)
10041 {
10042 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10043 {
10044 if (mHWData->mCPUAttached[idx])
10045 {
10046 settings::Cpu cpu;
10047 cpu.ulId = idx;
10048 data.llCpus.push_back(cpu);
10049 }
10050 }
10051 }
10052
10053 // memory
10054 data.ulMemorySizeMB = mHWData->mMemorySize;
10055 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10056
10057 // HID
10058 data.pointingHIDType = mHWData->mPointingHIDType;
10059 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10060
10061 // paravirt
10062 data.paravirtProvider = mHWData->mParavirtProvider;
10063 data.strParavirtDebug = mHWData->mParavirtDebug;
10064
10065 // emulated USB card reader
10066 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10067
10068 // boot order
10069 data.mapBootOrder.clear();
10070 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10071 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10072
10073 /* VRDEServer settings (optional) */
10074 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10075 if (FAILED(hrc)) throw hrc;
10076
10077 /* Platform (required) */
10078 hrc = mPlatform->i_saveSettings(data.platformSettings);
10079 if (FAILED(hrc)) return hrc;
10080
10081 /* Firmware settings (required) */
10082 hrc = mFirmwareSettings->i_saveSettings(data.firmwareSettings);
10083 if (FAILED(hrc)) throw hrc;
10084
10085 /* Recording settings. */
10086 hrc = mRecordingSettings->i_saveSettings(recording);
10087 if (FAILED(hrc)) throw hrc;
10088
10089 /* Trusted Platform Module settings (required) */
10090 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10091 if (FAILED(hrc)) throw hrc;
10092
10093 /* NVRAM settings (required) */
10094 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10095 if (FAILED(hrc)) throw hrc;
10096
10097 /* GraphicsAdapter settings (required) */
10098 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10099 if (FAILED(hrc)) throw hrc;
10100
10101 /* USB Controller (required) */
10102 data.usbSettings.llUSBControllers.clear();
10103 for (USBControllerList::const_iterator
10104 it = mUSBControllers->begin();
10105 it != mUSBControllers->end();
10106 ++it)
10107 {
10108 ComObjPtr<USBController> ctrl = *it;
10109 settings::USBController settingsCtrl;
10110
10111 settingsCtrl.strName = ctrl->i_getName();
10112 settingsCtrl.enmType = ctrl->i_getControllerType();
10113
10114 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10115 }
10116
10117 /* USB device filters (required) */
10118 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10119 if (FAILED(hrc)) throw hrc;
10120
10121 /* Network adapters (required) */
10122 size_t const uMaxNICs =
10123 RT_MIN(PlatformProperties::s_getMaxNetworkAdapters(data.platformSettings.chipsetType), mNetworkAdapters.size());
10124 data.llNetworkAdapters.clear();
10125 /* Write out only the nominal number of network adapters for this
10126 * chipset type. Since Machine::commit() hasn't been called there
10127 * may be extra NIC settings in the vector. */
10128 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10129 {
10130 settings::NetworkAdapter nic;
10131 nic.ulSlot = (uint32_t)slot;
10132 /* paranoia check... must not be NULL, but must not crash either. */
10133 if (mNetworkAdapters[slot])
10134 {
10135 if (mNetworkAdapters[slot]->i_hasDefaults())
10136 continue;
10137
10138 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10139 if (FAILED(hrc)) throw hrc;
10140
10141 data.llNetworkAdapters.push_back(nic);
10142 }
10143 }
10144
10145 /* Serial ports */
10146 data.llSerialPorts.clear();
10147 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10148 {
10149 if (mSerialPorts[slot]->i_hasDefaults())
10150 continue;
10151
10152 settings::SerialPort s;
10153 s.ulSlot = slot;
10154 hrc = mSerialPorts[slot]->i_saveSettings(s);
10155 if (FAILED(hrc)) return hrc;
10156
10157 data.llSerialPorts.push_back(s);
10158 }
10159
10160 /* Parallel ports */
10161 data.llParallelPorts.clear();
10162 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10163 {
10164 if (mParallelPorts[slot]->i_hasDefaults())
10165 continue;
10166
10167 settings::ParallelPort p;
10168 p.ulSlot = slot;
10169 hrc = mParallelPorts[slot]->i_saveSettings(p);
10170 if (FAILED(hrc)) return hrc;
10171
10172 data.llParallelPorts.push_back(p);
10173 }
10174
10175 /* Audio settings */
10176 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10177 if (FAILED(hrc)) return hrc;
10178
10179 hrc = i_saveStorageControllers(data.storage);
10180 if (FAILED(hrc)) return hrc;
10181
10182 /* Shared folders */
10183 data.llSharedFolders.clear();
10184 for (HWData::SharedFolderList::const_iterator
10185 it = mHWData->mSharedFolders.begin();
10186 it != mHWData->mSharedFolders.end();
10187 ++it)
10188 {
10189 SharedFolder *pSF = *it;
10190 AutoCaller sfCaller(pSF);
10191 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10192 settings::SharedFolder sf;
10193 sf.strName = pSF->i_getName();
10194 sf.strHostPath = pSF->i_getHostPath();
10195 sf.fWritable = !!pSF->i_isWritable();
10196 sf.fAutoMount = !!pSF->i_isAutoMounted();
10197 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10198 sf.enmSymlinkPolicy = pSF->i_getSymlinkPolicy();
10199
10200 data.llSharedFolders.push_back(sf);
10201 }
10202
10203 // clipboard
10204 data.clipboardMode = mHWData->mClipboardMode;
10205 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10206
10207 // drag'n'drop
10208 data.dndMode = mHWData->mDnDMode;
10209
10210 /* Guest */
10211 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10212
10213 // IO settings
10214 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10215 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10216
10217 /* BandwidthControl (required) */
10218 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10219 if (FAILED(hrc)) throw hrc;
10220
10221 /* Host PCI devices */
10222 data.pciAttachments.clear();
10223 for (HWData::PCIDeviceAssignmentList::const_iterator
10224 it = mHWData->mPCIDeviceAssignments.begin();
10225 it != mHWData->mPCIDeviceAssignments.end();
10226 ++it)
10227 {
10228 ComObjPtr<PCIDeviceAttachment> pda = *it;
10229 settings::HostPCIDeviceAttachment hpda;
10230
10231 hrc = pda->i_saveSettings(hpda);
10232 if (FAILED(hrc)) throw hrc;
10233
10234 data.pciAttachments.push_back(hpda);
10235 }
10236
10237 // guest properties
10238 data.llGuestProperties.clear();
10239#ifdef VBOX_WITH_GUEST_PROPS
10240 for (HWData::GuestPropertyMap::const_iterator
10241 it = mHWData->mGuestProperties.begin();
10242 it != mHWData->mGuestProperties.end();
10243 ++it)
10244 {
10245 HWData::GuestProperty property = it->second;
10246
10247 /* Remove transient guest properties at shutdown unless we
10248 * are saving state. Note that restoring snapshot intentionally
10249 * keeps them, they will be removed if appropriate once the final
10250 * machine state is set (as crashes etc. need to work). */
10251 if ( ( mData->mMachineState == MachineState_PoweredOff
10252 || mData->mMachineState == MachineState_Aborted
10253 || mData->mMachineState == MachineState_Teleported)
10254 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10255 continue;
10256 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10257 prop.strName = it->first;
10258 prop.strValue = property.strValue;
10259 prop.timestamp = (uint64_t)property.mTimestamp;
10260 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10261 GuestPropWriteFlags(property.mFlags, szFlags);
10262 prop.strFlags = szFlags;
10263
10264 data.llGuestProperties.push_back(prop);
10265 }
10266
10267 /* I presume this doesn't require a backup(). */
10268 mData->mGuestPropertiesModified = FALSE;
10269#endif /* VBOX_WITH_GUEST_PROPS defined */
10270
10271 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10272 if (FAILED(hrc)) throw hrc;
10273
10274 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10275 *pAutostart = mHWData->mAutostart;
10276
10277 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10278 }
10279 catch (std::bad_alloc &)
10280 {
10281 return E_OUTOFMEMORY;
10282 }
10283
10284 AssertComRC(hrc);
10285 return hrc;
10286}
10287
10288/**
10289 * Saves the storage controller configuration.
10290 *
10291 * @param data storage settings.
10292 */
10293HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10294{
10295 data.llStorageControllers.clear();
10296
10297 for (StorageControllerList::const_iterator
10298 it = mStorageControllers->begin();
10299 it != mStorageControllers->end();
10300 ++it)
10301 {
10302 ComObjPtr<StorageController> pCtl = *it;
10303
10304 settings::StorageController ctl;
10305 ctl.strName = pCtl->i_getName();
10306 ctl.controllerType = pCtl->i_getControllerType();
10307 ctl.storageBus = pCtl->i_getStorageBus();
10308 ctl.ulInstance = pCtl->i_getInstance();
10309 ctl.fBootable = pCtl->i_getBootable();
10310
10311 /* Save the port count. */
10312 ULONG portCount;
10313 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10314 ComAssertComRCRet(hrc, hrc);
10315 ctl.ulPortCount = portCount;
10316
10317 /* Save fUseHostIOCache */
10318 BOOL fUseHostIOCache;
10319 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10320 ComAssertComRCRet(hrc, hrc);
10321 ctl.fUseHostIOCache = !!fUseHostIOCache;
10322
10323 /* save the devices now. */
10324 hrc = i_saveStorageDevices(pCtl, ctl);
10325 ComAssertComRCRet(hrc, hrc);
10326
10327 data.llStorageControllers.push_back(ctl);
10328 }
10329
10330 return S_OK;
10331}
10332
10333/**
10334 * Saves the hard disk configuration.
10335 */
10336HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10337 settings::StorageController &data)
10338{
10339 MediumAttachmentList atts;
10340
10341 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10342 if (FAILED(hrc)) return hrc;
10343
10344 data.llAttachedDevices.clear();
10345 for (MediumAttachmentList::const_iterator
10346 it = atts.begin();
10347 it != atts.end();
10348 ++it)
10349 {
10350 settings::AttachedDevice dev;
10351 IMediumAttachment *iA = *it;
10352 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10353 Medium *pMedium = pAttach->i_getMedium();
10354
10355 dev.deviceType = pAttach->i_getType();
10356 dev.lPort = pAttach->i_getPort();
10357 dev.lDevice = pAttach->i_getDevice();
10358 dev.fPassThrough = pAttach->i_getPassthrough();
10359 dev.fHotPluggable = pAttach->i_getHotPluggable();
10360 if (pMedium)
10361 {
10362 if (pMedium->i_isHostDrive())
10363 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10364 else
10365 dev.uuid = pMedium->i_getId();
10366 dev.fTempEject = pAttach->i_getTempEject();
10367 dev.fNonRotational = pAttach->i_getNonRotational();
10368 dev.fDiscard = pAttach->i_getDiscard();
10369 }
10370
10371 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10372
10373 data.llAttachedDevices.push_back(dev);
10374 }
10375
10376 return S_OK;
10377}
10378
10379/**
10380 * Saves machine state settings as defined by aFlags
10381 * (SaveSTS_* values).
10382 *
10383 * @param aFlags Combination of SaveSTS_* flags.
10384 *
10385 * @note Locks objects for writing.
10386 */
10387HRESULT Machine::i_saveStateSettings(int aFlags)
10388{
10389 if (aFlags == 0)
10390 return S_OK;
10391
10392 AutoCaller autoCaller(this);
10393 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10394
10395 /* This object's write lock is also necessary to serialize file access
10396 * (prevent concurrent reads and writes) */
10397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10398
10399 HRESULT hrc = S_OK;
10400
10401 Assert(mData->pMachineConfigFile);
10402
10403 try
10404 {
10405 if (aFlags & SaveSTS_CurStateModified)
10406 mData->pMachineConfigFile->fCurrentStateModified = true;
10407
10408 if (aFlags & SaveSTS_StateFilePath)
10409 {
10410 if (!mSSData->strStateFilePath.isEmpty())
10411 /* try to make the file name relative to the settings file dir */
10412 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10413 else
10414 mData->pMachineConfigFile->strStateFile.setNull();
10415 }
10416
10417 if (aFlags & SaveSTS_StateTimeStamp)
10418 {
10419 Assert( mData->mMachineState != MachineState_Aborted
10420 || mSSData->strStateFilePath.isEmpty());
10421
10422 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10423
10424 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10425 || mData->mMachineState == MachineState_AbortedSaved);
10426/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10427 }
10428
10429 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10430 }
10431 catch (...)
10432 {
10433 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10434 }
10435
10436 return hrc;
10437}
10438
10439/**
10440 * Ensures that the given medium is added to a media registry. If this machine
10441 * was created with 4.0 or later, then the machine registry is used. Otherwise
10442 * the global VirtualBox media registry is used.
10443 *
10444 * Caller must NOT hold machine lock, media tree or any medium locks!
10445 *
10446 * @param pMedium
10447 */
10448void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10449{
10450 /* Paranoia checks: do not hold machine or media tree locks. */
10451 AssertReturnVoid(!isWriteLockOnCurrentThread());
10452 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10453
10454 ComObjPtr<Medium> pBase;
10455 {
10456 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10457 pBase = pMedium->i_getBase();
10458 }
10459
10460 /* Paranoia checks: do not hold medium locks. */
10461 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10462 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10463
10464 // decide which medium registry to use now that the medium is attached:
10465 Guid uuid;
10466 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10467 if (fCanHaveOwnMediaRegistry)
10468 // machine XML is VirtualBox 4.0 or higher:
10469 uuid = i_getId(); // machine UUID
10470 else
10471 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10472
10473 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10474 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10475 if (pMedium->i_addRegistry(uuid))
10476 mParent->i_markRegistryModified(uuid);
10477
10478 /* For more complex hard disk structures it can happen that the base
10479 * medium isn't yet associated with any medium registry. Do that now. */
10480 if (pMedium != pBase)
10481 {
10482 /* Tree lock needed by Medium::addRegistryAll. */
10483 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10484 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10485 {
10486 treeLock.release();
10487 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10488 treeLock.acquire();
10489 }
10490 if (pBase->i_addRegistryAll(uuid))
10491 {
10492 treeLock.release();
10493 mParent->i_markRegistryModified(uuid);
10494 }
10495 }
10496}
10497
10498/**
10499 * Physically deletes a file belonging to a machine.
10500 *
10501 * @returns HRESULT
10502 * @retval VBOX_E_FILE_ERROR on failure.
10503 * @param strFile File to delete.
10504 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
10505 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
10506 * @param strWhat File hint which will be used when setting an error. Optional.
10507 * @param prc Where to return IPRT's status code on failure.
10508 * Optional and can be NULL.
10509 */
10510HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
10511 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
10512{
10513 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
10514
10515 HRESULT hrc = S_OK;
10516
10517 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
10518
10519 int vrc = RTFileDelete(strFile.c_str());
10520 if (RT_FAILURE(vrc))
10521 {
10522 if ( !fIgnoreFailures
10523 /* Don't (externally) bitch about stuff which doesn't exist. */
10524 && ( vrc != VERR_FILE_NOT_FOUND
10525 && vrc != VERR_PATH_NOT_FOUND
10526 )
10527 )
10528 {
10529 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
10530
10531 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
10532 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
10533 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
10534 }
10535 }
10536
10537 if (prc)
10538 *prc = vrc;
10539 return hrc;
10540}
10541
10542/**
10543 * Creates differencing hard disks for all normal hard disks attached to this
10544 * machine and a new set of attachments to refer to created disks.
10545 *
10546 * Used when taking a snapshot or when deleting the current state. Gets called
10547 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10548 *
10549 * This method assumes that mMediumAttachments contains the original hard disk
10550 * attachments it needs to create diffs for. On success, these attachments will
10551 * be replaced with the created diffs.
10552 *
10553 * Attachments with non-normal hard disks are left as is.
10554 *
10555 * If @a aOnline is @c false then the original hard disks that require implicit
10556 * diffs will be locked for reading. Otherwise it is assumed that they are
10557 * already locked for writing (when the VM was started). Note that in the latter
10558 * case it is responsibility of the caller to lock the newly created diffs for
10559 * writing if this method succeeds.
10560 *
10561 * @param aProgress Progress object to run (must contain at least as
10562 * many operations left as the number of hard disks
10563 * attached).
10564 * @param aWeight Weight of this operation.
10565 * @param aOnline Whether the VM was online prior to this operation.
10566 *
10567 * @note The progress object is not marked as completed, neither on success nor
10568 * on failure. This is a responsibility of the caller.
10569 *
10570 * @note Locks this object and the media tree for writing.
10571 */
10572HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10573 ULONG aWeight,
10574 bool aOnline)
10575{
10576 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10577
10578 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10579 AssertReturn(!!pProgressControl, E_INVALIDARG);
10580
10581 AutoCaller autoCaller(this);
10582 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10583
10584 AutoMultiWriteLock2 alock(this->lockHandle(),
10585 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10586
10587 /* must be in a protective state because we release the lock below */
10588 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10589 || mData->mMachineState == MachineState_OnlineSnapshotting
10590 || mData->mMachineState == MachineState_LiveSnapshotting
10591 || mData->mMachineState == MachineState_RestoringSnapshot
10592 || mData->mMachineState == MachineState_DeletingSnapshot
10593 , E_FAIL);
10594
10595 HRESULT hrc = S_OK;
10596
10597 // use appropriate locked media map (online or offline)
10598 MediumLockListMap lockedMediaOffline;
10599 MediumLockListMap *lockedMediaMap;
10600 if (aOnline)
10601 lockedMediaMap = &mData->mSession.mLockedMedia;
10602 else
10603 lockedMediaMap = &lockedMediaOffline;
10604
10605 try
10606 {
10607 if (!aOnline)
10608 {
10609 /* lock all attached hard disks early to detect "in use"
10610 * situations before creating actual diffs */
10611 for (MediumAttachmentList::const_iterator
10612 it = mMediumAttachments->begin();
10613 it != mMediumAttachments->end();
10614 ++it)
10615 {
10616 MediumAttachment *pAtt = *it;
10617 if (pAtt->i_getType() == DeviceType_HardDisk)
10618 {
10619 Medium *pMedium = pAtt->i_getMedium();
10620 Assert(pMedium);
10621
10622 MediumLockList *pMediumLockList(new MediumLockList());
10623 alock.release();
10624 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10625 NULL /* pToLockWrite */,
10626 false /* fMediumLockWriteAll */,
10627 NULL,
10628 *pMediumLockList);
10629 alock.acquire();
10630 if (FAILED(hrc))
10631 {
10632 delete pMediumLockList;
10633 throw hrc;
10634 }
10635 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10636 if (FAILED(hrc))
10637 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
10638 }
10639 }
10640
10641 /* Now lock all media. If this fails, nothing is locked. */
10642 alock.release();
10643 hrc = lockedMediaMap->Lock();
10644 alock.acquire();
10645 if (FAILED(hrc))
10646 throw setError(hrc, tr("Locking of attached media failed"));
10647 }
10648
10649 /* remember the current list (note that we don't use backup() since
10650 * mMediumAttachments may be already backed up) */
10651 MediumAttachmentList atts = *mMediumAttachments.data();
10652
10653 /* start from scratch */
10654 mMediumAttachments->clear();
10655
10656 /* go through remembered attachments and create diffs for normal hard
10657 * disks and attach them */
10658 for (MediumAttachmentList::const_iterator
10659 it = atts.begin();
10660 it != atts.end();
10661 ++it)
10662 {
10663 MediumAttachment *pAtt = *it;
10664
10665 DeviceType_T devType = pAtt->i_getType();
10666 Medium *pMedium = pAtt->i_getMedium();
10667
10668 if ( devType != DeviceType_HardDisk
10669 || pMedium == NULL
10670 || pMedium->i_getType() != MediumType_Normal)
10671 {
10672 /* copy the attachment as is */
10673
10674 /** @todo the progress object created in SessionMachine::TakeSnaphot
10675 * only expects operations for hard disks. Later other
10676 * device types need to show up in the progress as well. */
10677 if (devType == DeviceType_HardDisk)
10678 {
10679 if (pMedium == NULL)
10680 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10681 aWeight); // weight
10682 else
10683 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10684 pMedium->i_getBase()->i_getName().c_str()).raw(),
10685 aWeight); // weight
10686 }
10687
10688 mMediumAttachments->push_back(pAtt);
10689 continue;
10690 }
10691
10692 /* need a diff */
10693 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10694 pMedium->i_getBase()->i_getName().c_str()).raw(),
10695 aWeight); // weight
10696
10697 Utf8Str strFullSnapshotFolder;
10698 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10699
10700 ComObjPtr<Medium> diff;
10701 diff.createObject();
10702 // store the diff in the same registry as the parent
10703 // (this cannot fail here because we can't create implicit diffs for
10704 // unregistered images)
10705 Guid uuidRegistryParent;
10706 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10707 Assert(fInRegistry); NOREF(fInRegistry);
10708 hrc = diff->init(mParent,
10709 pMedium->i_getPreferredDiffFormat(),
10710 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10711 uuidRegistryParent,
10712 DeviceType_HardDisk);
10713 if (FAILED(hrc)) throw hrc;
10714
10715 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10716 * the push_back? Looks like we're going to release medium with the
10717 * wrong kind of lock (general issue with if we fail anywhere at all)
10718 * and an orphaned VDI in the snapshots folder. */
10719
10720 /* update the appropriate lock list */
10721 MediumLockList *pMediumLockList;
10722 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
10723 AssertComRCThrowRC(hrc);
10724 if (aOnline)
10725 {
10726 alock.release();
10727 /* The currently attached medium will be read-only, change
10728 * the lock type to read. */
10729 hrc = pMediumLockList->Update(pMedium, false);
10730 alock.acquire();
10731 AssertComRCThrowRC(hrc);
10732 }
10733
10734 /* release the locks before the potentially lengthy operation */
10735 alock.release();
10736 hrc = pMedium->i_createDiffStorage(diff,
10737 pMedium->i_getPreferredDiffVariant(),
10738 pMediumLockList,
10739 NULL /* aProgress */,
10740 true /* aWait */,
10741 false /* aNotify */);
10742 alock.acquire();
10743 if (FAILED(hrc)) throw hrc;
10744
10745 /* actual lock list update is done in Machine::i_commitMedia */
10746
10747 hrc = diff->i_addBackReference(mData->mUuid);
10748 AssertComRCThrowRC(hrc);
10749
10750 /* add a new attachment */
10751 ComObjPtr<MediumAttachment> attachment;
10752 attachment.createObject();
10753 hrc = attachment->init(this,
10754 diff,
10755 pAtt->i_getControllerName(),
10756 pAtt->i_getPort(),
10757 pAtt->i_getDevice(),
10758 DeviceType_HardDisk,
10759 true /* aImplicit */,
10760 false /* aPassthrough */,
10761 false /* aTempEject */,
10762 pAtt->i_getNonRotational(),
10763 pAtt->i_getDiscard(),
10764 pAtt->i_getHotPluggable(),
10765 pAtt->i_getBandwidthGroup());
10766 if (FAILED(hrc)) throw hrc;
10767
10768 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10769 AssertComRCThrowRC(hrc);
10770 mMediumAttachments->push_back(attachment);
10771 }
10772 }
10773 catch (HRESULT hrcXcpt)
10774 {
10775 hrc = hrcXcpt;
10776 }
10777
10778 /* unlock all hard disks we locked when there is no VM */
10779 if (!aOnline)
10780 {
10781 ErrorInfoKeeper eik;
10782
10783 HRESULT hrc2 = lockedMediaMap->Clear();
10784 AssertComRC(hrc2);
10785 }
10786
10787 return hrc;
10788}
10789
10790/**
10791 * Deletes implicit differencing hard disks created either by
10792 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10793 * mMediumAttachments.
10794 *
10795 * Note that to delete hard disks created by #attachDevice() this method is
10796 * called from #i_rollbackMedia() when the changes are rolled back.
10797 *
10798 * @note Locks this object and the media tree for writing.
10799 */
10800HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10801{
10802 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10803
10804 AutoCaller autoCaller(this);
10805 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
10806
10807 AutoMultiWriteLock2 alock(this->lockHandle(),
10808 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10809
10810 /* We absolutely must have backed up state. */
10811 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10812
10813 /* Check if there are any implicitly created diff images. */
10814 bool fImplicitDiffs = false;
10815 for (MediumAttachmentList::const_iterator
10816 it = mMediumAttachments->begin();
10817 it != mMediumAttachments->end();
10818 ++it)
10819 {
10820 const ComObjPtr<MediumAttachment> &pAtt = *it;
10821 if (pAtt->i_isImplicit())
10822 {
10823 fImplicitDiffs = true;
10824 break;
10825 }
10826 }
10827 /* If there is nothing to do, leave early. This saves lots of image locking
10828 * effort. It also avoids a MachineStateChanged event without real reason.
10829 * This is important e.g. when loading a VM config, because there should be
10830 * no events. Otherwise API clients can become thoroughly confused for
10831 * inaccessible VMs (the code for loading VM configs uses this method for
10832 * cleanup if the config makes no sense), as they take such events as an
10833 * indication that the VM is alive, and they would force the VM config to
10834 * be reread, leading to an endless loop. */
10835 if (!fImplicitDiffs)
10836 return S_OK;
10837
10838 HRESULT hrc = S_OK;
10839 MachineState_T oldState = mData->mMachineState;
10840
10841 /* will release the lock before the potentially lengthy operation,
10842 * so protect with the special state (unless already protected) */
10843 if ( oldState != MachineState_Snapshotting
10844 && oldState != MachineState_OnlineSnapshotting
10845 && oldState != MachineState_LiveSnapshotting
10846 && oldState != MachineState_RestoringSnapshot
10847 && oldState != MachineState_DeletingSnapshot
10848 && oldState != MachineState_DeletingSnapshotOnline
10849 && oldState != MachineState_DeletingSnapshotPaused
10850 )
10851 i_setMachineState(MachineState_SettingUp);
10852
10853 // use appropriate locked media map (online or offline)
10854 MediumLockListMap lockedMediaOffline;
10855 MediumLockListMap *lockedMediaMap;
10856 if (aOnline)
10857 lockedMediaMap = &mData->mSession.mLockedMedia;
10858 else
10859 lockedMediaMap = &lockedMediaOffline;
10860
10861 try
10862 {
10863 if (!aOnline)
10864 {
10865 /* lock all attached hard disks early to detect "in use"
10866 * situations before deleting actual diffs */
10867 for (MediumAttachmentList::const_iterator
10868 it = mMediumAttachments->begin();
10869 it != mMediumAttachments->end();
10870 ++it)
10871 {
10872 MediumAttachment *pAtt = *it;
10873 if (pAtt->i_getType() == DeviceType_HardDisk)
10874 {
10875 Medium *pMedium = pAtt->i_getMedium();
10876 Assert(pMedium);
10877
10878 MediumLockList *pMediumLockList(new MediumLockList());
10879 alock.release();
10880 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10881 NULL /* pToLockWrite */,
10882 false /* fMediumLockWriteAll */,
10883 NULL,
10884 *pMediumLockList);
10885 alock.acquire();
10886
10887 if (FAILED(hrc))
10888 {
10889 delete pMediumLockList;
10890 throw hrc;
10891 }
10892
10893 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10894 if (FAILED(hrc))
10895 throw hrc;
10896 }
10897 }
10898
10899 if (FAILED(hrc))
10900 throw hrc;
10901 } // end of offline
10902
10903 /* Lock lists are now up to date and include implicitly created media */
10904
10905 /* Go through remembered attachments and delete all implicitly created
10906 * diffs and fix up the attachment information */
10907 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10908 MediumAttachmentList implicitAtts;
10909 for (MediumAttachmentList::const_iterator
10910 it = mMediumAttachments->begin();
10911 it != mMediumAttachments->end();
10912 ++it)
10913 {
10914 ComObjPtr<MediumAttachment> pAtt = *it;
10915 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10916 if (pMedium.isNull())
10917 continue;
10918
10919 // Implicit attachments go on the list for deletion and back references are removed.
10920 if (pAtt->i_isImplicit())
10921 {
10922 /* Deassociate and mark for deletion */
10923 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10924 hrc = pMedium->i_removeBackReference(mData->mUuid);
10925 if (FAILED(hrc))
10926 throw hrc;
10927 implicitAtts.push_back(pAtt);
10928 continue;
10929 }
10930
10931 /* Was this medium attached before? */
10932 if (!i_findAttachment(oldAtts, pMedium))
10933 {
10934 /* no: de-associate */
10935 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10936 hrc = pMedium->i_removeBackReference(mData->mUuid);
10937 if (FAILED(hrc))
10938 throw hrc;
10939 continue;
10940 }
10941 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10942 }
10943
10944 /* If there are implicit attachments to delete, throw away the lock
10945 * map contents (which will unlock all media) since the medium
10946 * attachments will be rolled back. Below we need to completely
10947 * recreate the lock map anyway since it is infinitely complex to
10948 * do this incrementally (would need reconstructing each attachment
10949 * change, which would be extremely hairy). */
10950 if (implicitAtts.size() != 0)
10951 {
10952 ErrorInfoKeeper eik;
10953
10954 HRESULT hrc2 = lockedMediaMap->Clear();
10955 AssertComRC(hrc2);
10956 }
10957
10958 /* rollback hard disk changes */
10959 mMediumAttachments.rollback();
10960
10961 MultiResult mrc(S_OK);
10962
10963 // Delete unused implicit diffs.
10964 if (implicitAtts.size() != 0)
10965 {
10966 alock.release();
10967
10968 for (MediumAttachmentList::const_iterator
10969 it = implicitAtts.begin();
10970 it != implicitAtts.end();
10971 ++it)
10972 {
10973 // Remove medium associated with this attachment.
10974 ComObjPtr<MediumAttachment> pAtt = *it;
10975 Assert(pAtt);
10976 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10977 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10978 Assert(pMedium);
10979
10980 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10981 // continue on delete failure, just collect error messages
10982 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
10983 pMedium->i_getLocationFull().c_str() ));
10984 mrc = hrc;
10985 }
10986 // Clear the list of deleted implicit attachments now, while not
10987 // holding the lock, as it will ultimately trigger Medium::uninit()
10988 // calls which assume that the media tree lock isn't held.
10989 implicitAtts.clear();
10990
10991 alock.acquire();
10992
10993 /* if there is a VM recreate media lock map as mentioned above,
10994 * otherwise it is a waste of time and we leave things unlocked */
10995 if (aOnline)
10996 {
10997 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10998 /* must never be NULL, but better safe than sorry */
10999 if (!pMachine.isNull())
11000 {
11001 alock.release();
11002 hrc = mData->mSession.mMachine->i_lockMedia();
11003 alock.acquire();
11004 if (FAILED(hrc))
11005 throw hrc;
11006 }
11007 }
11008 }
11009 }
11010 catch (HRESULT hrcXcpt)
11011 {
11012 hrc = hrcXcpt;
11013 }
11014
11015 if (mData->mMachineState == MachineState_SettingUp)
11016 i_setMachineState(oldState);
11017
11018 /* unlock all hard disks we locked when there is no VM */
11019 if (!aOnline)
11020 {
11021 ErrorInfoKeeper eik;
11022
11023 HRESULT hrc2 = lockedMediaMap->Clear();
11024 AssertComRC(hrc2);
11025 }
11026
11027 return hrc;
11028}
11029
11030
11031/**
11032 * Looks through the given list of media attachments for one with the given parameters
11033 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11034 * can be searched as well if needed.
11035 *
11036 * @param ll
11037 * @param aControllerName
11038 * @param aControllerPort
11039 * @param aDevice
11040 * @return
11041 */
11042MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11043 const Utf8Str &aControllerName,
11044 LONG aControllerPort,
11045 LONG aDevice)
11046{
11047 for (MediumAttachmentList::const_iterator
11048 it = ll.begin();
11049 it != ll.end();
11050 ++it)
11051 {
11052 MediumAttachment *pAttach = *it;
11053 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11054 return pAttach;
11055 }
11056
11057 return NULL;
11058}
11059
11060/**
11061 * Looks through the given list of media attachments for one with the given parameters
11062 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11063 * can be searched as well if needed.
11064 *
11065 * @param ll
11066 * @param pMedium
11067 * @return
11068 */
11069MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11070 ComObjPtr<Medium> pMedium)
11071{
11072 for (MediumAttachmentList::const_iterator
11073 it = ll.begin();
11074 it != ll.end();
11075 ++it)
11076 {
11077 MediumAttachment *pAttach = *it;
11078 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11079 if (pMediumThis == pMedium)
11080 return pAttach;
11081 }
11082
11083 return NULL;
11084}
11085
11086/**
11087 * Looks through the given list of media attachments for one with the given parameters
11088 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11089 * can be searched as well if needed.
11090 *
11091 * @param ll
11092 * @param id
11093 * @return
11094 */
11095MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11096 Guid &id)
11097{
11098 for (MediumAttachmentList::const_iterator
11099 it = ll.begin();
11100 it != ll.end();
11101 ++it)
11102 {
11103 MediumAttachment *pAttach = *it;
11104 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11105 if (pMediumThis->i_getId() == id)
11106 return pAttach;
11107 }
11108
11109 return NULL;
11110}
11111
11112/**
11113 * Main implementation for Machine::DetachDevice. This also gets called
11114 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11115 *
11116 * @param pAttach Medium attachment to detach.
11117 * @param writeLock Machine write lock which the caller must have locked once.
11118 * This may be released temporarily in here.
11119 * @param pSnapshot If NULL, then the detachment is for the current machine.
11120 * Otherwise this is for a SnapshotMachine, and this must be
11121 * its snapshot.
11122 * @return
11123 */
11124HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11125 AutoWriteLock &writeLock,
11126 Snapshot *pSnapshot)
11127{
11128 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11129 DeviceType_T mediumType = pAttach->i_getType();
11130
11131 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11132
11133 if (pAttach->i_isImplicit())
11134 {
11135 /* attempt to implicitly delete the implicitly created diff */
11136
11137 /// @todo move the implicit flag from MediumAttachment to Medium
11138 /// and forbid any hard disk operation when it is implicit. Or maybe
11139 /// a special media state for it to make it even more simple.
11140
11141 Assert(mMediumAttachments.isBackedUp());
11142
11143 /* will release the lock before the potentially lengthy operation, so
11144 * protect with the special state */
11145 MachineState_T oldState = mData->mMachineState;
11146 i_setMachineState(MachineState_SettingUp);
11147
11148 writeLock.release();
11149
11150 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11151
11152 writeLock.acquire();
11153
11154 i_setMachineState(oldState);
11155
11156 if (FAILED(hrc)) return hrc;
11157 }
11158
11159 i_setModified(IsModified_Storage);
11160 mMediumAttachments.backup();
11161 mMediumAttachments->remove(pAttach);
11162
11163 if (!oldmedium.isNull())
11164 {
11165 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11166 if (pSnapshot)
11167 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11168 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11169 else if (mediumType != DeviceType_HardDisk)
11170 oldmedium->i_removeBackReference(mData->mUuid);
11171 }
11172
11173 return S_OK;
11174}
11175
11176/**
11177 * Goes thru all media of the given list and
11178 *
11179 * 1) calls i_detachDevice() on each of them for this machine and
11180 * 2) adds all Medium objects found in the process to the given list,
11181 * depending on cleanupMode.
11182 *
11183 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11184 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11185 * media to the list.
11186 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11187 * also removable media if they are located in the VM folder and referenced
11188 * only by this VM (media prepared by unattended installer).
11189 *
11190 * This gets called from Machine::Unregister, both for the actual Machine and
11191 * the SnapshotMachine objects that might be found in the snapshots.
11192 *
11193 * Requires caller and locking. The machine lock must be passed in because it
11194 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11195 *
11196 * @param writeLock Machine lock from top-level caller; this gets passed to
11197 * i_detachDevice.
11198 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11199 * object if called for a SnapshotMachine.
11200 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11201 * added to llMedia; if Full, then all media get added;
11202 * otherwise no media get added.
11203 * @param llMedia Caller's list to receive Medium objects which got detached so
11204 * caller can close() them, depending on cleanupMode.
11205 * @return
11206 */
11207HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11208 Snapshot *pSnapshot,
11209 CleanupMode_T cleanupMode,
11210 MediaList &llMedia)
11211{
11212 Assert(isWriteLockOnCurrentThread());
11213
11214 HRESULT hrc;
11215
11216 // make a temporary list because i_detachDevice invalidates iterators into
11217 // mMediumAttachments
11218 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11219
11220 for (MediumAttachmentList::iterator
11221 it = llAttachments2.begin();
11222 it != llAttachments2.end();
11223 ++it)
11224 {
11225 ComObjPtr<MediumAttachment> &pAttach = *it;
11226 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11227
11228 if (!pMedium.isNull())
11229 {
11230 AutoCaller mac(pMedium);
11231 if (FAILED(mac.hrc())) return mac.hrc();
11232 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11233 DeviceType_T devType = pMedium->i_getDeviceType();
11234 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11235 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11236 strMediumLocation.stripFilename();
11237 Utf8Str strMachineFolder = i_getSettingsFileFull();
11238 strMachineFolder.stripFilename();
11239 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11240 && devType == DeviceType_HardDisk)
11241 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11242 && ( devType == DeviceType_HardDisk
11243 || ( cBackRefs <= 1
11244 && strMediumLocation == strMachineFolder
11245 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11246 || (cleanupMode == CleanupMode_Full)
11247 )
11248 {
11249 llMedia.push_back(pMedium);
11250 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11251 /* Not allowed to keep this lock as below we need the parent
11252 * medium lock, and the lock order is parent to child. */
11253 lock.release();
11254 /*
11255 * Search for media which are not attached to any machine, but
11256 * in the chain to an attached disk. Media are only consided
11257 * if they are:
11258 * - have only one child
11259 * - no references to any machines
11260 * - are of normal medium type
11261 */
11262 while (!pParent.isNull())
11263 {
11264 AutoCaller mac1(pParent);
11265 if (FAILED(mac1.hrc())) return mac1.hrc();
11266 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11267 if (pParent->i_getChildren().size() == 1)
11268 {
11269 if ( pParent->i_getMachineBackRefCount() == 0
11270 && pParent->i_getType() == MediumType_Normal
11271 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11272 llMedia.push_back(pParent);
11273 }
11274 else
11275 break;
11276 pParent = pParent->i_getParent();
11277 }
11278 }
11279 }
11280
11281 // real machine: then we need to use the proper method
11282 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11283
11284 if (FAILED(hrc))
11285 return hrc;
11286 }
11287
11288 return S_OK;
11289}
11290
11291/**
11292 * Perform deferred hard disk detachments.
11293 *
11294 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11295 * changed (not backed up).
11296 *
11297 * If @a aOnline is @c true then this method will also unlock the old hard
11298 * disks for which the new implicit diffs were created and will lock these new
11299 * diffs for writing.
11300 *
11301 * @param aOnline Whether the VM was online prior to this operation.
11302 *
11303 * @note Locks this object for writing!
11304 */
11305void Machine::i_commitMedia(bool aOnline /*= false*/)
11306{
11307 AutoCaller autoCaller(this);
11308 AssertComRCReturnVoid(autoCaller.hrc());
11309
11310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11311
11312 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11313
11314 HRESULT hrc = S_OK;
11315
11316 /* no attach/detach operations -- nothing to do */
11317 if (!mMediumAttachments.isBackedUp())
11318 return;
11319
11320 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11321 bool fMediaNeedsLocking = false;
11322
11323 /* enumerate new attachments */
11324 for (MediumAttachmentList::const_iterator
11325 it = mMediumAttachments->begin();
11326 it != mMediumAttachments->end();
11327 ++it)
11328 {
11329 MediumAttachment *pAttach = *it;
11330
11331 pAttach->i_commit();
11332
11333 Medium *pMedium = pAttach->i_getMedium();
11334 bool fImplicit = pAttach->i_isImplicit();
11335
11336 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11337 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11338 fImplicit));
11339
11340 /** @todo convert all this Machine-based voodoo to MediumAttachment
11341 * based commit logic. */
11342 if (fImplicit)
11343 {
11344 /* convert implicit attachment to normal */
11345 pAttach->i_setImplicit(false);
11346
11347 if ( aOnline
11348 && pMedium
11349 && pAttach->i_getType() == DeviceType_HardDisk
11350 )
11351 {
11352 /* update the appropriate lock list */
11353 MediumLockList *pMediumLockList;
11354 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11355 AssertComRC(hrc);
11356 if (pMediumLockList)
11357 {
11358 /* unlock if there's a need to change the locking */
11359 if (!fMediaNeedsLocking)
11360 {
11361 Assert(mData->mSession.mLockedMedia.IsLocked());
11362 hrc = mData->mSession.mLockedMedia.Unlock();
11363 AssertComRC(hrc);
11364 fMediaNeedsLocking = true;
11365 }
11366 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
11367 AssertComRC(hrc);
11368 hrc = pMediumLockList->Append(pMedium, true);
11369 AssertComRC(hrc);
11370 }
11371 }
11372
11373 continue;
11374 }
11375
11376 if (pMedium)
11377 {
11378 /* was this medium attached before? */
11379 for (MediumAttachmentList::iterator
11380 oldIt = oldAtts.begin();
11381 oldIt != oldAtts.end();
11382 ++oldIt)
11383 {
11384 MediumAttachment *pOldAttach = *oldIt;
11385 if (pOldAttach->i_getMedium() == pMedium)
11386 {
11387 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11388
11389 /* yes: remove from old to avoid de-association */
11390 oldAtts.erase(oldIt);
11391 break;
11392 }
11393 }
11394 }
11395 }
11396
11397 /* enumerate remaining old attachments and de-associate from the
11398 * current machine state */
11399 for (MediumAttachmentList::const_iterator
11400 it = oldAtts.begin();
11401 it != oldAtts.end();
11402 ++it)
11403 {
11404 MediumAttachment *pAttach = *it;
11405 Medium *pMedium = pAttach->i_getMedium();
11406
11407 /* Detach only hard disks, since DVD/floppy media is detached
11408 * instantly in MountMedium. */
11409 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11410 {
11411 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11412
11413 /* now de-associate from the current machine state */
11414 hrc = pMedium->i_removeBackReference(mData->mUuid);
11415 AssertComRC(hrc);
11416
11417 if (aOnline)
11418 {
11419 /* unlock since medium is not used anymore */
11420 MediumLockList *pMediumLockList;
11421 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11422 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
11423 {
11424 /* this happens for online snapshots, there the attachment
11425 * is changing, but only to a diff image created under
11426 * the old one, so there is no separate lock list */
11427 Assert(!pMediumLockList);
11428 }
11429 else
11430 {
11431 AssertComRC(hrc);
11432 if (pMediumLockList)
11433 {
11434 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
11435 AssertComRC(hrc);
11436 }
11437 }
11438 }
11439 }
11440 }
11441
11442 /* take media locks again so that the locking state is consistent */
11443 if (fMediaNeedsLocking)
11444 {
11445 Assert(aOnline);
11446 hrc = mData->mSession.mLockedMedia.Lock();
11447 AssertComRC(hrc);
11448 }
11449
11450 /* commit the hard disk changes */
11451 mMediumAttachments.commit();
11452
11453 if (i_isSessionMachine())
11454 {
11455 /*
11456 * Update the parent machine to point to the new owner.
11457 * This is necessary because the stored parent will point to the
11458 * session machine otherwise and cause crashes or errors later
11459 * when the session machine gets invalid.
11460 */
11461 /** @todo Change the MediumAttachment class to behave like any other
11462 * class in this regard by creating peer MediumAttachment
11463 * objects for session machines and share the data with the peer
11464 * machine.
11465 */
11466 for (MediumAttachmentList::const_iterator
11467 it = mMediumAttachments->begin();
11468 it != mMediumAttachments->end();
11469 ++it)
11470 (*it)->i_updateParentMachine(mPeer);
11471
11472 /* attach new data to the primary machine and reshare it */
11473 mPeer->mMediumAttachments.attach(mMediumAttachments);
11474 }
11475
11476 return;
11477}
11478
11479/**
11480 * Perform deferred deletion of implicitly created diffs.
11481 *
11482 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11483 * changed (not backed up).
11484 *
11485 * @note Locks this object for writing!
11486 */
11487void Machine::i_rollbackMedia()
11488{
11489 AutoCaller autoCaller(this);
11490 AssertComRCReturnVoid(autoCaller.hrc());
11491
11492 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11493 LogFlowThisFunc(("Entering rollbackMedia\n"));
11494
11495 HRESULT hrc = S_OK;
11496
11497 /* no attach/detach operations -- nothing to do */
11498 if (!mMediumAttachments.isBackedUp())
11499 return;
11500
11501 /* enumerate new attachments */
11502 for (MediumAttachmentList::const_iterator
11503 it = mMediumAttachments->begin();
11504 it != mMediumAttachments->end();
11505 ++it)
11506 {
11507 MediumAttachment *pAttach = *it;
11508 /* Fix up the backrefs for DVD/floppy media. */
11509 if (pAttach->i_getType() != DeviceType_HardDisk)
11510 {
11511 Medium *pMedium = pAttach->i_getMedium();
11512 if (pMedium)
11513 {
11514 hrc = pMedium->i_removeBackReference(mData->mUuid);
11515 AssertComRC(hrc);
11516 }
11517 }
11518
11519 (*it)->i_rollback();
11520
11521 pAttach = *it;
11522 /* Fix up the backrefs for DVD/floppy media. */
11523 if (pAttach->i_getType() != DeviceType_HardDisk)
11524 {
11525 Medium *pMedium = pAttach->i_getMedium();
11526 if (pMedium)
11527 {
11528 hrc = pMedium->i_addBackReference(mData->mUuid);
11529 AssertComRC(hrc);
11530 }
11531 }
11532 }
11533
11534 /** @todo convert all this Machine-based voodoo to MediumAttachment
11535 * based rollback logic. */
11536 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11537
11538 return;
11539}
11540
11541/**
11542 * Returns true if the settings file is located in the directory named exactly
11543 * as the machine; this means, among other things, that the machine directory
11544 * should be auto-renamed.
11545 *
11546 * @param aSettingsDir if not NULL, the full machine settings file directory
11547 * name will be assigned there.
11548 *
11549 * @note Doesn't lock anything.
11550 * @note Not thread safe (must be called from this object's lock).
11551 */
11552bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11553{
11554 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11555 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11556 if (aSettingsDir)
11557 *aSettingsDir = strMachineDirName;
11558 strMachineDirName.stripPath(); // vmname
11559 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11560 strConfigFileOnly.stripPath() // vmname.vbox
11561 .stripSuffix(); // vmname
11562 /** @todo hack, make somehow use of ComposeMachineFilename */
11563 if (mUserData->s.fDirectoryIncludesUUID)
11564 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11565
11566 AssertReturn(!strMachineDirName.isEmpty(), false);
11567 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11568
11569 return strMachineDirName == strConfigFileOnly;
11570}
11571
11572/**
11573 * Discards all changes to machine settings.
11574 *
11575 * @param aNotify Whether to notify the direct session about changes or not.
11576 *
11577 * @note Locks objects for writing!
11578 */
11579void Machine::i_rollback(bool aNotify)
11580{
11581 AutoCaller autoCaller(this);
11582 AssertComRCReturn(autoCaller.hrc(), (void)0);
11583
11584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11585
11586 if (!mStorageControllers.isNull())
11587 {
11588 if (mStorageControllers.isBackedUp())
11589 {
11590 /* unitialize all new devices (absent in the backed up list). */
11591 StorageControllerList *backedList = mStorageControllers.backedUpData();
11592 for (StorageControllerList::const_iterator
11593 it = mStorageControllers->begin();
11594 it != mStorageControllers->end();
11595 ++it)
11596 {
11597 if ( std::find(backedList->begin(), backedList->end(), *it)
11598 == backedList->end()
11599 )
11600 {
11601 (*it)->uninit();
11602 }
11603 }
11604
11605 /* restore the list */
11606 mStorageControllers.rollback();
11607 }
11608
11609 /* rollback any changes to devices after restoring the list */
11610 if (mData->flModifications & IsModified_Storage)
11611 {
11612 for (StorageControllerList::const_iterator
11613 it = mStorageControllers->begin();
11614 it != mStorageControllers->end();
11615 ++it)
11616 {
11617 (*it)->i_rollback();
11618 }
11619 }
11620 }
11621
11622 if (!mUSBControllers.isNull())
11623 {
11624 if (mUSBControllers.isBackedUp())
11625 {
11626 /* unitialize all new devices (absent in the backed up list). */
11627 USBControllerList *backedList = mUSBControllers.backedUpData();
11628 for (USBControllerList::const_iterator
11629 it = mUSBControllers->begin();
11630 it != mUSBControllers->end();
11631 ++it)
11632 {
11633 if ( std::find(backedList->begin(), backedList->end(), *it)
11634 == backedList->end()
11635 )
11636 {
11637 (*it)->uninit();
11638 }
11639 }
11640
11641 /* restore the list */
11642 mUSBControllers.rollback();
11643 }
11644
11645 /* rollback any changes to devices after restoring the list */
11646 if (mData->flModifications & IsModified_USB)
11647 {
11648 for (USBControllerList::const_iterator
11649 it = mUSBControllers->begin();
11650 it != mUSBControllers->end();
11651 ++it)
11652 {
11653 (*it)->i_rollback();
11654 }
11655 }
11656 }
11657
11658 mUserData.rollback();
11659
11660 mHWData.rollback();
11661
11662 if (mData->flModifications & IsModified_Storage)
11663 i_rollbackMedia();
11664
11665 if (mPlatform)
11666 {
11667 mPlatform->i_rollback();
11668 i_platformPropertiesUpdate();
11669 }
11670
11671 if (mFirmwareSettings)
11672 mFirmwareSettings->i_rollback();
11673
11674 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11675 mRecordingSettings->i_rollback();
11676
11677 if (mTrustedPlatformModule)
11678 mTrustedPlatformModule->i_rollback();
11679
11680 if (mNvramStore)
11681 mNvramStore->i_rollback();
11682
11683 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11684 mGraphicsAdapter->i_rollback();
11685
11686 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11687 mVRDEServer->i_rollback();
11688
11689 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
11690 mAudioSettings->i_rollback();
11691
11692 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11693 mUSBDeviceFilters->i_rollback();
11694
11695 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11696 mBandwidthControl->i_rollback();
11697
11698 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
11699 mGuestDebugControl->i_rollback();
11700
11701 if (mPlatform && (mData->flModifications & IsModified_Platform))
11702 {
11703 ChipsetType_T enmChipset;
11704 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11705 ComAssertComRC(hrc);
11706
11707 mNetworkAdapters.resize(PlatformProperties::s_getMaxNetworkAdapters(enmChipset));
11708 }
11709
11710 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11711 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11712 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11713
11714 if (mData->flModifications & IsModified_NetworkAdapters)
11715 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11716 if ( mNetworkAdapters[slot]
11717 && mNetworkAdapters[slot]->i_isModified())
11718 {
11719 mNetworkAdapters[slot]->i_rollback();
11720 networkAdapters[slot] = mNetworkAdapters[slot];
11721 }
11722
11723 if (mData->flModifications & IsModified_SerialPorts)
11724 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11725 if ( mSerialPorts[slot]
11726 && mSerialPorts[slot]->i_isModified())
11727 {
11728 mSerialPorts[slot]->i_rollback();
11729 serialPorts[slot] = mSerialPorts[slot];
11730 }
11731
11732 if (mData->flModifications & IsModified_ParallelPorts)
11733 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11734 if ( mParallelPorts[slot]
11735 && mParallelPorts[slot]->i_isModified())
11736 {
11737 mParallelPorts[slot]->i_rollback();
11738 parallelPorts[slot] = mParallelPorts[slot];
11739 }
11740
11741 if (aNotify)
11742 {
11743 /* inform the direct session about changes */
11744
11745 ComObjPtr<Machine> that = this;
11746 uint32_t flModifications = mData->flModifications;
11747 alock.release();
11748
11749 if (flModifications & IsModified_SharedFolders)
11750 that->i_onSharedFolderChange();
11751
11752 if (flModifications & IsModified_VRDEServer)
11753 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11754 if (flModifications & IsModified_USB)
11755 that->i_onUSBControllerChange();
11756
11757 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11758 if (networkAdapters[slot])
11759 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11760 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11761 if (serialPorts[slot])
11762 that->i_onSerialPortChange(serialPorts[slot]);
11763 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11764 if (parallelPorts[slot])
11765 that->i_onParallelPortChange(parallelPorts[slot]);
11766
11767 if (flModifications & IsModified_Storage)
11768 {
11769 for (StorageControllerList::const_iterator
11770 it = mStorageControllers->begin();
11771 it != mStorageControllers->end();
11772 ++it)
11773 {
11774 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11775 }
11776 }
11777
11778 if (flModifications & IsModified_GuestDebugControl)
11779 that->i_onGuestDebugControlChange(mGuestDebugControl);
11780
11781#if 0
11782 if (flModifications & IsModified_BandwidthControl)
11783 that->onBandwidthControlChange();
11784#endif
11785 }
11786}
11787
11788/**
11789 * Commits all the changes to machine settings.
11790 *
11791 * Note that this operation is supposed to never fail.
11792 *
11793 * @note Locks this object and children for writing.
11794 */
11795void Machine::i_commit()
11796{
11797 AutoCaller autoCaller(this);
11798 AssertComRCReturnVoid(autoCaller.hrc());
11799
11800 AutoCaller peerCaller(mPeer);
11801 AssertComRCReturnVoid(peerCaller.hrc());
11802
11803 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11804
11805 /*
11806 * use safe commit to ensure Snapshot machines (that share mUserData)
11807 * will still refer to a valid memory location
11808 */
11809 mUserData.commitCopy();
11810
11811 mHWData.commit();
11812
11813 if (mMediumAttachments.isBackedUp())
11814 i_commitMedia(Global::IsOnline(mData->mMachineState));
11815
11816 mPlatform->i_commit();
11817 mFirmwareSettings->i_commit();
11818 mRecordingSettings->i_commit();
11819 mTrustedPlatformModule->i_commit();
11820 mNvramStore->i_commit();
11821 mGraphicsAdapter->i_commit();
11822 mVRDEServer->i_commit();
11823 mAudioSettings->i_commit();
11824 mUSBDeviceFilters->i_commit();
11825 mBandwidthControl->i_commit();
11826 mGuestDebugControl->i_commit();
11827
11828 /* Since mNetworkAdapters is a list which might have been changed (resized)
11829 * without using the Backupable<> template we need to handle the copying
11830 * of the list entries manually, including the creation of peers for the
11831 * new objects. */
11832 ChipsetType_T enmChipset;
11833 HRESULT hrc = mPlatform->getChipsetType(&enmChipset);
11834 ComAssertComRC(hrc);
11835
11836 bool commitNetworkAdapters = false;
11837 size_t const newSize = PlatformProperties::s_getMaxNetworkAdapters(enmChipset);
11838 if (mPeer)
11839 {
11840 size_t const oldSize = mNetworkAdapters.size();
11841 size_t const oldPeerSize = mPeer->mNetworkAdapters.size();
11842
11843 /* commit everything, even the ones which will go away */
11844 for (size_t slot = 0; slot < oldSize; slot++)
11845 mNetworkAdapters[slot]->i_commit();
11846 /* copy over the new entries, creating a peer and uninit the original */
11847 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, oldPeerSize));
11848 /* make sure to have enough room for iterating over the (newly added) slots down below */
11849 if (newSize > oldSize)
11850 {
11851 mNetworkAdapters.resize(newSize);
11852
11853 com::Utf8Str osTypeId;
11854 ComObjPtr<GuestOSType> osType = NULL;
11855 hrc = getOSTypeId(osTypeId);
11856 if (SUCCEEDED(hrc))
11857 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
11858
11859 for (size_t slot = oldSize; slot < newSize; slot++)
11860 {
11861 mNetworkAdapters[slot].createObject();
11862 mNetworkAdapters[slot]->init(this, (ULONG)slot);
11863 mNetworkAdapters[slot]->i_applyDefaults(SUCCEEDED(hrc) ? osType : NULL);
11864 }
11865 }
11866 for (size_t slot = 0; slot < newSize; slot++)
11867 {
11868 /* look if this adapter has a peer device */
11869 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11870 if (!peer)
11871 {
11872 /* no peer means the adapter is a newly created one;
11873 * create a peer owning data this data share it with */
11874 peer.createObject();
11875 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11876 }
11877 mPeer->mNetworkAdapters[slot] = peer;
11878 }
11879 /* uninit any no longer needed network adapters */
11880 for (size_t slot = newSize; slot < oldSize; ++slot)
11881 mNetworkAdapters[slot]->uninit();
11882 for (size_t slot = newSize; slot < oldPeerSize; ++slot)
11883 {
11884 if (mPeer->mNetworkAdapters[slot])
11885 mPeer->mNetworkAdapters[slot]->uninit();
11886 }
11887 /* Keep the original network adapter count until this point, so that
11888 * discarding a chipset type change will not lose settings. */
11889 mNetworkAdapters.resize(newSize);
11890 mPeer->mNetworkAdapters.resize(newSize);
11891 }
11892 else
11893 {
11894 /* we have no peer (our parent is the newly created machine);
11895 * just commit changes to the network adapters */
11896 commitNetworkAdapters = true;
11897 }
11898 if (commitNetworkAdapters)
11899 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11900 mNetworkAdapters[slot]->i_commit();
11901
11902 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11903 mSerialPorts[slot]->i_commit();
11904 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11905 mParallelPorts[slot]->i_commit();
11906
11907 bool commitStorageControllers = false;
11908
11909 if (mStorageControllers.isBackedUp())
11910 {
11911 mStorageControllers.commit();
11912
11913 if (mPeer)
11914 {
11915 /* Commit all changes to new controllers (this will reshare data with
11916 * peers for those who have peers) */
11917 StorageControllerList *newList = new StorageControllerList();
11918 for (StorageControllerList::const_iterator
11919 it = mStorageControllers->begin();
11920 it != mStorageControllers->end();
11921 ++it)
11922 {
11923 (*it)->i_commit();
11924
11925 /* look if this controller has a peer device */
11926 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11927 if (!peer)
11928 {
11929 /* no peer means the device is a newly created one;
11930 * create a peer owning data this device share it with */
11931 peer.createObject();
11932 peer->init(mPeer, *it, true /* aReshare */);
11933 }
11934 else
11935 {
11936 /* remove peer from the old list */
11937 mPeer->mStorageControllers->remove(peer);
11938 }
11939 /* and add it to the new list */
11940 newList->push_back(peer);
11941 }
11942
11943 /* uninit old peer's controllers that are left */
11944 for (StorageControllerList::const_iterator
11945 it = mPeer->mStorageControllers->begin();
11946 it != mPeer->mStorageControllers->end();
11947 ++it)
11948 {
11949 (*it)->uninit();
11950 }
11951
11952 /* attach new list of controllers to our peer */
11953 mPeer->mStorageControllers.attach(newList);
11954 }
11955 else
11956 {
11957 /* we have no peer (our parent is the newly created machine);
11958 * just commit changes to devices */
11959 commitStorageControllers = true;
11960 }
11961 }
11962 else
11963 {
11964 /* the list of controllers itself is not changed,
11965 * just commit changes to controllers themselves */
11966 commitStorageControllers = true;
11967 }
11968
11969 if (commitStorageControllers)
11970 {
11971 for (StorageControllerList::const_iterator
11972 it = mStorageControllers->begin();
11973 it != mStorageControllers->end();
11974 ++it)
11975 {
11976 (*it)->i_commit();
11977 }
11978 }
11979
11980 bool commitUSBControllers = false;
11981
11982 if (mUSBControllers.isBackedUp())
11983 {
11984 mUSBControllers.commit();
11985
11986 if (mPeer)
11987 {
11988 /* Commit all changes to new controllers (this will reshare data with
11989 * peers for those who have peers) */
11990 USBControllerList *newList = new USBControllerList();
11991 for (USBControllerList::const_iterator
11992 it = mUSBControllers->begin();
11993 it != mUSBControllers->end();
11994 ++it)
11995 {
11996 (*it)->i_commit();
11997
11998 /* look if this controller has a peer device */
11999 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12000 if (!peer)
12001 {
12002 /* no peer means the device is a newly created one;
12003 * create a peer owning data this device share it with */
12004 peer.createObject();
12005 peer->init(mPeer, *it, true /* aReshare */);
12006 }
12007 else
12008 {
12009 /* remove peer from the old list */
12010 mPeer->mUSBControllers->remove(peer);
12011 }
12012 /* and add it to the new list */
12013 newList->push_back(peer);
12014 }
12015
12016 /* uninit old peer's controllers that are left */
12017 for (USBControllerList::const_iterator
12018 it = mPeer->mUSBControllers->begin();
12019 it != mPeer->mUSBControllers->end();
12020 ++it)
12021 {
12022 (*it)->uninit();
12023 }
12024
12025 /* attach new list of controllers to our peer */
12026 mPeer->mUSBControllers.attach(newList);
12027 }
12028 else
12029 {
12030 /* we have no peer (our parent is the newly created machine);
12031 * just commit changes to devices */
12032 commitUSBControllers = true;
12033 }
12034 }
12035 else
12036 {
12037 /* the list of controllers itself is not changed,
12038 * just commit changes to controllers themselves */
12039 commitUSBControllers = true;
12040 }
12041
12042 if (commitUSBControllers)
12043 {
12044 for (USBControllerList::const_iterator
12045 it = mUSBControllers->begin();
12046 it != mUSBControllers->end();
12047 ++it)
12048 {
12049 (*it)->i_commit();
12050 }
12051 }
12052
12053 if (i_isSessionMachine())
12054 {
12055 /* attach new data to the primary machine and reshare it */
12056 mPeer->mUserData.attach(mUserData);
12057 mPeer->mHWData.attach(mHWData);
12058 /* mmMediumAttachments is reshared by fixupMedia */
12059 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12060 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12061 }
12062}
12063
12064/**
12065 * Copies all the hardware data from the given machine.
12066 *
12067 * Currently, only called when the VM is being restored from a snapshot. In
12068 * particular, this implies that the VM is not running during this method's
12069 * call.
12070 *
12071 * @note This method must be called from under this object's lock.
12072 *
12073 * @note This method doesn't call #i_commit(), so all data remains backed up and
12074 * unsaved.
12075 */
12076void Machine::i_copyFrom(Machine *aThat)
12077{
12078 AssertReturnVoid(!i_isSnapshotMachine());
12079 AssertReturnVoid(aThat->i_isSnapshotMachine());
12080
12081 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12082
12083 mHWData.assignCopy(aThat->mHWData);
12084
12085 // create copies of all shared folders (mHWData after attaching a copy
12086 // contains just references to original objects)
12087 for (HWData::SharedFolderList::iterator
12088 it = mHWData->mSharedFolders.begin();
12089 it != mHWData->mSharedFolders.end();
12090 ++it)
12091 {
12092 ComObjPtr<SharedFolder> folder;
12093 folder.createObject();
12094 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12095 AssertComRC(hrc);
12096 *it = folder;
12097 }
12098
12099 mPlatform->i_copyFrom(aThat->mPlatform);
12100 i_platformPropertiesUpdate();
12101 mFirmwareSettings->i_copyFrom(aThat->mFirmwareSettings);
12102 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12103 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12104 mNvramStore->i_copyFrom(aThat->mNvramStore);
12105 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12106 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12107 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12108 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12109 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12110 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12111
12112 /* create private copies of all controllers */
12113 mStorageControllers.backup();
12114 mStorageControllers->clear();
12115 for (StorageControllerList::const_iterator
12116 it = aThat->mStorageControllers->begin();
12117 it != aThat->mStorageControllers->end();
12118 ++it)
12119 {
12120 ComObjPtr<StorageController> ctrl;
12121 ctrl.createObject();
12122 ctrl->initCopy(this, *it);
12123 mStorageControllers->push_back(ctrl);
12124 }
12125
12126 /* create private copies of all USB controllers */
12127 mUSBControllers.backup();
12128 mUSBControllers->clear();
12129 for (USBControllerList::const_iterator
12130 it = aThat->mUSBControllers->begin();
12131 it != aThat->mUSBControllers->end();
12132 ++it)
12133 {
12134 ComObjPtr<USBController> ctrl;
12135 ctrl.createObject();
12136 ctrl->initCopy(this, *it);
12137 mUSBControllers->push_back(ctrl);
12138 }
12139
12140 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12141 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12142 {
12143 if (mNetworkAdapters[slot].isNotNull())
12144 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12145 else
12146 {
12147 unconst(mNetworkAdapters[slot]).createObject();
12148 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12149 }
12150 }
12151 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12152 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12153 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12154 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12155}
12156
12157/**
12158 * Returns whether the given storage controller is hotplug capable.
12159 *
12160 * @returns true if the controller supports hotplugging
12161 * false otherwise.
12162 * @param enmCtrlType The controller type to check for.
12163 */
12164bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12165{
12166 BOOL aHotplugCapable = FALSE;
12167 HRESULT hrc = mPlatformProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12168 AssertComRC(hrc);
12169
12170 return RT_BOOL(aHotplugCapable);
12171}
12172
12173#ifdef VBOX_WITH_RESOURCE_USAGE_API
12174
12175void Machine::i_getDiskList(MediaList &list)
12176{
12177 for (MediumAttachmentList::const_iterator
12178 it = mMediumAttachments->begin();
12179 it != mMediumAttachments->end();
12180 ++it)
12181 {
12182 MediumAttachment *pAttach = *it;
12183 /* just in case */
12184 AssertContinue(pAttach);
12185
12186 AutoCaller localAutoCallerA(pAttach);
12187 if (FAILED(localAutoCallerA.hrc())) continue;
12188
12189 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12190
12191 if (pAttach->i_getType() == DeviceType_HardDisk)
12192 list.push_back(pAttach->i_getMedium());
12193 }
12194}
12195
12196void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12197{
12198 AssertReturnVoid(isWriteLockOnCurrentThread());
12199 AssertPtrReturnVoid(aCollector);
12200
12201 pm::CollectorHAL *hal = aCollector->getHAL();
12202 /* Create sub metrics */
12203 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12204 "Percentage of processor time spent in user mode by the VM process.");
12205 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12206 "Percentage of processor time spent in kernel mode by the VM process.");
12207 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12208 "Size of resident portion of VM process in memory.");
12209 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12210 "Actual size of all VM disks combined.");
12211 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12212 "Network receive rate.");
12213 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12214 "Network transmit rate.");
12215 /* Create and register base metrics */
12216 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12217 cpuLoadUser, cpuLoadKernel);
12218 aCollector->registerBaseMetric(cpuLoad);
12219 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12220 ramUsageUsed);
12221 aCollector->registerBaseMetric(ramUsage);
12222 MediaList disks;
12223 i_getDiskList(disks);
12224 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12225 diskUsageUsed);
12226 aCollector->registerBaseMetric(diskUsage);
12227
12228 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12230 new pm::AggregateAvg()));
12231 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12232 new pm::AggregateMin()));
12233 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12234 new pm::AggregateMax()));
12235 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12236 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12237 new pm::AggregateAvg()));
12238 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12239 new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12241 new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12244 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12245 new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12247 new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12249 new pm::AggregateMax()));
12250
12251 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12252 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12253 new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12255 new pm::AggregateMin()));
12256 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12257 new pm::AggregateMax()));
12258
12259
12260 /* Guest metrics collector */
12261 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12262 aCollector->registerGuest(mCollectorGuest);
12263 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12264
12265 /* Create sub metrics */
12266 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12267 "Percentage of processor time spent in user mode as seen by the guest.");
12268 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12269 "Percentage of processor time spent in kernel mode as seen by the guest.");
12270 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12271 "Percentage of processor time spent idling as seen by the guest.");
12272
12273 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12274 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12275 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12276 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12277 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12278 pm::SubMetric *guestMemCache = new pm::SubMetric(
12279 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12280
12281 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12282 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12283
12284 /* Create and register base metrics */
12285 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12286 machineNetRx, machineNetTx);
12287 aCollector->registerBaseMetric(machineNetRate);
12288
12289 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12290 guestLoadUser, guestLoadKernel, guestLoadIdle);
12291 aCollector->registerBaseMetric(guestCpuLoad);
12292
12293 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12294 guestMemTotal, guestMemFree,
12295 guestMemBalloon, guestMemShared,
12296 guestMemCache, guestPagedTotal);
12297 aCollector->registerBaseMetric(guestCpuMem);
12298
12299 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12300 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12301 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12302 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12303
12304 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12305 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12306 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12307 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12308
12309 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12313
12314 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12315 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12316 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12318
12319 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12320 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12321 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12323
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12328
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12333
12334 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12338
12339 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12343
12344 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12348
12349 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12353}
12354
12355void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12356{
12357 AssertReturnVoid(isWriteLockOnCurrentThread());
12358
12359 if (aCollector)
12360 {
12361 aCollector->unregisterMetricsFor(aMachine);
12362 aCollector->unregisterBaseMetricsFor(aMachine);
12363 }
12364}
12365
12366#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12367
12368/**
12369 * Updates the machine's platform properties based on the current platform architecture.
12370 *
12371 * @note Called internally when committing, rolling back or loading settings.
12372 */
12373void Machine::i_platformPropertiesUpdate()
12374{
12375 if (mPlatform)
12376 {
12377 /* Update architecture for platform properties. */
12378 PlatformArchitecture_T platformArchitecture;
12379 HRESULT hrc = mPlatform->getArchitecture(&platformArchitecture);
12380 ComAssertComRC(hrc);
12381 hrc = mPlatformProperties->i_setArchitecture(platformArchitecture);
12382 ComAssertComRC(hrc);
12383 }
12384}
12385
12386
12387////////////////////////////////////////////////////////////////////////////////
12388
12389DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12390
12391HRESULT SessionMachine::FinalConstruct()
12392{
12393 LogFlowThisFunc(("\n"));
12394
12395 mClientToken = NULL;
12396
12397 return BaseFinalConstruct();
12398}
12399
12400void SessionMachine::FinalRelease()
12401{
12402 LogFlowThisFunc(("\n"));
12403
12404 Assert(!mClientToken);
12405 /* paranoia, should not hang around any more */
12406 if (mClientToken)
12407 {
12408 delete mClientToken;
12409 mClientToken = NULL;
12410 }
12411
12412 uninit(Uninit::Unexpected);
12413
12414 BaseFinalRelease();
12415}
12416
12417/**
12418 * @note Must be called only by Machine::LockMachine() from its own write lock.
12419 */
12420HRESULT SessionMachine::init(Machine *aMachine)
12421{
12422 LogFlowThisFuncEnter();
12423 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12424
12425 AssertReturn(aMachine, E_INVALIDARG);
12426
12427 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12428
12429 /* Enclose the state transition NotReady->InInit->Ready */
12430 AutoInitSpan autoInitSpan(this);
12431 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12432
12433 HRESULT hrc = S_OK;
12434
12435 RT_ZERO(mAuthLibCtx);
12436
12437 /* create the machine client token */
12438 try
12439 {
12440 mClientToken = new ClientToken(aMachine, this);
12441 if (!mClientToken->isReady())
12442 {
12443 delete mClientToken;
12444 mClientToken = NULL;
12445 hrc = E_FAIL;
12446 }
12447 }
12448 catch (std::bad_alloc &)
12449 {
12450 hrc = E_OUTOFMEMORY;
12451 }
12452 if (FAILED(hrc))
12453 return hrc;
12454
12455 /* memorize the peer Machine */
12456 unconst(mPeer) = aMachine;
12457 /* share the parent pointer */
12458 unconst(mParent) = aMachine->mParent;
12459
12460 /* take the pointers to data to share */
12461 mData.share(aMachine->mData);
12462 mSSData.share(aMachine->mSSData);
12463
12464 mUserData.share(aMachine->mUserData);
12465 mHWData.share(aMachine->mHWData);
12466 mMediumAttachments.share(aMachine->mMediumAttachments);
12467
12468 mStorageControllers.allocate();
12469 for (StorageControllerList::const_iterator
12470 it = aMachine->mStorageControllers->begin();
12471 it != aMachine->mStorageControllers->end();
12472 ++it)
12473 {
12474 ComObjPtr<StorageController> ctl;
12475 ctl.createObject();
12476 ctl->init(this, *it);
12477 mStorageControllers->push_back(ctl);
12478 }
12479
12480 mUSBControllers.allocate();
12481 for (USBControllerList::const_iterator
12482 it = aMachine->mUSBControllers->begin();
12483 it != aMachine->mUSBControllers->end();
12484 ++it)
12485 {
12486 ComObjPtr<USBController> ctl;
12487 ctl.createObject();
12488 ctl->init(this, *it);
12489 mUSBControllers->push_back(ctl);
12490 }
12491
12492 unconst(mPlatformProperties).createObject();
12493 mPlatformProperties->init(mParent);
12494 unconst(mPlatform).createObject();
12495 mPlatform->init(this, aMachine->mPlatform);
12496
12497 i_platformPropertiesUpdate();
12498
12499 unconst(mFirmwareSettings).createObject();
12500 mFirmwareSettings->init(this, aMachine->mFirmwareSettings);
12501
12502 unconst(mRecordingSettings).createObject();
12503 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12504
12505 unconst(mTrustedPlatformModule).createObject();
12506 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12507
12508 unconst(mNvramStore).createObject();
12509 mNvramStore->init(this, aMachine->mNvramStore);
12510
12511 /* create another GraphicsAdapter object that will be mutable */
12512 unconst(mGraphicsAdapter).createObject();
12513 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12514 /* create another VRDEServer object that will be mutable */
12515 unconst(mVRDEServer).createObject();
12516 mVRDEServer->init(this, aMachine->mVRDEServer);
12517 /* create another audio settings object that will be mutable */
12518 unconst(mAudioSettings).createObject();
12519 mAudioSettings->init(this, aMachine->mAudioSettings);
12520 /* create a list of serial ports that will be mutable */
12521 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12522 {
12523 unconst(mSerialPorts[slot]).createObject();
12524 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12525 }
12526 /* create a list of parallel ports that will be mutable */
12527 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12528 {
12529 unconst(mParallelPorts[slot]).createObject();
12530 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12531 }
12532
12533 /* create another USB device filters object that will be mutable */
12534 unconst(mUSBDeviceFilters).createObject();
12535 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12536
12537 /* create a list of network adapters that will be mutable */
12538 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12539 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12540 {
12541 unconst(mNetworkAdapters[slot]).createObject();
12542 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12543 }
12544
12545 /* create another bandwidth control object that will be mutable */
12546 unconst(mBandwidthControl).createObject();
12547 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12548
12549 unconst(mGuestDebugControl).createObject();
12550 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
12551
12552 /* default is to delete saved state on Saved -> PoweredOff transition */
12553 mRemoveSavedState = true;
12554
12555 /* Confirm a successful initialization when it's the case */
12556 autoInitSpan.setSucceeded();
12557
12558 miNATNetworksStarted = 0;
12559
12560 LogFlowThisFuncLeave();
12561 return hrc;
12562}
12563
12564/**
12565 * Uninitializes this session object. If the reason is other than
12566 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12567 * or the client watcher code.
12568 *
12569 * @param aReason uninitialization reason
12570 *
12571 * @note Locks mParent + this object for writing.
12572 */
12573void SessionMachine::uninit(Uninit::Reason aReason)
12574{
12575 LogFlowThisFuncEnter();
12576 LogFlowThisFunc(("reason=%d\n", aReason));
12577
12578 /*
12579 * Strongly reference ourselves to prevent this object deletion after
12580 * mData->mSession.mMachine.setNull() below (which can release the last
12581 * reference and call the destructor). Important: this must be done before
12582 * accessing any members (and before AutoUninitSpan that does it as well).
12583 * This self reference will be released as the very last step on return.
12584 */
12585 ComObjPtr<SessionMachine> selfRef;
12586 if (aReason != Uninit::Unexpected)
12587 selfRef = this;
12588
12589 /* Enclose the state transition Ready->InUninit->NotReady */
12590 AutoUninitSpan autoUninitSpan(this);
12591 if (autoUninitSpan.uninitDone())
12592 {
12593 LogFlowThisFunc(("Already uninitialized\n"));
12594 LogFlowThisFuncLeave();
12595 return;
12596 }
12597
12598 if (autoUninitSpan.initFailed())
12599 {
12600 /* We've been called by init() because it's failed. It's not really
12601 * necessary (nor it's safe) to perform the regular uninit sequence
12602 * below, the following is enough.
12603 */
12604 LogFlowThisFunc(("Initialization failed.\n"));
12605 /* destroy the machine client token */
12606 if (mClientToken)
12607 {
12608 delete mClientToken;
12609 mClientToken = NULL;
12610 }
12611 uninitDataAndChildObjects();
12612 mData.free();
12613 unconst(mParent) = NULL;
12614 unconst(mPeer) = NULL;
12615 LogFlowThisFuncLeave();
12616 return;
12617 }
12618
12619 MachineState_T lastState;
12620 {
12621 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12622 lastState = mData->mMachineState;
12623 }
12624 NOREF(lastState);
12625
12626#ifdef VBOX_WITH_USB
12627 // release all captured USB devices, but do this before requesting the locks below
12628 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12629 {
12630 /* Console::captureUSBDevices() is called in the VM process only after
12631 * setting the machine state to Starting or Restoring.
12632 * Console::detachAllUSBDevices() will be called upon successful
12633 * termination. So, we need to release USB devices only if there was
12634 * an abnormal termination of a running VM.
12635 *
12636 * This is identical to SessionMachine::DetachAllUSBDevices except
12637 * for the aAbnormal argument. */
12638 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12639 AssertComRC(hrc);
12640 NOREF(hrc);
12641
12642 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12643 if (service)
12644 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12645 }
12646#endif /* VBOX_WITH_USB */
12647
12648 // we need to lock this object in uninit() because the lock is shared
12649 // with mPeer (as well as data we modify below). mParent lock is needed
12650 // by several calls to it.
12651 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12652
12653#ifdef VBOX_WITH_RESOURCE_USAGE_API
12654 /*
12655 * It is safe to call Machine::i_unregisterMetrics() here because
12656 * PerformanceCollector::samplerCallback no longer accesses guest methods
12657 * holding the lock.
12658 */
12659 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12660 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12661 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12662 if (mCollectorGuest)
12663 {
12664 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12665 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12666 mCollectorGuest = NULL;
12667 }
12668#endif
12669
12670 if (aReason == Uninit::Abnormal)
12671 {
12672 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12673
12674 /*
12675 * Move the VM to the 'Aborted' machine state unless we are restoring a
12676 * VM that was in the 'Saved' machine state. In that case, if the VM
12677 * fails before reaching either the 'Restoring' machine state or the
12678 * 'Running' machine state then we set the machine state to
12679 * 'AbortedSaved' in order to preserve the saved state file so that the
12680 * VM can be restored in the future.
12681 */
12682 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12683 i_setMachineState(MachineState_AbortedSaved);
12684 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12685 i_setMachineState(MachineState_Aborted);
12686 }
12687
12688 // any machine settings modified?
12689 if (mData->flModifications)
12690 {
12691 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12692 i_rollback(false /* aNotify */);
12693 }
12694
12695 mData->mSession.mPID = NIL_RTPROCESS;
12696
12697 if (aReason == Uninit::Unexpected)
12698 {
12699 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12700 * client watcher thread to update the set of machines that have open
12701 * sessions. */
12702 mParent->i_updateClientWatcher();
12703 }
12704
12705 /* uninitialize all remote controls */
12706 if (mData->mSession.mRemoteControls.size())
12707 {
12708 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12709 mData->mSession.mRemoteControls.size()));
12710
12711 /* Always restart a the beginning, since the iterator is invalidated
12712 * by using erase(). */
12713 for (Data::Session::RemoteControlList::iterator
12714 it = mData->mSession.mRemoteControls.begin();
12715 it != mData->mSession.mRemoteControls.end();
12716 it = mData->mSession.mRemoteControls.begin())
12717 {
12718 ComPtr<IInternalSessionControl> pControl = *it;
12719 mData->mSession.mRemoteControls.erase(it);
12720 multilock.release();
12721 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12722 HRESULT hrc = pControl->Uninitialize();
12723 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
12724 if (FAILED(hrc))
12725 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12726 multilock.acquire();
12727 }
12728 mData->mSession.mRemoteControls.clear();
12729 }
12730
12731 /* Remove all references to the NAT network service. The service will stop
12732 * if all references (also from other VMs) are removed. */
12733 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12734 {
12735 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12736 {
12737 BOOL enabled;
12738 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12739 if ( FAILED(hrc)
12740 || !enabled)
12741 continue;
12742
12743 NetworkAttachmentType_T type;
12744 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12745 if ( SUCCEEDED(hrc)
12746 && type == NetworkAttachmentType_NATNetwork)
12747 {
12748 Bstr name;
12749 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12750 if (SUCCEEDED(hrc))
12751 {
12752 multilock.release();
12753 Utf8Str strName(name);
12754 LogRel(("VM '%s' stops using NAT network '%s'\n",
12755 mUserData->s.strName.c_str(), strName.c_str()));
12756 mParent->i_natNetworkRefDec(strName);
12757 multilock.acquire();
12758 }
12759 }
12760 }
12761 }
12762
12763 /*
12764 * An expected uninitialization can come only from #i_checkForDeath().
12765 * Otherwise it means that something's gone really wrong (for example,
12766 * the Session implementation has released the VirtualBox reference
12767 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12768 * etc). However, it's also possible, that the client releases the IPC
12769 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12770 * but the VirtualBox release event comes first to the server process.
12771 * This case is practically possible, so we should not assert on an
12772 * unexpected uninit, just log a warning.
12773 */
12774
12775 if (aReason == Uninit::Unexpected)
12776 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12777
12778 if (aReason != Uninit::Normal)
12779 {
12780 mData->mSession.mDirectControl.setNull();
12781 }
12782 else
12783 {
12784 /* this must be null here (see #OnSessionEnd()) */
12785 Assert(mData->mSession.mDirectControl.isNull());
12786 Assert(mData->mSession.mState == SessionState_Unlocking);
12787 Assert(!mData->mSession.mProgress.isNull());
12788 }
12789 if (mData->mSession.mProgress)
12790 {
12791 if (aReason == Uninit::Normal)
12792 mData->mSession.mProgress->i_notifyComplete(S_OK);
12793 else
12794 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12795 COM_IIDOF(ISession),
12796 getComponentName(),
12797 tr("The VM session was aborted"));
12798 mData->mSession.mProgress.setNull();
12799 }
12800
12801 if (mConsoleTaskData.mProgress)
12802 {
12803 Assert(aReason == Uninit::Abnormal);
12804 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12805 COM_IIDOF(ISession),
12806 getComponentName(),
12807 tr("The VM session was aborted"));
12808 mConsoleTaskData.mProgress.setNull();
12809 }
12810
12811 /* remove the association between the peer machine and this session machine */
12812 Assert( (SessionMachine*)mData->mSession.mMachine == this
12813 || aReason == Uninit::Unexpected);
12814
12815 /* reset the rest of session data */
12816 mData->mSession.mLockType = LockType_Null;
12817 mData->mSession.mMachine.setNull();
12818 mData->mSession.mState = SessionState_Unlocked;
12819 mData->mSession.mName.setNull();
12820
12821 /* destroy the machine client token before leaving the exclusive lock */
12822 if (mClientToken)
12823 {
12824 delete mClientToken;
12825 mClientToken = NULL;
12826 }
12827
12828 /* fire an event */
12829 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12830
12831 uninitDataAndChildObjects();
12832
12833 /* free the essential data structure last */
12834 mData.free();
12835
12836 /* release the exclusive lock before setting the below two to NULL */
12837 multilock.release();
12838
12839 unconst(mParent) = NULL;
12840 unconst(mPeer) = NULL;
12841
12842 AuthLibUnload(&mAuthLibCtx);
12843
12844 LogFlowThisFuncLeave();
12845}
12846
12847// util::Lockable interface
12848////////////////////////////////////////////////////////////////////////////////
12849
12850/**
12851 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12852 * with the primary Machine instance (mPeer).
12853 */
12854RWLockHandle *SessionMachine::lockHandle() const
12855{
12856 AssertReturn(mPeer != NULL, NULL);
12857 return mPeer->lockHandle();
12858}
12859
12860// IInternalMachineControl methods
12861////////////////////////////////////////////////////////////////////////////////
12862
12863/**
12864 * Passes collected guest statistics to performance collector object
12865 */
12866HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12867 ULONG aCpuKernel, ULONG aCpuIdle,
12868 ULONG aMemTotal, ULONG aMemFree,
12869 ULONG aMemBalloon, ULONG aMemShared,
12870 ULONG aMemCache, ULONG aPageTotal,
12871 ULONG aAllocVMM, ULONG aFreeVMM,
12872 ULONG aBalloonedVMM, ULONG aSharedVMM,
12873 ULONG aVmNetRx, ULONG aVmNetTx)
12874{
12875#ifdef VBOX_WITH_RESOURCE_USAGE_API
12876 if (mCollectorGuest)
12877 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12878 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12879 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12880 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12881
12882 return S_OK;
12883#else
12884 NOREF(aValidStats);
12885 NOREF(aCpuUser);
12886 NOREF(aCpuKernel);
12887 NOREF(aCpuIdle);
12888 NOREF(aMemTotal);
12889 NOREF(aMemFree);
12890 NOREF(aMemBalloon);
12891 NOREF(aMemShared);
12892 NOREF(aMemCache);
12893 NOREF(aPageTotal);
12894 NOREF(aAllocVMM);
12895 NOREF(aFreeVMM);
12896 NOREF(aBalloonedVMM);
12897 NOREF(aSharedVMM);
12898 NOREF(aVmNetRx);
12899 NOREF(aVmNetTx);
12900 return E_NOTIMPL;
12901#endif
12902}
12903
12904////////////////////////////////////////////////////////////////////////////////
12905//
12906// SessionMachine task records
12907//
12908////////////////////////////////////////////////////////////////////////////////
12909
12910/**
12911 * Task record for saving the machine state.
12912 */
12913class SessionMachine::SaveStateTask
12914 : public Machine::Task
12915{
12916public:
12917 SaveStateTask(SessionMachine *m,
12918 Progress *p,
12919 const Utf8Str &t,
12920 Reason_T enmReason,
12921 const Utf8Str &strStateFilePath)
12922 : Task(m, p, t),
12923 m_enmReason(enmReason),
12924 m_strStateFilePath(strStateFilePath)
12925 {}
12926
12927private:
12928 void handler()
12929 {
12930 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12931 }
12932
12933 Reason_T m_enmReason;
12934 Utf8Str m_strStateFilePath;
12935
12936 friend class SessionMachine;
12937};
12938
12939/**
12940 * Task thread implementation for SessionMachine::SaveState(), called from
12941 * SessionMachine::taskHandler().
12942 *
12943 * @note Locks this object for writing.
12944 *
12945 * @param task
12946 */
12947void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12948{
12949 LogFlowThisFuncEnter();
12950
12951 AutoCaller autoCaller(this);
12952 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12953 if (FAILED(autoCaller.hrc()))
12954 {
12955 /* we might have been uninitialized because the session was accidentally
12956 * closed by the client, so don't assert */
12957 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
12958 task.m_pProgress->i_notifyComplete(hrc);
12959 LogFlowThisFuncLeave();
12960 return;
12961 }
12962
12963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12964
12965 HRESULT hrc = S_OK;
12966
12967 try
12968 {
12969 ComPtr<IInternalSessionControl> directControl;
12970 if (mData->mSession.mLockType == LockType_VM)
12971 directControl = mData->mSession.mDirectControl;
12972 if (directControl.isNull())
12973 throw setError(VBOX_E_INVALID_VM_STATE,
12974 tr("Trying to save state without a running VM"));
12975 alock.release();
12976 BOOL fSuspendedBySave;
12977 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12978 Assert(!fSuspendedBySave);
12979 alock.acquire();
12980
12981 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
12982 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
12983 throw E_FAIL);
12984
12985 if (SUCCEEDED(hrc))
12986 {
12987 mSSData->strStateFilePath = task.m_strStateFilePath;
12988
12989 /* save all VM settings */
12990 hrc = i_saveSettings(NULL, alock);
12991 // no need to check whether VirtualBox.xml needs saving also since
12992 // we can't have a name change pending at this point
12993 }
12994 else
12995 {
12996 // On failure, set the state to the state we had at the beginning.
12997 i_setMachineState(task.m_machineStateBackup);
12998 i_updateMachineStateOnClient();
12999
13000 // Delete the saved state file (might have been already created).
13001 // No need to check whether this is shared with a snapshot here
13002 // because we certainly created a fresh saved state file here.
13003 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13004 }
13005 }
13006 catch (HRESULT hrcXcpt)
13007 {
13008 hrc = hrcXcpt;
13009 }
13010
13011 task.m_pProgress->i_notifyComplete(hrc);
13012
13013 LogFlowThisFuncLeave();
13014}
13015
13016/**
13017 * @note Locks this object for writing.
13018 */
13019HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13020{
13021 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13022}
13023
13024HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13025{
13026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13029 if (FAILED(hrc)) return hrc;
13030
13031 if ( mData->mMachineState != MachineState_Running
13032 && mData->mMachineState != MachineState_Paused
13033 )
13034 return setError(VBOX_E_INVALID_VM_STATE,
13035 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13036 Global::stringifyMachineState(mData->mMachineState));
13037
13038 ComObjPtr<Progress> pProgress;
13039 pProgress.createObject();
13040 hrc = pProgress->init(i_getVirtualBox(),
13041 static_cast<IMachine *>(this) /* aInitiator */,
13042 tr("Saving the execution state of the virtual machine"),
13043 FALSE /* aCancelable */);
13044 if (FAILED(hrc))
13045 return hrc;
13046
13047 Utf8Str strStateFilePath;
13048 i_composeSavedStateFilename(strStateFilePath);
13049
13050 /* create and start the task on a separate thread (note that it will not
13051 * start working until we release alock) */
13052 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13053 hrc = pTask->createThread();
13054 if (FAILED(hrc))
13055 return hrc;
13056
13057 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13058 i_setMachineState(MachineState_Saving);
13059 i_updateMachineStateOnClient();
13060
13061 pProgress.queryInterfaceTo(aProgress.asOutParam());
13062
13063 return S_OK;
13064}
13065
13066/**
13067 * @note Locks this object for writing.
13068 */
13069HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13070{
13071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13072
13073 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13074 if (FAILED(hrc)) return hrc;
13075
13076 if ( mData->mMachineState != MachineState_PoweredOff
13077 && mData->mMachineState != MachineState_Teleported
13078 && mData->mMachineState != MachineState_Aborted
13079 )
13080 return setError(VBOX_E_INVALID_VM_STATE,
13081 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13082 Global::stringifyMachineState(mData->mMachineState));
13083
13084 com::Utf8Str stateFilePathFull;
13085 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13086 if (RT_FAILURE(vrc))
13087 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13088 tr("Invalid saved state file path '%s' (%Rrc)"),
13089 aSavedStateFile.c_str(),
13090 vrc);
13091
13092 mSSData->strStateFilePath = stateFilePathFull;
13093
13094 /* The below i_setMachineState() will detect the state transition and will
13095 * update the settings file */
13096
13097 return i_setMachineState(MachineState_Saved);
13098}
13099
13100/**
13101 * @note Locks this object for writing.
13102 */
13103HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13104{
13105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13106
13107 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13108 if (FAILED(hrc)) return hrc;
13109
13110 if ( mData->mMachineState != MachineState_Saved
13111 && mData->mMachineState != MachineState_AbortedSaved)
13112 return setError(VBOX_E_INVALID_VM_STATE,
13113 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13114 Global::stringifyMachineState(mData->mMachineState));
13115
13116 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13117
13118 /*
13119 * Saved -> PoweredOff transition will be detected in the SessionMachine
13120 * and properly handled.
13121 */
13122 hrc = i_setMachineState(MachineState_PoweredOff);
13123 return hrc;
13124}
13125
13126
13127/**
13128 * @note Locks the same as #i_setMachineState() does.
13129 */
13130HRESULT SessionMachine::updateState(MachineState_T aState)
13131{
13132 return i_setMachineState(aState);
13133}
13134
13135/**
13136 * @note Locks this object for writing.
13137 */
13138HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13139{
13140 IProgress *pProgress(aProgress);
13141
13142 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13143
13144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13145
13146 if (mData->mSession.mState != SessionState_Locked)
13147 return VBOX_E_INVALID_OBJECT_STATE;
13148
13149 if (!mData->mSession.mProgress.isNull())
13150 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13151
13152 /* If we didn't reference the NAT network service yet, add a reference to
13153 * force a start */
13154 if (miNATNetworksStarted < 1)
13155 {
13156 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13157 {
13158 BOOL enabled;
13159 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13160 if ( FAILED(hrc)
13161 || !enabled)
13162 continue;
13163
13164 NetworkAttachmentType_T type;
13165 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13166 if ( SUCCEEDED(hrc)
13167 && type == NetworkAttachmentType_NATNetwork)
13168 {
13169 Bstr name;
13170 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13171 if (SUCCEEDED(hrc))
13172 {
13173 Utf8Str strName(name);
13174 LogRel(("VM '%s' starts using NAT network '%s'\n",
13175 mUserData->s.strName.c_str(), strName.c_str()));
13176 mPeer->lockHandle()->unlockWrite();
13177 mParent->i_natNetworkRefInc(strName);
13178#ifdef RT_LOCK_STRICT
13179 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13180#else
13181 mPeer->lockHandle()->lockWrite();
13182#endif
13183 }
13184 }
13185 }
13186 miNATNetworksStarted++;
13187 }
13188
13189 LogFlowThisFunc(("returns S_OK.\n"));
13190 return S_OK;
13191}
13192
13193/**
13194 * @note Locks this object for writing.
13195 */
13196HRESULT SessionMachine::endPowerUp(LONG aResult)
13197{
13198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13199
13200 if (mData->mSession.mState != SessionState_Locked)
13201 return VBOX_E_INVALID_OBJECT_STATE;
13202
13203 /* Finalize the LaunchVMProcess progress object. */
13204 if (mData->mSession.mProgress)
13205 {
13206 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13207 mData->mSession.mProgress.setNull();
13208 }
13209
13210 if (SUCCEEDED((HRESULT)aResult))
13211 {
13212#ifdef VBOX_WITH_RESOURCE_USAGE_API
13213 /* The VM has been powered up successfully, so it makes sense
13214 * now to offer the performance metrics for a running machine
13215 * object. Doing it earlier wouldn't be safe. */
13216 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13217 mData->mSession.mPID);
13218#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13219 }
13220
13221 return S_OK;
13222}
13223
13224/**
13225 * @note Locks this object for writing.
13226 */
13227HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13228{
13229 LogFlowThisFuncEnter();
13230
13231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13232
13233 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13234 E_FAIL);
13235
13236 /* create a progress object to track operation completion */
13237 ComObjPtr<Progress> pProgress;
13238 pProgress.createObject();
13239 pProgress->init(i_getVirtualBox(),
13240 static_cast<IMachine *>(this) /* aInitiator */,
13241 tr("Stopping the virtual machine"),
13242 FALSE /* aCancelable */);
13243
13244 /* fill in the console task data */
13245 mConsoleTaskData.mLastState = mData->mMachineState;
13246 mConsoleTaskData.mProgress = pProgress;
13247
13248 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13249 i_setMachineState(MachineState_Stopping);
13250
13251 pProgress.queryInterfaceTo(aProgress.asOutParam());
13252
13253 return S_OK;
13254}
13255
13256/**
13257 * @note Locks this object for writing.
13258 */
13259HRESULT SessionMachine::endPoweringDown(LONG aResult,
13260 const com::Utf8Str &aErrMsg)
13261{
13262 HRESULT const hrcResult = (HRESULT)aResult;
13263 LogFlowThisFuncEnter();
13264
13265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13266
13267 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13268 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13269 && mConsoleTaskData.mLastState != MachineState_Null,
13270 E_FAIL);
13271
13272 /*
13273 * On failure, set the state to the state we had when BeginPoweringDown()
13274 * was called (this is expected by Console::PowerDown() and the associated
13275 * task). On success the VM process already changed the state to
13276 * MachineState_PoweredOff, so no need to do anything.
13277 */
13278 if (FAILED(hrcResult))
13279 i_setMachineState(mConsoleTaskData.mLastState);
13280
13281 /* notify the progress object about operation completion */
13282 Assert(mConsoleTaskData.mProgress);
13283 if (SUCCEEDED(hrcResult))
13284 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13285 else
13286 {
13287 if (aErrMsg.length())
13288 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13289 COM_IIDOF(ISession),
13290 getComponentName(),
13291 aErrMsg.c_str());
13292 else
13293 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13294 }
13295
13296 /* clear out the temporary saved state data */
13297 mConsoleTaskData.mLastState = MachineState_Null;
13298 mConsoleTaskData.mProgress.setNull();
13299
13300 LogFlowThisFuncLeave();
13301 return S_OK;
13302}
13303
13304
13305/**
13306 * Goes through the USB filters of the given machine to see if the given
13307 * device matches any filter or not.
13308 *
13309 * @note Locks the same as USBController::hasMatchingFilter() does.
13310 */
13311HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13312 BOOL *aMatched,
13313 ULONG *aMaskedInterfaces)
13314{
13315 LogFlowThisFunc(("\n"));
13316
13317#ifdef VBOX_WITH_USB
13318 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13319#else
13320 NOREF(aDevice);
13321 NOREF(aMaskedInterfaces);
13322 *aMatched = FALSE;
13323#endif
13324
13325 return S_OK;
13326}
13327
13328/**
13329 * @note Locks the same as Host::captureUSBDevice() does.
13330 */
13331HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13332{
13333 LogFlowThisFunc(("\n"));
13334
13335#ifdef VBOX_WITH_USB
13336 /* if captureDeviceForVM() fails, it must have set extended error info */
13337 clearError();
13338 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13339 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13340 return hrc;
13341
13342 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13343 AssertReturn(service, E_FAIL);
13344 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13345#else
13346 RT_NOREF(aId, aCaptureFilename);
13347 return E_NOTIMPL;
13348#endif
13349}
13350
13351/**
13352 * @note Locks the same as Host::detachUSBDevice() does.
13353 */
13354HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13355 BOOL aDone)
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359#ifdef VBOX_WITH_USB
13360 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13361 AssertReturn(service, E_FAIL);
13362 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13363#else
13364 NOREF(aId);
13365 NOREF(aDone);
13366 return E_NOTIMPL;
13367#endif
13368}
13369
13370/**
13371 * Inserts all machine filters to the USB proxy service and then calls
13372 * Host::autoCaptureUSBDevices().
13373 *
13374 * Called by Console from the VM process upon VM startup.
13375 *
13376 * @note Locks what called methods lock.
13377 */
13378HRESULT SessionMachine::autoCaptureUSBDevices()
13379{
13380 LogFlowThisFunc(("\n"));
13381
13382#ifdef VBOX_WITH_USB
13383 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13384 AssertComRC(hrc);
13385 NOREF(hrc);
13386
13387 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13388 AssertReturn(service, E_FAIL);
13389 return service->autoCaptureDevicesForVM(this);
13390#else
13391 return S_OK;
13392#endif
13393}
13394
13395/**
13396 * Removes all machine filters from the USB proxy service and then calls
13397 * Host::detachAllUSBDevices().
13398 *
13399 * Called by Console from the VM process upon normal VM termination or by
13400 * SessionMachine::uninit() upon abnormal VM termination (from under the
13401 * Machine/SessionMachine lock).
13402 *
13403 * @note Locks what called methods lock.
13404 */
13405HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13406{
13407 LogFlowThisFunc(("\n"));
13408
13409#ifdef VBOX_WITH_USB
13410 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13411 AssertComRC(hrc);
13412 NOREF(hrc);
13413
13414 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13415 AssertReturn(service, E_FAIL);
13416 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13417#else
13418 NOREF(aDone);
13419 return S_OK;
13420#endif
13421}
13422
13423/**
13424 * @note Locks this object for writing.
13425 */
13426HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13427 ComPtr<IProgress> &aProgress)
13428{
13429 LogFlowThisFuncEnter();
13430
13431 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13432 /*
13433 * We don't assert below because it might happen that a non-direct session
13434 * informs us it is closed right after we've been uninitialized -- it's ok.
13435 */
13436
13437 /* get IInternalSessionControl interface */
13438 ComPtr<IInternalSessionControl> control(aSession);
13439
13440 ComAssertRet(!control.isNull(), E_INVALIDARG);
13441
13442 /* Creating a Progress object requires the VirtualBox lock, and
13443 * thus locking it here is required by the lock order rules. */
13444 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13445
13446 if (control == mData->mSession.mDirectControl)
13447 {
13448 /* The direct session is being normally closed by the client process
13449 * ----------------------------------------------------------------- */
13450
13451 /* go to the closing state (essential for all open*Session() calls and
13452 * for #i_checkForDeath()) */
13453 Assert(mData->mSession.mState == SessionState_Locked);
13454 mData->mSession.mState = SessionState_Unlocking;
13455
13456 /* set direct control to NULL to release the remote instance */
13457 mData->mSession.mDirectControl.setNull();
13458 LogFlowThisFunc(("Direct control is set to NULL\n"));
13459
13460 if (mData->mSession.mProgress)
13461 {
13462 /* finalize the progress, someone might wait if a frontend
13463 * closes the session before powering on the VM. */
13464 mData->mSession.mProgress->notifyComplete(E_FAIL,
13465 COM_IIDOF(ISession),
13466 getComponentName(),
13467 tr("The VM session was closed before any attempt to power it on"));
13468 mData->mSession.mProgress.setNull();
13469 }
13470
13471 /* Create the progress object the client will use to wait until
13472 * #i_checkForDeath() is called to uninitialize this session object after
13473 * it releases the IPC semaphore.
13474 * Note! Because we're "reusing" mProgress here, this must be a proxy
13475 * object just like for LaunchVMProcess. */
13476 Assert(mData->mSession.mProgress.isNull());
13477 ComObjPtr<ProgressProxy> progress;
13478 progress.createObject();
13479 ComPtr<IUnknown> pPeer(mPeer);
13480 progress->init(mParent, pPeer,
13481 Bstr(tr("Closing session")).raw(),
13482 FALSE /* aCancelable */);
13483 progress.queryInterfaceTo(aProgress.asOutParam());
13484 mData->mSession.mProgress = progress;
13485 }
13486 else
13487 {
13488 /* the remote session is being normally closed */
13489 bool found = false;
13490 for (Data::Session::RemoteControlList::iterator
13491 it = mData->mSession.mRemoteControls.begin();
13492 it != mData->mSession.mRemoteControls.end();
13493 ++it)
13494 {
13495 if (control == *it)
13496 {
13497 found = true;
13498 // This MUST be erase(it), not remove(*it) as the latter
13499 // triggers a very nasty use after free due to the place where
13500 // the value "lives".
13501 mData->mSession.mRemoteControls.erase(it);
13502 break;
13503 }
13504 }
13505 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13506 E_INVALIDARG);
13507 }
13508
13509 /* signal the client watcher thread, because the client is going away */
13510 mParent->i_updateClientWatcher();
13511
13512 LogFlowThisFuncLeave();
13513 return S_OK;
13514}
13515
13516HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13517 std::vector<com::Utf8Str> &aValues,
13518 std::vector<LONG64> &aTimestamps,
13519 std::vector<com::Utf8Str> &aFlags)
13520{
13521 LogFlowThisFunc(("\n"));
13522
13523#ifdef VBOX_WITH_GUEST_PROPS
13524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13525
13526 size_t cEntries = mHWData->mGuestProperties.size();
13527 aNames.resize(cEntries);
13528 aValues.resize(cEntries);
13529 aTimestamps.resize(cEntries);
13530 aFlags.resize(cEntries);
13531
13532 size_t i = 0;
13533 for (HWData::GuestPropertyMap::const_iterator
13534 it = mHWData->mGuestProperties.begin();
13535 it != mHWData->mGuestProperties.end();
13536 ++it, ++i)
13537 {
13538 aNames[i] = it->first;
13539 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
13540 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13541
13542 aValues[i] = it->second.strValue;
13543 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
13544 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
13545
13546 aTimestamps[i] = it->second.mTimestamp;
13547
13548 /* If it is NULL, keep it NULL. */
13549 if (it->second.mFlags)
13550 {
13551 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13552 GuestPropWriteFlags(it->second.mFlags, szFlags);
13553 aFlags[i] = szFlags;
13554 }
13555 else
13556 aFlags[i] = "";
13557 }
13558 return S_OK;
13559#else
13560 ReturnComNotImplemented();
13561#endif
13562}
13563
13564HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13565 const com::Utf8Str &aValue,
13566 LONG64 aTimestamp,
13567 const com::Utf8Str &aFlags,
13568 BOOL fWasDeleted)
13569{
13570 LogFlowThisFunc(("\n"));
13571
13572#ifdef VBOX_WITH_GUEST_PROPS
13573 try
13574 {
13575 /*
13576 * Convert input up front.
13577 */
13578 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13579 if (aFlags.length())
13580 {
13581 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13582 AssertRCReturn(vrc, E_INVALIDARG);
13583 }
13584
13585 /*
13586 * Now grab the object lock, validate the state and do the update.
13587 */
13588
13589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13590
13591 if (!Global::IsOnline(mData->mMachineState))
13592 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13593
13594 i_setModified(IsModified_MachineData);
13595 mHWData.backup();
13596
13597 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13598 if (it != mHWData->mGuestProperties.end())
13599 {
13600 if (!fWasDeleted)
13601 {
13602 it->second.strValue = aValue;
13603 it->second.mTimestamp = aTimestamp;
13604 it->second.mFlags = fFlags;
13605 }
13606 else
13607 mHWData->mGuestProperties.erase(it);
13608
13609 mData->mGuestPropertiesModified = TRUE;
13610 }
13611 else if (!fWasDeleted)
13612 {
13613 HWData::GuestProperty prop;
13614 prop.strValue = aValue;
13615 prop.mTimestamp = aTimestamp;
13616 prop.mFlags = fFlags;
13617
13618 mHWData->mGuestProperties[aName] = prop;
13619 mData->mGuestPropertiesModified = TRUE;
13620 }
13621
13622 alock.release();
13623
13624 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13625 }
13626 catch (...)
13627 {
13628 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13629 }
13630 return S_OK;
13631#else
13632 ReturnComNotImplemented();
13633#endif
13634}
13635
13636
13637HRESULT SessionMachine::lockMedia()
13638{
13639 AutoMultiWriteLock2 alock(this->lockHandle(),
13640 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13641
13642 AssertReturn( mData->mMachineState == MachineState_Starting
13643 || mData->mMachineState == MachineState_Restoring
13644 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13645
13646 clearError();
13647 alock.release();
13648 return i_lockMedia();
13649}
13650
13651HRESULT SessionMachine::unlockMedia()
13652{
13653 HRESULT hrc = i_unlockMedia();
13654 return hrc;
13655}
13656
13657HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13658 ComPtr<IMediumAttachment> &aNewAttachment)
13659{
13660 // request the host lock first, since might be calling Host methods for getting host drives;
13661 // next, protect the media tree all the while we're in here, as well as our member variables
13662 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13663 this->lockHandle(),
13664 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13665
13666 IMediumAttachment *iAttach = aAttachment;
13667 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13668
13669 Utf8Str ctrlName;
13670 LONG lPort;
13671 LONG lDevice;
13672 bool fTempEject;
13673 {
13674 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13675
13676 /* Need to query the details first, as the IMediumAttachment reference
13677 * might be to the original settings, which we are going to change. */
13678 ctrlName = pAttach->i_getControllerName();
13679 lPort = pAttach->i_getPort();
13680 lDevice = pAttach->i_getDevice();
13681 fTempEject = pAttach->i_getTempEject();
13682 }
13683
13684 if (!fTempEject)
13685 {
13686 /* Remember previously mounted medium. The medium before taking the
13687 * backup is not necessarily the same thing. */
13688 ComObjPtr<Medium> oldmedium;
13689 oldmedium = pAttach->i_getMedium();
13690
13691 i_setModified(IsModified_Storage);
13692 mMediumAttachments.backup();
13693
13694 // The backup operation makes the pAttach reference point to the
13695 // old settings. Re-get the correct reference.
13696 pAttach = i_findAttachment(*mMediumAttachments.data(),
13697 ctrlName,
13698 lPort,
13699 lDevice);
13700
13701 {
13702 AutoCaller autoAttachCaller(this);
13703 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
13704
13705 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13706 if (!oldmedium.isNull())
13707 oldmedium->i_removeBackReference(mData->mUuid);
13708
13709 pAttach->i_updateMedium(NULL);
13710 pAttach->i_updateEjected();
13711 }
13712
13713 i_setModified(IsModified_Storage);
13714 }
13715 else
13716 {
13717 {
13718 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13719 pAttach->i_updateEjected();
13720 }
13721 }
13722
13723 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13724
13725 return S_OK;
13726}
13727
13728HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13729 com::Utf8Str &aResult)
13730{
13731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13732
13733 HRESULT hrc = S_OK;
13734
13735 if (!mAuthLibCtx.hAuthLibrary)
13736 {
13737 /* Load the external authentication library. */
13738 Bstr authLibrary;
13739 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13740
13741 Utf8Str filename = authLibrary;
13742
13743 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13744 if (RT_FAILURE(vrc))
13745 hrc = setErrorBoth(E_FAIL, vrc,
13746 tr("Could not load the external authentication library '%s' (%Rrc)"),
13747 filename.c_str(), vrc);
13748 }
13749
13750 /* The auth library might need the machine lock. */
13751 alock.release();
13752
13753 if (FAILED(hrc))
13754 return hrc;
13755
13756 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13757 {
13758 enum VRDEAuthParams
13759 {
13760 parmUuid = 1,
13761 parmGuestJudgement,
13762 parmUser,
13763 parmPassword,
13764 parmDomain,
13765 parmClientId
13766 };
13767
13768 AuthResult result = AuthResultAccessDenied;
13769
13770 Guid uuid(aAuthParams[parmUuid]);
13771 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13772 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13773
13774 result = AuthLibAuthenticate(&mAuthLibCtx,
13775 uuid.raw(), guestJudgement,
13776 aAuthParams[parmUser].c_str(),
13777 aAuthParams[parmPassword].c_str(),
13778 aAuthParams[parmDomain].c_str(),
13779 u32ClientId);
13780
13781 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13782 size_t cbPassword = aAuthParams[parmPassword].length();
13783 if (cbPassword)
13784 {
13785 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13786 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13787 }
13788
13789 if (result == AuthResultAccessGranted)
13790 aResult = "granted";
13791 else
13792 aResult = "denied";
13793
13794 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13795 aAuthParams[parmUser].c_str(), aResult.c_str()));
13796 }
13797 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13798 {
13799 enum VRDEAuthDisconnectParams
13800 {
13801 parmUuid = 1,
13802 parmClientId
13803 };
13804
13805 Guid uuid(aAuthParams[parmUuid]);
13806 uint32_t u32ClientId = 0;
13807 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13808 }
13809 else
13810 {
13811 hrc = E_INVALIDARG;
13812 }
13813
13814 return hrc;
13815}
13816
13817// public methods only for internal purposes
13818/////////////////////////////////////////////////////////////////////////////
13819
13820#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13821/**
13822 * Called from the client watcher thread to check for expected or unexpected
13823 * death of the client process that has a direct session to this machine.
13824 *
13825 * On Win32 and on OS/2, this method is called only when we've got the
13826 * mutex (i.e. the client has either died or terminated normally) so it always
13827 * returns @c true (the client is terminated, the session machine is
13828 * uninitialized).
13829 *
13830 * On other platforms, the method returns @c true if the client process has
13831 * terminated normally or abnormally and the session machine was uninitialized,
13832 * and @c false if the client process is still alive.
13833 *
13834 * @note Locks this object for writing.
13835 */
13836bool SessionMachine::i_checkForDeath()
13837{
13838 Uninit::Reason reason;
13839 bool terminated = false;
13840
13841 /* Enclose autoCaller with a block because calling uninit() from under it
13842 * will deadlock. */
13843 {
13844 AutoCaller autoCaller(this);
13845 if (!autoCaller.isOk())
13846 {
13847 /* return true if not ready, to cause the client watcher to exclude
13848 * the corresponding session from watching */
13849 LogFlowThisFunc(("Already uninitialized!\n"));
13850 return true;
13851 }
13852
13853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13854
13855 /* Determine the reason of death: if the session state is Closing here,
13856 * everything is fine. Otherwise it means that the client did not call
13857 * OnSessionEnd() before it released the IPC semaphore. This may happen
13858 * either because the client process has abnormally terminated, or
13859 * because it simply forgot to call ISession::Close() before exiting. We
13860 * threat the latter also as an abnormal termination (see
13861 * Session::uninit() for details). */
13862 reason = mData->mSession.mState == SessionState_Unlocking ?
13863 Uninit::Normal :
13864 Uninit::Abnormal;
13865
13866 if (mClientToken)
13867 terminated = mClientToken->release();
13868 } /* AutoCaller block */
13869
13870 if (terminated)
13871 uninit(reason);
13872
13873 return terminated;
13874}
13875
13876void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13877{
13878 LogFlowThisFunc(("\n"));
13879
13880 strTokenId.setNull();
13881
13882 AutoCaller autoCaller(this);
13883 AssertComRCReturnVoid(autoCaller.hrc());
13884
13885 Assert(mClientToken);
13886 if (mClientToken)
13887 mClientToken->getId(strTokenId);
13888}
13889#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13890IToken *SessionMachine::i_getToken()
13891{
13892 LogFlowThisFunc(("\n"));
13893
13894 AutoCaller autoCaller(this);
13895 AssertComRCReturn(autoCaller.hrc(), NULL);
13896
13897 Assert(mClientToken);
13898 if (mClientToken)
13899 return mClientToken->getToken();
13900 else
13901 return NULL;
13902}
13903#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13904
13905Machine::ClientToken *SessionMachine::i_getClientToken()
13906{
13907 LogFlowThisFunc(("\n"));
13908
13909 AutoCaller autoCaller(this);
13910 AssertComRCReturn(autoCaller.hrc(), NULL);
13911
13912 return mClientToken;
13913}
13914
13915
13916/**
13917 * @note Locks this object for reading.
13918 */
13919HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13920{
13921 LogFlowThisFunc(("\n"));
13922
13923 AutoCaller autoCaller(this);
13924 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13925
13926 ComPtr<IInternalSessionControl> directControl;
13927 {
13928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13929 if (mData->mSession.mLockType == LockType_VM)
13930 directControl = mData->mSession.mDirectControl;
13931 }
13932
13933 /* ignore notifications sent after #OnSessionEnd() is called */
13934 if (!directControl)
13935 return S_OK;
13936
13937 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13938}
13939
13940/**
13941 * @note Locks this object for reading.
13942 */
13943HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13944 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13945 const Utf8Str &aGuestIp, LONG aGuestPort)
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13951
13952 ComPtr<IInternalSessionControl> directControl;
13953 {
13954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13955 if (mData->mSession.mLockType == LockType_VM)
13956 directControl = mData->mSession.mDirectControl;
13957 }
13958
13959 /* ignore notifications sent after #OnSessionEnd() is called */
13960 if (!directControl)
13961 return S_OK;
13962 /*
13963 * instead acting like callback we ask IVirtualBox deliver corresponding event
13964 */
13965
13966 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13967 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13968 return S_OK;
13969}
13970
13971/**
13972 * @note Locks this object for reading.
13973 */
13974HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnAudioAdapterChange(audioAdapter);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 /* ignore notifications sent after #OnSessionEnd() is called */
14037 if (!directControl)
14038 return S_OK;
14039
14040 return directControl->OnSerialPortChange(serialPort);
14041}
14042
14043/**
14044 * @note Locks this object for reading.
14045 */
14046HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnParallelPortChange(parallelPort);
14065}
14066
14067/**
14068 * @note Locks this object for reading.
14069 */
14070HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 AutoCaller autoCaller(this);
14075 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14076
14077 ComPtr<IInternalSessionControl> directControl;
14078 {
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080 if (mData->mSession.mLockType == LockType_VM)
14081 directControl = mData->mSession.mDirectControl;
14082 }
14083
14084 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14085
14086 /* ignore notifications sent after #OnSessionEnd() is called */
14087 if (!directControl)
14088 return S_OK;
14089
14090 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14091}
14092
14093/**
14094 * @note Locks this object for reading.
14095 */
14096HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
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 mParent->i_onMediumChanged(aAttachment);
14111
14112 /* ignore notifications sent after #OnSessionEnd() is called */
14113 if (!directControl)
14114 return S_OK;
14115
14116 return directControl->OnMediumChange(aAttachment, aForce);
14117}
14118
14119HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14120{
14121 LogFlowThisFunc(("\n"));
14122
14123 AutoCaller autoCaller(this);
14124 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14125
14126 ComPtr<IInternalSessionControl> directControl;
14127 {
14128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14129 if (mData->mSession.mLockType == LockType_VM)
14130 directControl = mData->mSession.mDirectControl;
14131 }
14132
14133 /* ignore notifications sent after #OnSessionEnd() is called */
14134 if (!directControl)
14135 return S_OK;
14136
14137 return directControl->OnVMProcessPriorityChange(aPriority);
14138}
14139
14140/**
14141 * @note Locks this object for reading.
14142 */
14143HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14144{
14145 LogFlowThisFunc(("\n"));
14146
14147 AutoCaller autoCaller(this);
14148 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14149
14150 ComPtr<IInternalSessionControl> directControl;
14151 {
14152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14153 if (mData->mSession.mLockType == LockType_VM)
14154 directControl = mData->mSession.mDirectControl;
14155 }
14156
14157 /* ignore notifications sent after #OnSessionEnd() is called */
14158 if (!directControl)
14159 return S_OK;
14160
14161 return directControl->OnCPUChange(aCPU, aRemove);
14162}
14163
14164HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14165{
14166 LogFlowThisFunc(("\n"));
14167
14168 AutoCaller autoCaller(this);
14169 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14170
14171 ComPtr<IInternalSessionControl> directControl;
14172 {
14173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14174 if (mData->mSession.mLockType == LockType_VM)
14175 directControl = mData->mSession.mDirectControl;
14176 }
14177
14178 /* ignore notifications sent after #OnSessionEnd() is called */
14179 if (!directControl)
14180 return S_OK;
14181
14182 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14183}
14184
14185/**
14186 * @note Locks this object for reading.
14187 */
14188HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14189{
14190 LogFlowThisFunc(("\n"));
14191
14192 AutoCaller autoCaller(this);
14193 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14194
14195 ComPtr<IInternalSessionControl> directControl;
14196 {
14197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14198 if (mData->mSession.mLockType == LockType_VM)
14199 directControl = mData->mSession.mDirectControl;
14200 }
14201
14202 /* ignore notifications sent after #OnSessionEnd() is called */
14203 if (!directControl)
14204 return S_OK;
14205
14206 return directControl->OnVRDEServerChange(aRestart);
14207}
14208
14209/**
14210 * @note Locks this object for reading.
14211 */
14212HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14213{
14214 LogFlowThisFunc(("\n"));
14215
14216 AutoCaller autoCaller(this);
14217 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14218
14219 ComPtr<IInternalSessionControl> directControl;
14220 {
14221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14222 if (mData->mSession.mLockType == LockType_VM)
14223 directControl = mData->mSession.mDirectControl;
14224 }
14225
14226 /* ignore notifications sent after #OnSessionEnd() is called */
14227 if (!directControl)
14228 return S_OK;
14229
14230 return directControl->OnRecordingChange(aEnable);
14231}
14232
14233/**
14234 * @note Locks this object for reading.
14235 */
14236HRESULT SessionMachine::i_onUSBControllerChange()
14237{
14238 LogFlowThisFunc(("\n"));
14239
14240 AutoCaller autoCaller(this);
14241 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14242
14243 ComPtr<IInternalSessionControl> directControl;
14244 {
14245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14246 if (mData->mSession.mLockType == LockType_VM)
14247 directControl = mData->mSession.mDirectControl;
14248 }
14249
14250 /* ignore notifications sent after #OnSessionEnd() is called */
14251 if (!directControl)
14252 return S_OK;
14253
14254 return directControl->OnUSBControllerChange();
14255}
14256
14257/**
14258 * @note Locks this object for reading.
14259 */
14260HRESULT SessionMachine::i_onSharedFolderChange()
14261{
14262 LogFlowThisFunc(("\n"));
14263
14264 AutoCaller autoCaller(this);
14265 AssertComRCReturnRC(autoCaller.hrc());
14266
14267 ComPtr<IInternalSessionControl> directControl;
14268 {
14269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14270 if (mData->mSession.mLockType == LockType_VM)
14271 directControl = mData->mSession.mDirectControl;
14272 }
14273
14274 /* ignore notifications sent after #OnSessionEnd() is called */
14275 if (!directControl)
14276 return S_OK;
14277
14278 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14279}
14280
14281/**
14282 * @note Locks this object for reading.
14283 */
14284HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14285{
14286 LogFlowThisFunc(("\n"));
14287
14288 AutoCaller autoCaller(this);
14289 AssertComRCReturnRC(autoCaller.hrc());
14290
14291 ComPtr<IInternalSessionControl> directControl;
14292 {
14293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14294 if (mData->mSession.mLockType == LockType_VM)
14295 directControl = mData->mSession.mDirectControl;
14296 }
14297
14298 /* ignore notifications sent after #OnSessionEnd() is called */
14299 if (!directControl)
14300 return S_OK;
14301
14302 return directControl->OnClipboardModeChange(aClipboardMode);
14303}
14304
14305/**
14306 * @note Locks this object for reading.
14307 */
14308HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14309{
14310 LogFlowThisFunc(("\n"));
14311
14312 AutoCaller autoCaller(this);
14313 AssertComRCReturnRC(autoCaller.hrc());
14314
14315 ComPtr<IInternalSessionControl> directControl;
14316 {
14317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14318 if (mData->mSession.mLockType == LockType_VM)
14319 directControl = mData->mSession.mDirectControl;
14320 }
14321
14322 /* ignore notifications sent after #OnSessionEnd() is called */
14323 if (!directControl)
14324 return S_OK;
14325
14326 return directControl->OnClipboardFileTransferModeChange(aEnable);
14327}
14328
14329/**
14330 * @note Locks this object for reading.
14331 */
14332HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14333{
14334 LogFlowThisFunc(("\n"));
14335
14336 AutoCaller autoCaller(this);
14337 AssertComRCReturnRC(autoCaller.hrc());
14338
14339 ComPtr<IInternalSessionControl> directControl;
14340 {
14341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14342 if (mData->mSession.mLockType == LockType_VM)
14343 directControl = mData->mSession.mDirectControl;
14344 }
14345
14346 /* ignore notifications sent after #OnSessionEnd() is called */
14347 if (!directControl)
14348 return S_OK;
14349
14350 return directControl->OnDnDModeChange(aDnDMode);
14351}
14352
14353/**
14354 * @note Locks this object for reading.
14355 */
14356HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14357{
14358 LogFlowThisFunc(("\n"));
14359
14360 AutoCaller autoCaller(this);
14361 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14362
14363 ComPtr<IInternalSessionControl> directControl;
14364 {
14365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14366 if (mData->mSession.mLockType == LockType_VM)
14367 directControl = mData->mSession.mDirectControl;
14368 }
14369
14370 /* ignore notifications sent after #OnSessionEnd() is called */
14371 if (!directControl)
14372 return S_OK;
14373
14374 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14375}
14376
14377/**
14378 * @note Locks this object for reading.
14379 */
14380HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14381{
14382 LogFlowThisFunc(("\n"));
14383
14384 AutoCaller autoCaller(this);
14385 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14386
14387 ComPtr<IInternalSessionControl> directControl;
14388 {
14389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14390 if (mData->mSession.mLockType == LockType_VM)
14391 directControl = mData->mSession.mDirectControl;
14392 }
14393
14394 /* ignore notifications sent after #OnSessionEnd() is called */
14395 if (!directControl)
14396 return S_OK;
14397
14398 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14399}
14400
14401/**
14402 * @note Locks this object for reading.
14403 */
14404HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14405{
14406 LogFlowThisFunc(("\n"));
14407
14408 AutoCaller autoCaller(this);
14409 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14410
14411 ComPtr<IInternalSessionControl> directControl;
14412 {
14413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14414 if (mData->mSession.mLockType == LockType_VM)
14415 directControl = mData->mSession.mDirectControl;
14416 }
14417
14418 /* ignore notifications sent after #OnSessionEnd() is called */
14419 if (!directControl)
14420 return S_OK;
14421
14422 return directControl->OnGuestDebugControlChange(guestDebugControl);
14423}
14424
14425/**
14426 * Returns @c true if this machine's USB controller reports it has a matching
14427 * filter for the given USB device and @c false otherwise.
14428 *
14429 * @note locks this object for reading.
14430 */
14431bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14432{
14433 AutoCaller autoCaller(this);
14434 /* silently return if not ready -- this method may be called after the
14435 * direct machine session has been called */
14436 if (!autoCaller.isOk())
14437 return false;
14438
14439#ifdef VBOX_WITH_USB
14440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14441
14442 switch (mData->mMachineState)
14443 {
14444 case MachineState_Starting:
14445 case MachineState_Restoring:
14446 case MachineState_TeleportingIn:
14447 case MachineState_Paused:
14448 case MachineState_Running:
14449 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14450 * elsewhere... */
14451 alock.release();
14452 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14453 default: break;
14454 }
14455#else
14456 NOREF(aDevice);
14457 NOREF(aMaskedIfs);
14458#endif
14459 return false;
14460}
14461
14462/**
14463 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14464 */
14465HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14466 IVirtualBoxErrorInfo *aError,
14467 ULONG aMaskedIfs,
14468 const com::Utf8Str &aCaptureFilename)
14469{
14470 LogFlowThisFunc(("\n"));
14471
14472 AutoCaller autoCaller(this);
14473
14474 /* This notification may happen after the machine object has been
14475 * uninitialized (the session was closed), so don't assert. */
14476 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14477
14478 ComPtr<IInternalSessionControl> directControl;
14479 {
14480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14481 if (mData->mSession.mLockType == LockType_VM)
14482 directControl = mData->mSession.mDirectControl;
14483 }
14484
14485 /* fail on notifications sent after #OnSessionEnd() is called, it is
14486 * expected by the caller */
14487 if (!directControl)
14488 return E_FAIL;
14489
14490 /* No locks should be held at this point. */
14491 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14492 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14493
14494 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14495}
14496
14497/**
14498 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14499 */
14500HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14501 IVirtualBoxErrorInfo *aError)
14502{
14503 LogFlowThisFunc(("\n"));
14504
14505 AutoCaller autoCaller(this);
14506
14507 /* This notification may happen after the machine object has been
14508 * uninitialized (the session was closed), so don't assert. */
14509 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
14510
14511 ComPtr<IInternalSessionControl> directControl;
14512 {
14513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14514 if (mData->mSession.mLockType == LockType_VM)
14515 directControl = mData->mSession.mDirectControl;
14516 }
14517
14518 /* fail on notifications sent after #OnSessionEnd() is called, it is
14519 * expected by the caller */
14520 if (!directControl)
14521 return E_FAIL;
14522
14523 /* No locks should be held at this point. */
14524 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14525 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14526
14527 return directControl->OnUSBDeviceDetach(aId, aError);
14528}
14529
14530// protected methods
14531/////////////////////////////////////////////////////////////////////////////
14532
14533/**
14534 * Deletes the given file if it is no longer in use by either the current machine state
14535 * (if the machine is "saved") or any of the machine's snapshots.
14536 *
14537 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14538 * but is different for each SnapshotMachine. When calling this, the order of calling this
14539 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14540 * is therefore critical. I know, it's all rather messy.
14541 *
14542 * @param strStateFile
14543 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14544 * the test for whether the saved state file is in use.
14545 */
14546void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14547 Snapshot *pSnapshotToIgnore)
14548{
14549 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14550 if ( (strStateFile.isNotEmpty())
14551 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14552 )
14553 // ... and it must also not be shared with other snapshots
14554 if ( !mData->mFirstSnapshot
14555 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14556 // this checks the SnapshotMachine's state file paths
14557 )
14558 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
14559}
14560
14561/**
14562 * Locks the attached media.
14563 *
14564 * All attached hard disks are locked for writing and DVD/floppy are locked for
14565 * reading. Parents of attached hard disks (if any) are locked for reading.
14566 *
14567 * This method also performs accessibility check of all media it locks: if some
14568 * media is inaccessible, the method will return a failure and a bunch of
14569 * extended error info objects per each inaccessible medium.
14570 *
14571 * Note that this method is atomic: if it returns a success, all media are
14572 * locked as described above; on failure no media is locked at all (all
14573 * succeeded individual locks will be undone).
14574 *
14575 * The caller is responsible for doing the necessary state sanity checks.
14576 *
14577 * The locks made by this method must be undone by calling #unlockMedia() when
14578 * no more needed.
14579 */
14580HRESULT SessionMachine::i_lockMedia()
14581{
14582 AutoCaller autoCaller(this);
14583 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14584
14585 AutoMultiWriteLock2 alock(this->lockHandle(),
14586 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14587
14588 /* bail out if trying to lock things with already set up locking */
14589 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14590
14591 MultiResult hrcMult(S_OK);
14592
14593 /* Collect locking information for all medium objects attached to the VM. */
14594 for (MediumAttachmentList::const_iterator
14595 it = mMediumAttachments->begin();
14596 it != mMediumAttachments->end();
14597 ++it)
14598 {
14599 MediumAttachment *pAtt = *it;
14600 DeviceType_T devType = pAtt->i_getType();
14601 Medium *pMedium = pAtt->i_getMedium();
14602
14603 MediumLockList *pMediumLockList(new MediumLockList());
14604 // There can be attachments without a medium (floppy/dvd), and thus
14605 // it's impossible to create a medium lock list. It still makes sense
14606 // to have the empty medium lock list in the map in case a medium is
14607 // attached later.
14608 if (pMedium != NULL)
14609 {
14610 MediumType_T mediumType = pMedium->i_getType();
14611 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14612 || mediumType == MediumType_Shareable;
14613 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14614
14615 alock.release();
14616 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14617 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14618 false /* fMediumLockWriteAll */,
14619 NULL,
14620 *pMediumLockList);
14621 alock.acquire();
14622 if (FAILED(hrcMult))
14623 {
14624 delete pMediumLockList;
14625 mData->mSession.mLockedMedia.Clear();
14626 break;
14627 }
14628 }
14629
14630 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14631 if (FAILED(hrc))
14632 {
14633 mData->mSession.mLockedMedia.Clear();
14634 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
14635 break;
14636 }
14637 }
14638
14639 if (SUCCEEDED(hrcMult))
14640 {
14641 /* Now lock all media. If this fails, nothing is locked. */
14642 alock.release();
14643 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
14644 alock.acquire();
14645 if (FAILED(hrc))
14646 hrcMult = setError(hrc,
14647 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14648 }
14649
14650 return hrcMult;
14651}
14652
14653/**
14654 * Undoes the locks made by by #lockMedia().
14655 */
14656HRESULT SessionMachine::i_unlockMedia()
14657{
14658 AutoCaller autoCaller(this);
14659 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
14660
14661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14662
14663 /* we may be holding important error info on the current thread;
14664 * preserve it */
14665 ErrorInfoKeeper eik;
14666
14667 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
14668 AssertComRC(hrc);
14669 return hrc;
14670}
14671
14672/**
14673 * Helper to change the machine state (reimplementation).
14674 *
14675 * @note Locks this object for writing.
14676 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14677 * it can cause crashes in random places due to unexpectedly committing
14678 * the current settings. The caller is responsible for that. The call
14679 * to saveStateSettings is fine, because this method does not commit.
14680 */
14681HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14682{
14683 LogFlowThisFuncEnter();
14684
14685 AutoCaller autoCaller(this);
14686 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14687
14688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14689
14690 MachineState_T oldMachineState = mData->mMachineState;
14691
14692 AssertMsgReturn(oldMachineState != aMachineState,
14693 ("oldMachineState=%s, aMachineState=%s\n",
14694 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14695 E_FAIL);
14696
14697 HRESULT hrc = S_OK;
14698
14699 int stsFlags = 0;
14700 bool deleteSavedState = false;
14701
14702 /* detect some state transitions */
14703
14704 if ( ( ( oldMachineState == MachineState_Saved
14705 || oldMachineState == MachineState_AbortedSaved
14706 )
14707 && aMachineState == MachineState_Restoring
14708 )
14709 || ( ( oldMachineState == MachineState_PoweredOff
14710 || oldMachineState == MachineState_Teleported
14711 || oldMachineState == MachineState_Aborted
14712 )
14713 && ( aMachineState == MachineState_TeleportingIn
14714 || aMachineState == MachineState_Starting
14715 )
14716 )
14717 )
14718 {
14719 /* The EMT thread is about to start */
14720
14721 /* Nothing to do here for now... */
14722
14723 /// @todo NEWMEDIA don't let mDVDDrive and other children
14724 /// change anything when in the Starting/Restoring state
14725 }
14726 else if ( ( oldMachineState == MachineState_Running
14727 || oldMachineState == MachineState_Paused
14728 || oldMachineState == MachineState_Teleporting
14729 || oldMachineState == MachineState_OnlineSnapshotting
14730 || oldMachineState == MachineState_LiveSnapshotting
14731 || oldMachineState == MachineState_Stuck
14732 || oldMachineState == MachineState_Starting
14733 || oldMachineState == MachineState_Stopping
14734 || oldMachineState == MachineState_Saving
14735 || oldMachineState == MachineState_Restoring
14736 || oldMachineState == MachineState_TeleportingPausedVM
14737 || oldMachineState == MachineState_TeleportingIn
14738 )
14739 && ( aMachineState == MachineState_PoweredOff
14740 || aMachineState == MachineState_Saved
14741 || aMachineState == MachineState_Teleported
14742 || aMachineState == MachineState_Aborted
14743 || aMachineState == MachineState_AbortedSaved
14744 )
14745 )
14746 {
14747 /* The EMT thread has just stopped, unlock attached media. Note that as
14748 * opposed to locking that is done from Console, we do unlocking here
14749 * because the VM process may have aborted before having a chance to
14750 * properly unlock all media it locked. */
14751
14752 unlockMedia();
14753 }
14754
14755 if (oldMachineState == MachineState_Restoring)
14756 {
14757 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14758 {
14759 /*
14760 * delete the saved state file once the machine has finished
14761 * restoring from it (note that Console sets the state from
14762 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14763 * to give the user an ability to fix an error and retry --
14764 * we keep the saved state file in this case)
14765 */
14766 deleteSavedState = true;
14767 }
14768 }
14769 else if ( ( oldMachineState == MachineState_Saved
14770 || oldMachineState == MachineState_AbortedSaved
14771 )
14772 && ( aMachineState == MachineState_PoweredOff
14773 || aMachineState == MachineState_Teleported
14774 )
14775 )
14776 {
14777 /* delete the saved state after SessionMachine::discardSavedState() is called */
14778 deleteSavedState = true;
14779 mData->mCurrentStateModified = TRUE;
14780 stsFlags |= SaveSTS_CurStateModified;
14781 }
14782 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14783 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14784
14785 if ( aMachineState == MachineState_Starting
14786 || aMachineState == MachineState_Restoring
14787 || aMachineState == MachineState_TeleportingIn
14788 )
14789 {
14790 /* set the current state modified flag to indicate that the current
14791 * state is no more identical to the state in the
14792 * current snapshot */
14793 if (!mData->mCurrentSnapshot.isNull())
14794 {
14795 mData->mCurrentStateModified = TRUE;
14796 stsFlags |= SaveSTS_CurStateModified;
14797 }
14798 }
14799
14800 if (deleteSavedState)
14801 {
14802 if (mRemoveSavedState)
14803 {
14804 Assert(!mSSData->strStateFilePath.isEmpty());
14805
14806 // it is safe to delete the saved state file if ...
14807 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14808 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14809 // ... none of the snapshots share the saved state file
14810 )
14811 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
14812 }
14813
14814 mSSData->strStateFilePath.setNull();
14815 stsFlags |= SaveSTS_StateFilePath;
14816 }
14817
14818 /* redirect to the underlying peer machine */
14819 mPeer->i_setMachineState(aMachineState);
14820
14821 if ( oldMachineState != MachineState_RestoringSnapshot
14822 && ( aMachineState == MachineState_PoweredOff
14823 || aMachineState == MachineState_Teleported
14824 || aMachineState == MachineState_Aborted
14825 || aMachineState == MachineState_AbortedSaved
14826 || aMachineState == MachineState_Saved))
14827 {
14828 /* the machine has stopped execution
14829 * (or the saved state file was adopted) */
14830 stsFlags |= SaveSTS_StateTimeStamp;
14831 }
14832
14833 if ( ( oldMachineState == MachineState_PoweredOff
14834 || oldMachineState == MachineState_Aborted
14835 || oldMachineState == MachineState_Teleported
14836 )
14837 && aMachineState == MachineState_Saved)
14838 {
14839 /* the saved state file was adopted */
14840 Assert(!mSSData->strStateFilePath.isEmpty());
14841 stsFlags |= SaveSTS_StateFilePath;
14842 }
14843
14844#ifdef VBOX_WITH_GUEST_PROPS
14845 if ( aMachineState == MachineState_PoweredOff
14846 || aMachineState == MachineState_Aborted
14847 || aMachineState == MachineState_Teleported)
14848 {
14849 /* Make sure any transient guest properties get removed from the
14850 * property store on shutdown. */
14851 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14852
14853 /* remove it from the settings representation */
14854 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14855 for (settings::GuestPropertiesList::iterator
14856 it = llGuestProperties.begin();
14857 it != llGuestProperties.end();
14858 /*nothing*/)
14859 {
14860 const settings::GuestProperty &prop = *it;
14861 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14862 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14863 {
14864 it = llGuestProperties.erase(it);
14865 fNeedsSaving = true;
14866 }
14867 else
14868 {
14869 ++it;
14870 }
14871 }
14872
14873 /* Additionally remove it from the HWData representation. Required to
14874 * keep everything in sync, as this is what the API keeps using. */
14875 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14876 for (HWData::GuestPropertyMap::iterator
14877 it = llHWGuestProperties.begin();
14878 it != llHWGuestProperties.end();
14879 /*nothing*/)
14880 {
14881 uint32_t fFlags = it->second.mFlags;
14882 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14883 {
14884 /* iterator where we need to continue after the erase call
14885 * (C++03 is a fact still, and it doesn't return the iterator
14886 * which would allow continuing) */
14887 HWData::GuestPropertyMap::iterator it2 = it;
14888 ++it2;
14889 llHWGuestProperties.erase(it);
14890 it = it2;
14891 fNeedsSaving = true;
14892 }
14893 else
14894 {
14895 ++it;
14896 }
14897 }
14898
14899 if (fNeedsSaving)
14900 {
14901 mData->mCurrentStateModified = TRUE;
14902 stsFlags |= SaveSTS_CurStateModified;
14903 }
14904 }
14905#endif /* VBOX_WITH_GUEST_PROPS */
14906
14907 hrc = i_saveStateSettings(stsFlags);
14908
14909 if ( ( oldMachineState != MachineState_PoweredOff
14910 && oldMachineState != MachineState_Aborted
14911 && oldMachineState != MachineState_Teleported
14912 )
14913 && ( aMachineState == MachineState_PoweredOff
14914 || aMachineState == MachineState_Aborted
14915 || aMachineState == MachineState_Teleported
14916 )
14917 )
14918 {
14919 /* we've been shut down for any reason */
14920 /* no special action so far */
14921 }
14922
14923 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
14924 LogFlowThisFuncLeave();
14925 return hrc;
14926}
14927
14928/**
14929 * Sends the current machine state value to the VM process.
14930 *
14931 * @note Locks this object for reading, then calls a client process.
14932 */
14933HRESULT SessionMachine::i_updateMachineStateOnClient()
14934{
14935 AutoCaller autoCaller(this);
14936 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14937
14938 ComPtr<IInternalSessionControl> directControl;
14939 {
14940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14941 AssertReturn(!!mData, E_FAIL);
14942 if (mData->mSession.mLockType == LockType_VM)
14943 directControl = mData->mSession.mDirectControl;
14944
14945 /* directControl may be already set to NULL here in #OnSessionEnd()
14946 * called too early by the direct session process while there is still
14947 * some operation (like deleting the snapshot) in progress. The client
14948 * process in this case is waiting inside Session::close() for the
14949 * "end session" process object to complete, while #uninit() called by
14950 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14951 * operation to complete. For now, we accept this inconsistent behavior
14952 * and simply do nothing here. */
14953
14954 if (mData->mSession.mState == SessionState_Unlocking)
14955 return S_OK;
14956 }
14957
14958 /* ignore notifications sent after #OnSessionEnd() is called */
14959 if (!directControl)
14960 return S_OK;
14961
14962 return directControl->UpdateMachineState(mData->mMachineState);
14963}
14964
14965
14966/*static*/
14967HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14968{
14969 va_list args;
14970 va_start(args, pcszMsg);
14971 HRESULT hrc = setErrorInternalV(aResultCode,
14972 getStaticClassIID(),
14973 getStaticComponentName(),
14974 pcszMsg, args,
14975 false /* aWarning */,
14976 true /* aLogIt */);
14977 va_end(args);
14978 return hrc;
14979}
14980
14981
14982HRESULT Machine::updateState(MachineState_T aState)
14983{
14984 NOREF(aState);
14985 ReturnComNotImplemented();
14986}
14987
14988HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14989{
14990 NOREF(aProgress);
14991 ReturnComNotImplemented();
14992}
14993
14994HRESULT Machine::endPowerUp(LONG aResult)
14995{
14996 NOREF(aResult);
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15001{
15002 NOREF(aProgress);
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::endPoweringDown(LONG aResult,
15007 const com::Utf8Str &aErrMsg)
15008{
15009 NOREF(aResult);
15010 NOREF(aErrMsg);
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15015 BOOL *aMatched,
15016 ULONG *aMaskedInterfaces)
15017{
15018 NOREF(aDevice);
15019 NOREF(aMatched);
15020 NOREF(aMaskedInterfaces);
15021 ReturnComNotImplemented();
15022
15023}
15024
15025HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15026{
15027 NOREF(aId); NOREF(aCaptureFilename);
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15032 BOOL aDone)
15033{
15034 NOREF(aId);
15035 NOREF(aDone);
15036 ReturnComNotImplemented();
15037}
15038
15039HRESULT Machine::autoCaptureUSBDevices()
15040{
15041 ReturnComNotImplemented();
15042}
15043
15044HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15045{
15046 NOREF(aDone);
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15051 ComPtr<IProgress> &aProgress)
15052{
15053 NOREF(aSession);
15054 NOREF(aProgress);
15055 ReturnComNotImplemented();
15056}
15057
15058HRESULT Machine::finishOnlineMergeMedium()
15059{
15060 ReturnComNotImplemented();
15061}
15062
15063HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15064 std::vector<com::Utf8Str> &aValues,
15065 std::vector<LONG64> &aTimestamps,
15066 std::vector<com::Utf8Str> &aFlags)
15067{
15068 NOREF(aNames);
15069 NOREF(aValues);
15070 NOREF(aTimestamps);
15071 NOREF(aFlags);
15072 ReturnComNotImplemented();
15073}
15074
15075HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15076 const com::Utf8Str &aValue,
15077 LONG64 aTimestamp,
15078 const com::Utf8Str &aFlags,
15079 BOOL fWasDeleted)
15080{
15081 NOREF(aName);
15082 NOREF(aValue);
15083 NOREF(aTimestamp);
15084 NOREF(aFlags);
15085 NOREF(fWasDeleted);
15086 ReturnComNotImplemented();
15087}
15088
15089HRESULT Machine::lockMedia()
15090{
15091 ReturnComNotImplemented();
15092}
15093
15094HRESULT Machine::unlockMedia()
15095{
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15100 ComPtr<IMediumAttachment> &aNewAttachment)
15101{
15102 NOREF(aAttachment);
15103 NOREF(aNewAttachment);
15104 ReturnComNotImplemented();
15105}
15106
15107HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15108 ULONG aCpuUser,
15109 ULONG aCpuKernel,
15110 ULONG aCpuIdle,
15111 ULONG aMemTotal,
15112 ULONG aMemFree,
15113 ULONG aMemBalloon,
15114 ULONG aMemShared,
15115 ULONG aMemCache,
15116 ULONG aPagedTotal,
15117 ULONG aMemAllocTotal,
15118 ULONG aMemFreeTotal,
15119 ULONG aMemBalloonTotal,
15120 ULONG aMemSharedTotal,
15121 ULONG aVmNetRx,
15122 ULONG aVmNetTx)
15123{
15124 NOREF(aValidStats);
15125 NOREF(aCpuUser);
15126 NOREF(aCpuKernel);
15127 NOREF(aCpuIdle);
15128 NOREF(aMemTotal);
15129 NOREF(aMemFree);
15130 NOREF(aMemBalloon);
15131 NOREF(aMemShared);
15132 NOREF(aMemCache);
15133 NOREF(aPagedTotal);
15134 NOREF(aMemAllocTotal);
15135 NOREF(aMemFreeTotal);
15136 NOREF(aMemBalloonTotal);
15137 NOREF(aMemSharedTotal);
15138 NOREF(aVmNetRx);
15139 NOREF(aVmNetTx);
15140 ReturnComNotImplemented();
15141}
15142
15143HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15144 com::Utf8Str &aResult)
15145{
15146 NOREF(aAuthParams);
15147 NOREF(aResult);
15148 ReturnComNotImplemented();
15149}
15150
15151HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15152{
15153 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15154
15155 AutoCaller autoCaller(this);
15156 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15157
15158 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15159 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15160 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15161 if (FAILED(hrc)) return hrc;
15162
15163 NOREF(aFlags);
15164 com::Utf8Str osTypeId;
15165 ComObjPtr<GuestOSType> osType = NULL;
15166
15167 /* Get the guest os type as a string from the VB. */
15168 hrc = getOSTypeId(osTypeId);
15169 if (FAILED(hrc)) return hrc;
15170
15171 /* Get the os type obj that coresponds, can be used to get
15172 * the defaults for this guest OS. */
15173 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15174 if (FAILED(hrc)) return hrc;
15175
15176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15177
15178 mPlatform->i_applyDefaults(osType);
15179
15180 /* This one covers IOAPICEnabled. */
15181 mFirmwareSettings->i_applyDefaults(osType);
15182
15183 /* Initialize default record settings. */
15184 mRecordingSettings->i_applyDefaults();
15185
15186 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15187 if (FAILED(hrc)) return hrc;
15188
15189 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15190 if (FAILED(hrc)) return hrc;
15191
15192 /* Graphics stuff. */
15193 GraphicsControllerType_T graphicsController;
15194 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15195 if (FAILED(hrc)) return hrc;
15196
15197 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15198 if (FAILED(hrc)) return hrc;
15199
15200 ULONG vramSize;
15201 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15202 if (FAILED(hrc)) return hrc;
15203
15204 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15205 if (FAILED(hrc)) return hrc;
15206
15207 BOOL fAccelerate2DVideoEnabled;
15208 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15209 if (FAILED(hrc)) return hrc;
15210
15211 hrc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15212 if (FAILED(hrc)) return hrc;
15213
15214 BOOL fAccelerate3DEnabled;
15215 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15216 if (FAILED(hrc)) return hrc;
15217
15218 hrc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15219 if (FAILED(hrc)) return hrc;
15220
15221 /* Apply network adapters defaults */
15222 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15223 mNetworkAdapters[slot]->i_applyDefaults(osType);
15224
15225 /* Apply serial port defaults */
15226 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15227 mSerialPorts[slot]->i_applyDefaults(osType);
15228
15229 /* Apply parallel port defaults - not OS dependent*/
15230 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15231 mParallelPorts[slot]->i_applyDefaults();
15232
15233 /* This one covers the TPM type. */
15234 mTrustedPlatformModule->i_applyDefaults(osType);
15235
15236 /* This one covers secure boot. */
15237 hrc = mNvramStore->i_applyDefaults(osType);
15238 if (FAILED(hrc)) return hrc;
15239
15240 /* Audio stuff. */
15241 hrc = mAudioSettings->i_applyDefaults(osType);
15242 if (FAILED(hrc)) return hrc;
15243
15244 /* Storage Controllers */
15245 StorageControllerType_T hdStorageControllerType;
15246 StorageBus_T hdStorageBusType;
15247 StorageControllerType_T dvdStorageControllerType;
15248 StorageBus_T dvdStorageBusType;
15249 BOOL recommendedFloppy;
15250 ComPtr<IStorageController> floppyController;
15251 ComPtr<IStorageController> hdController;
15252 ComPtr<IStorageController> dvdController;
15253 Utf8Str strFloppyName, strDVDName, strHDName;
15254
15255 /* GUI auto generates controller names using bus type. Do the same*/
15256 strFloppyName = StorageController::i_controllerNameFromBusType(StorageBus_Floppy);
15257
15258 /* Floppy recommended? add one. */
15259 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15260 if (FAILED(hrc)) return hrc;
15261 if (recommendedFloppy)
15262 {
15263 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15264 if (FAILED(hrc)) return hrc;
15265 }
15266
15267 /* Setup one DVD storage controller. */
15268 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15269 if (FAILED(hrc)) return hrc;
15270
15271 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15272 if (FAILED(hrc)) return hrc;
15273
15274 strDVDName = StorageController::i_controllerNameFromBusType(dvdStorageBusType);
15275
15276 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15277 if (FAILED(hrc)) return hrc;
15278
15279 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15280 if (FAILED(hrc)) return hrc;
15281
15282 /* Setup one HDD storage controller. */
15283 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15284 if (FAILED(hrc)) return hrc;
15285
15286 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15287 if (FAILED(hrc)) return hrc;
15288
15289 strHDName = StorageController::i_controllerNameFromBusType(hdStorageBusType);
15290
15291 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15292 {
15293 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15294 if (FAILED(hrc)) return hrc;
15295
15296 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15297 if (FAILED(hrc)) return hrc;
15298 }
15299 else
15300 {
15301 /* The HD controller is the same as DVD: */
15302 hdController = dvdController;
15303 }
15304
15305 /* Limit the AHCI port count if it's used because windows has trouble with
15306 * too many ports and other guest (OS X in particular) may take extra long
15307 * boot: */
15308
15309 // pParent = static_cast<Medium*>(aP)
15310 IStorageController *temp = hdController;
15311 ComObjPtr<StorageController> storageController;
15312 storageController = static_cast<StorageController *>(temp);
15313
15314 // tempHDController = aHDController;
15315 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15316 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15317 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15318 storageController->COMSETTER(PortCount)(1);
15319
15320 /* VirtioSCSI configures only one port per default -- set two ports here, one for HDD and one for DVD drive. */
15321 if (hdStorageControllerType == StorageControllerType_VirtioSCSI)
15322 {
15323 hrc = storageController->COMSETTER(PortCount)(2);
15324 if (FAILED(hrc)) return hrc;
15325 }
15326
15327 /* USB stuff */
15328
15329 bool ohciEnabled = false;
15330
15331 ComPtr<IUSBController> usbController;
15332 BOOL recommendedUSB3;
15333 BOOL recommendedUSB;
15334 BOOL usbProxyAvailable;
15335
15336 getUSBProxyAvailable(&usbProxyAvailable);
15337 if (FAILED(hrc)) return hrc;
15338
15339 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15340 if (FAILED(hrc)) return hrc;
15341 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15342 if (FAILED(hrc)) return hrc;
15343
15344 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15345 {
15346 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15347 if (FAILED(hrc)) return hrc;
15348
15349 /* xHci includes OHCI */
15350 ohciEnabled = true;
15351 }
15352 if ( !ohciEnabled
15353 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15354 {
15355 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15356 if (FAILED(hrc)) return hrc;
15357 ohciEnabled = true;
15358
15359 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15360 if (FAILED(hrc)) return hrc;
15361 }
15362
15363 /* Set recommended human interface device types: */
15364 BOOL recommendedUSBHID;
15365 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15366 if (FAILED(hrc)) return hrc;
15367
15368 if (recommendedUSBHID)
15369 {
15370 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15371 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15372 if (!ohciEnabled && !usbDeviceFilters.isNull())
15373 {
15374 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15375 if (FAILED(hrc)) return hrc;
15376 }
15377 }
15378
15379 BOOL recommendedUSBTablet;
15380 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15381 if (FAILED(hrc)) return hrc;
15382
15383 if (recommendedUSBTablet)
15384 {
15385 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15386 if (!ohciEnabled && !usbDeviceFilters.isNull())
15387 {
15388 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15389 if (FAILED(hrc)) return hrc;
15390 }
15391 }
15392
15393 /* Enable the VMMDev testing feature for bootsector VMs: */
15394 if (osTypeId == GUEST_OS_ID_STR_X64("VBoxBS"))
15395 {
15396 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15397 if (FAILED(hrc))
15398 return hrc;
15399 }
15400
15401 return S_OK;
15402}
15403
15404#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15405/**
15406 * Task record for change encryption settins.
15407 */
15408class Machine::ChangeEncryptionTask
15409 : public Machine::Task
15410{
15411public:
15412 ChangeEncryptionTask(Machine *m,
15413 Progress *p,
15414 const Utf8Str &t,
15415 const com::Utf8Str &aCurrentPassword,
15416 const com::Utf8Str &aCipher,
15417 const com::Utf8Str &aNewPassword,
15418 const com::Utf8Str &aNewPasswordId,
15419 const BOOL aForce,
15420 const MediaList &llMedia)
15421 : Task(m, p, t),
15422 mstrNewPassword(aNewPassword),
15423 mstrCurrentPassword(aCurrentPassword),
15424 mstrCipher(aCipher),
15425 mstrNewPasswordId(aNewPasswordId),
15426 mForce(aForce),
15427 mllMedia(llMedia)
15428 {}
15429
15430 ~ChangeEncryptionTask()
15431 {
15432 if (mstrNewPassword.length())
15433 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15434 if (mstrCurrentPassword.length())
15435 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15436 if (m_pCryptoIf)
15437 {
15438 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15439 m_pCryptoIf = NULL;
15440 }
15441 }
15442
15443 Utf8Str mstrNewPassword;
15444 Utf8Str mstrCurrentPassword;
15445 Utf8Str mstrCipher;
15446 Utf8Str mstrNewPasswordId;
15447 BOOL mForce;
15448 MediaList mllMedia;
15449 PCVBOXCRYPTOIF m_pCryptoIf;
15450private:
15451 void handler()
15452 {
15453 try
15454 {
15455 m_pMachine->i_changeEncryptionHandler(*this);
15456 }
15457 catch (...)
15458 {
15459 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
15460 }
15461 }
15462
15463 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
15464};
15465
15466/**
15467 * Scans specified directory and fills list by files found
15468 *
15469 * @returns VBox status code.
15470 * @param lstFiles
15471 * @param strDir
15472 * @param filePattern
15473 */
15474int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
15475 const com::Utf8Str &strPattern)
15476{
15477 /* To get all entries including subdirectories. */
15478 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
15479 if (!pszFilePattern)
15480 return VERR_NO_STR_MEMORY;
15481
15482 PRTDIRENTRYEX pDirEntry = NULL;
15483 RTDIR hDir;
15484 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
15485 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
15486 if (RT_SUCCESS(vrc))
15487 {
15488 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
15489 if (pDirEntry)
15490 {
15491 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
15492 != VERR_NO_MORE_FILES)
15493 {
15494 char *pszFilePath = NULL;
15495
15496 if (vrc == VERR_BUFFER_OVERFLOW)
15497 {
15498 /* allocate new buffer. */
15499 RTMemFree(pDirEntry);
15500 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
15501 if (!pDirEntry)
15502 {
15503 vrc = VERR_NO_MEMORY;
15504 break;
15505 }
15506 /* Retry. */
15507 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
15508 if (RT_FAILURE(vrc))
15509 break;
15510 }
15511 else if (RT_FAILURE(vrc))
15512 break;
15513
15514 /* Exclude . and .. */
15515 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
15516 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
15517 continue;
15518 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
15519 {
15520 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15521 if (!pszSubDirPath)
15522 {
15523 vrc = VERR_NO_STR_MEMORY;
15524 break;
15525 }
15526 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
15527 RTMemFree(pszSubDirPath);
15528 if (RT_FAILURE(vrc))
15529 break;
15530 continue;
15531 }
15532
15533 /* We got the new entry. */
15534 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
15535 continue;
15536
15537 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
15538 continue;
15539
15540 /* Prepend the path to the libraries. */
15541 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
15542 if (!pszFilePath)
15543 {
15544 vrc = VERR_NO_STR_MEMORY;
15545 break;
15546 }
15547
15548 lstFiles.push_back(pszFilePath);
15549 RTStrFree(pszFilePath);
15550 }
15551
15552 RTMemFree(pDirEntry);
15553 }
15554 else
15555 vrc = VERR_NO_MEMORY;
15556
15557 RTDirClose(hDir);
15558 }
15559 else
15560 {
15561 /* On Windows the above immediately signals that there are no
15562 * files matching, while on other platforms enumerating the
15563 * files below fails. Either way: stop searching. */
15564 }
15565
15566 if ( vrc == VERR_NO_MORE_FILES
15567 || vrc == VERR_FILE_NOT_FOUND
15568 || vrc == VERR_PATH_NOT_FOUND)
15569 vrc = VINF_SUCCESS;
15570 RTStrFree(pszFilePattern);
15571 return vrc;
15572}
15573
15574/**
15575 * Helper to set up an I/O stream to read or write a possibly encrypted file.
15576 *
15577 * @returns VBox status code.
15578 * @param pszFilename The file to open.
15579 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
15580 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
15581 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
15582 * @param fOpen The open flags for the file.
15583 * @param phVfsIos Where to store the handle to the I/O stream on success.
15584 */
15585int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
15586 const char *pszKeyStore, const char *pszPassword,
15587 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
15588{
15589 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
15590 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
15591 if (RT_SUCCESS(vrc))
15592 {
15593 if (pCryptoIf)
15594 {
15595 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
15596 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
15597 if (RT_SUCCESS(vrc))
15598 {
15599 RTVfsFileRelease(hVfsFile);
15600 hVfsFile = hVfsFileCrypto;
15601 }
15602 }
15603
15604 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
15605 RTVfsFileRelease(hVfsFile);
15606 }
15607
15608 return vrc;
15609}
15610
15611/**
15612 * Helper function processing all actions for one component (saved state files,
15613 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
15614 *
15615 * @param task
15616 * @param strDirectory
15617 * @param strFilePattern
15618 * @param strMagic
15619 * @param strKeyStore
15620 * @param strKeyId
15621 * @return
15622 */
15623HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
15624 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
15625 com::Utf8Str &strKeyId, int iCipherMode)
15626{
15627 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
15628 && task.mstrCipher.isEmpty()
15629 && task.mstrNewPassword.isEmpty()
15630 && task.mstrNewPasswordId.isEmpty();
15631 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
15632 && task.mstrCipher.isNotEmpty()
15633 && task.mstrNewPassword.isNotEmpty()
15634 && task.mstrNewPasswordId.isNotEmpty();
15635
15636 /* check if the cipher is changed which causes the reencryption*/
15637
15638 const char *pszTaskCipher = NULL;
15639 if (task.mstrCipher.isNotEmpty())
15640 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
15641
15642 if (!task.mForce && !fDecrypt && !fEncrypt)
15643 {
15644 char *pszCipher = NULL;
15645 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
15646 NULL /*pszPassword*/,
15647 NULL /*ppbKey*/,
15648 NULL /*pcbKey*/,
15649 &pszCipher);
15650 if (RT_SUCCESS(vrc))
15651 {
15652 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
15653 RTMemFree(pszCipher);
15654 }
15655 else
15656 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
15657 strFilePattern.c_str(), vrc);
15658 }
15659
15660 /* Only the password needs to be changed */
15661 if (!task.mForce && !fDecrypt && !fEncrypt)
15662 {
15663 Assert(task.m_pCryptoIf);
15664
15665 VBOXCRYPTOCTX hCryptoCtx;
15666 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
15667 if (RT_FAILURE(vrc))
15668 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
15669 strFilePattern.c_str(), vrc);
15670 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15671 if (RT_FAILURE(vrc))
15672 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
15673 strFilePattern.c_str(), vrc);
15674
15675 char *pszKeyStore = NULL;
15676 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15677 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15678 if (RT_FAILURE(vrc))
15679 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
15680 strFilePattern.c_str(), vrc);
15681 strKeyStore = pszKeyStore;
15682 RTMemFree(pszKeyStore);
15683 strKeyId = task.mstrNewPasswordId;
15684 return S_OK;
15685 }
15686
15687 /* Reencryption required */
15688 HRESULT hrc = S_OK;
15689 int vrc = VINF_SUCCESS;
15690
15691 std::list<com::Utf8Str> lstFiles;
15692 if (SUCCEEDED(hrc))
15693 {
15694 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
15695 if (RT_FAILURE(vrc))
15696 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
15697 }
15698 com::Utf8Str strNewKeyStore;
15699 if (SUCCEEDED(hrc))
15700 {
15701 if (!fDecrypt)
15702 {
15703 VBOXCRYPTOCTX hCryptoCtx;
15704 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
15705 if (RT_FAILURE(vrc))
15706 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
15707 strFilePattern.c_str(), vrc);
15708
15709 char *pszKeyStore = NULL;
15710 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15711 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15712 if (RT_FAILURE(vrc))
15713 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
15714 strFilePattern.c_str(), vrc);
15715 strNewKeyStore = pszKeyStore;
15716 RTMemFree(pszKeyStore);
15717 }
15718
15719 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15720 it != lstFiles.end();
15721 ++it)
15722 {
15723 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
15724 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
15725
15726 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
15727 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
15728
15729 vrc = i_createIoStreamForFile((*it).c_str(),
15730 fEncrypt ? NULL : task.m_pCryptoIf,
15731 fEncrypt ? NULL : strKeyStore.c_str(),
15732 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
15733 fOpenForRead, &hVfsIosOld);
15734 if (RT_SUCCESS(vrc))
15735 {
15736 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
15737 fDecrypt ? NULL : task.m_pCryptoIf,
15738 fDecrypt ? NULL : strNewKeyStore.c_str(),
15739 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
15740 fOpenForWrite, &hVfsIosNew);
15741 if (RT_FAILURE(vrc))
15742 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
15743 (*it + ".tmp").c_str(), vrc);
15744 }
15745 else
15746 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
15747
15748 if (RT_SUCCESS(vrc))
15749 {
15750 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
15751 if (RT_FAILURE(vrc))
15752 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
15753 (*it).c_str(), vrc);
15754 }
15755
15756 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
15757 RTVfsIoStrmRelease(hVfsIosOld);
15758 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
15759 RTVfsIoStrmRelease(hVfsIosNew);
15760 }
15761 }
15762
15763 if (SUCCEEDED(hrc))
15764 {
15765 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
15766 it != lstFiles.end();
15767 ++it)
15768 {
15769 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
15770 if (RT_FAILURE(vrc))
15771 {
15772 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
15773 break;
15774 }
15775 }
15776 }
15777
15778 if (SUCCEEDED(hrc))
15779 {
15780 strKeyStore = strNewKeyStore;
15781 strKeyId = task.mstrNewPasswordId;
15782 }
15783
15784 return hrc;
15785}
15786
15787/**
15788 * Task thread implementation for Machine::changeEncryption(), called from
15789 * Machine::taskHandler().
15790 *
15791 * @note Locks this object for writing.
15792 *
15793 * @param task
15794 * @return
15795 */
15796void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
15797{
15798 LogFlowThisFuncEnter();
15799
15800 AutoCaller autoCaller(this);
15801 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
15802 if (FAILED(autoCaller.hrc()))
15803 {
15804 /* we might have been uninitialized because the session was accidentally
15805 * closed by the client, so don't assert */
15806 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
15807 task.m_pProgress->i_notifyComplete(hrc);
15808 LogFlowThisFuncLeave();
15809 return;
15810 }
15811
15812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15813
15814 HRESULT hrc = S_OK;
15815 com::Utf8Str strOldKeyId = mData->mstrKeyId;
15816 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
15817 try
15818 {
15819 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
15820 if (FAILED(hrc))
15821 throw hrc;
15822
15823 if (task.mstrCurrentPassword.isEmpty())
15824 {
15825 if (mData->mstrKeyStore.isNotEmpty())
15826 throw setError(VBOX_E_PASSWORD_INCORRECT,
15827 tr("The password given for the encrypted VM is incorrect"));
15828 }
15829 else
15830 {
15831 if (mData->mstrKeyStore.isEmpty())
15832 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15833 tr("The VM is not configured for encryption"));
15834 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
15835 if (hrc == VBOX_E_PASSWORD_INCORRECT)
15836 throw setError(VBOX_E_PASSWORD_INCORRECT,
15837 tr("The password to decrypt the VM is incorrect"));
15838 }
15839
15840 if (task.mstrCipher.isNotEmpty())
15841 {
15842 if ( task.mstrNewPassword.isEmpty()
15843 && task.mstrNewPasswordId.isEmpty()
15844 && task.mstrCurrentPassword.isNotEmpty())
15845 {
15846 /* An empty password and password ID will default to the current password. */
15847 task.mstrNewPassword = task.mstrCurrentPassword;
15848 }
15849 else if (task.mstrNewPassword.isEmpty())
15850 throw setError(VBOX_E_OBJECT_NOT_FOUND,
15851 tr("A password must be given for the VM encryption"));
15852 else if (task.mstrNewPasswordId.isEmpty())
15853 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15854 tr("A valid identifier for the password must be given"));
15855 }
15856 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
15857 throw setError(VBOX_E_INVALID_OBJECT_STATE,
15858 tr("The password and password identifier must be empty if the output should be unencrypted"));
15859
15860 /*
15861 * Save config.
15862 * Must be first operation to prevent making encrypted copies
15863 * for old version of the config file.
15864 */
15865 int fSave = Machine::SaveS_Force;
15866 if (task.mstrNewPassword.isNotEmpty())
15867 {
15868 VBOXCRYPTOCTX hCryptoCtx;
15869
15870 int vrc = VINF_SUCCESS;
15871 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
15872 {
15873 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
15874 task.mstrNewPassword.c_str(), &hCryptoCtx);
15875 if (RT_FAILURE(vrc))
15876 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
15877 }
15878 else
15879 {
15880 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
15881 task.mstrCurrentPassword.c_str(),
15882 &hCryptoCtx);
15883 if (RT_FAILURE(vrc))
15884 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
15885 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
15886 if (RT_FAILURE(vrc))
15887 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
15888 }
15889
15890 char *pszKeyStore;
15891 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
15892 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
15893 if (RT_FAILURE(vrc))
15894 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
15895 mData->mstrKeyStore = pszKeyStore;
15896 RTStrFree(pszKeyStore);
15897 mData->mstrKeyId = task.mstrNewPasswordId;
15898 size_t cbPassword = task.mstrNewPassword.length() + 1;
15899 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
15900 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
15901 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
15902 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
15903
15904 /*
15905 * Remove backuped config after saving because it can contain
15906 * unencrypted version of the config
15907 */
15908 fSave |= Machine::SaveS_RemoveBackup;
15909 }
15910 else
15911 {
15912 mData->mstrKeyId.setNull();
15913 mData->mstrKeyStore.setNull();
15914 }
15915
15916 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
15917 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
15918 Bstr bstrNewPassword(task.mstrNewPassword);
15919 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
15920 /* encrypt media */
15921 alock.release();
15922 for (MediaList::iterator it = task.mllMedia.begin();
15923 it != task.mllMedia.end();
15924 ++it)
15925 {
15926 ComPtr<IProgress> pProgress1;
15927 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
15928 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
15929 pProgress1.asOutParam());
15930 if (FAILED(hrc)) throw hrc;
15931 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
15932 if (FAILED(hrc)) throw hrc;
15933 }
15934 alock.acquire();
15935
15936 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
15937
15938 Utf8Str strFullSnapshotFolder;
15939 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
15940
15941 /* .sav files (main and snapshots) */
15942 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
15943 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
15944 if (FAILED(hrc))
15945 /* the helper function already sets error object */
15946 throw hrc;
15947
15948 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
15949
15950 /* .nvram files */
15951 com::Utf8Str strNVRAMKeyId;
15952 com::Utf8Str strNVRAMKeyStore;
15953 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
15954 if (FAILED(hrc))
15955 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
15956
15957 Utf8Str strMachineFolder;
15958 i_calculateFullPath(".", strMachineFolder);
15959
15960 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
15961 if (FAILED(hrc))
15962 /* the helper function already sets error object */
15963 throw hrc;
15964
15965 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
15966 if (FAILED(hrc))
15967 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
15968
15969 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
15970
15971 /* .log files */
15972 com::Utf8Str strLogFolder;
15973 i_getLogFolder(strLogFolder);
15974 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
15975 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
15976 if (FAILED(hrc))
15977 /* the helper function already sets error object */
15978 throw hrc;
15979
15980 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
15981
15982 i_saveSettings(NULL, alock, fSave);
15983 }
15984 catch (HRESULT hrcXcpt)
15985 {
15986 hrc = hrcXcpt;
15987 mData->mstrKeyId = strOldKeyId;
15988 mData->mstrKeyStore = strOldKeyStore;
15989 }
15990
15991 task.m_pProgress->i_notifyComplete(hrc);
15992
15993 LogFlowThisFuncLeave();
15994}
15995#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
15996
15997HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
15998 const com::Utf8Str &aCipher,
15999 const com::Utf8Str &aNewPassword,
16000 const com::Utf8Str &aNewPasswordId,
16001 BOOL aForce,
16002 ComPtr<IProgress> &aProgress)
16003{
16004 LogFlowFuncEnter();
16005
16006#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16007 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16008 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16009#else
16010 /* make the VM accessible */
16011 if (!mData->mAccessible)
16012 {
16013 if ( aCurrentPassword.isEmpty()
16014 || mData->mstrKeyId.isEmpty())
16015 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16016
16017 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16018 if (FAILED(hrc))
16019 return hrc;
16020 }
16021
16022 AutoLimitedCaller autoCaller(this);
16023 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16024
16025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16026
16027 /* define media to be change encryption */
16028
16029 MediaList llMedia;
16030 for (MediumAttachmentList::iterator
16031 it = mMediumAttachments->begin();
16032 it != mMediumAttachments->end();
16033 ++it)
16034 {
16035 ComObjPtr<MediumAttachment> &pAttach = *it;
16036 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16037
16038 if (!pMedium.isNull())
16039 {
16040 AutoCaller mac(pMedium);
16041 if (FAILED(mac.hrc())) return mac.hrc();
16042 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16043 DeviceType_T devType = pMedium->i_getDeviceType();
16044 if (devType == DeviceType_HardDisk)
16045 {
16046 /*
16047 * We need to move to last child because the Medium::changeEncryption
16048 * encrypts all chain of specified medium with its parents.
16049 * Also we perform cheking of back reference and children for
16050 * all media in the chain to raise error before we start any action.
16051 * So, we first move into root parent and then we will move to last child
16052 * keeping latter in the list for encryption.
16053 */
16054
16055 /* move to root parent */
16056 ComObjPtr<Medium> pTmpMedium = pMedium;
16057 while (pTmpMedium.isNotNull())
16058 {
16059 AutoCaller mediumAC(pTmpMedium);
16060 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16061 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16062
16063 /* Cannot encrypt media which are attached to more than one virtual machine. */
16064 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16065 if (cBackRefs > 1)
16066 return setError(VBOX_E_INVALID_OBJECT_STATE,
16067 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16068 pTmpMedium->i_getName().c_str(), cBackRefs);
16069
16070 size_t cChildren = pTmpMedium->i_getChildren().size();
16071 if (cChildren > 1)
16072 return setError(VBOX_E_INVALID_OBJECT_STATE,
16073 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16074 pTmpMedium->i_getName().c_str(), cChildren);
16075
16076 pTmpMedium = pTmpMedium->i_getParent();
16077 }
16078 /* move to last child */
16079 pTmpMedium = pMedium;
16080 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16081 {
16082 AutoCaller mediumAC(pTmpMedium);
16083 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16084 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16085
16086 /* Cannot encrypt media which are attached to more than one virtual machine. */
16087 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16088 if (cBackRefs > 1)
16089 return setError(VBOX_E_INVALID_OBJECT_STATE,
16090 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16091 pTmpMedium->i_getName().c_str(), cBackRefs);
16092
16093 size_t cChildren = pTmpMedium->i_getChildren().size();
16094 if (cChildren > 1)
16095 return setError(VBOX_E_INVALID_OBJECT_STATE,
16096 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16097 pTmpMedium->i_getName().c_str(), cChildren);
16098
16099 pTmpMedium = pTmpMedium->i_getChildren().front();
16100 }
16101 llMedia.push_back(pTmpMedium);
16102 }
16103 }
16104 }
16105
16106 ComObjPtr<Progress> pProgress;
16107 pProgress.createObject();
16108 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16109 static_cast<IMachine*>(this) /* aInitiator */,
16110 tr("Change encryption"),
16111 TRUE /* fCancellable */,
16112 (ULONG)(4 + + llMedia.size()), // cOperations
16113 tr("Change encryption of the mediuma"));
16114 if (FAILED(hrc))
16115 return hrc;
16116
16117 /* create and start the task on a separate thread (note that it will not
16118 * start working until we release alock) */
16119 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16120 aCurrentPassword, aCipher, aNewPassword,
16121 aNewPasswordId, aForce, llMedia);
16122 hrc = pTask->createThread();
16123 pTask = NULL;
16124 if (FAILED(hrc))
16125 return hrc;
16126
16127 pProgress.queryInterfaceTo(aProgress.asOutParam());
16128
16129 LogFlowFuncLeave();
16130
16131 return S_OK;
16132#endif
16133}
16134
16135HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16136 com::Utf8Str &aPasswordId)
16137{
16138#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16139 RT_NOREF(aCipher, aPasswordId);
16140 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16141#else
16142 AutoLimitedCaller autoCaller(this);
16143 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16144
16145 PCVBOXCRYPTOIF pCryptoIf = NULL;
16146 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16147 if (FAILED(hrc)) return hrc; /* Error is set */
16148
16149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16150
16151 if (mData->mstrKeyStore.isNotEmpty())
16152 {
16153 char *pszCipher = NULL;
16154 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16155 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16156 if (RT_SUCCESS(vrc))
16157 {
16158 aCipher = getCipherStringWithoutMode(pszCipher);
16159 RTStrFree(pszCipher);
16160 aPasswordId = mData->mstrKeyId;
16161 }
16162 else
16163 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16164 tr("Failed to query the encryption settings with %Rrc"),
16165 vrc);
16166 }
16167 else
16168 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16169
16170 mParent->i_releaseCryptoIf(pCryptoIf);
16171
16172 return hrc;
16173#endif
16174}
16175
16176HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16177{
16178#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16179 RT_NOREF(aPassword);
16180 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16181#else
16182 AutoLimitedCaller autoCaller(this);
16183 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16184
16185 PCVBOXCRYPTOIF pCryptoIf = NULL;
16186 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16187 if (FAILED(hrc)) return hrc; /* Error is set */
16188
16189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16190
16191 if (mData->mstrKeyStore.isNotEmpty())
16192 {
16193 char *pszCipher = NULL;
16194 uint8_t *pbDek = NULL;
16195 size_t cbDek = 0;
16196 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16197 &pbDek, &cbDek, &pszCipher);
16198 if (RT_SUCCESS(vrc))
16199 {
16200 RTStrFree(pszCipher);
16201 RTMemSaferFree(pbDek, cbDek);
16202 }
16203 else
16204 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16205 tr("The password supplied for the encrypted machine is incorrect"));
16206 }
16207 else
16208 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16209
16210 mParent->i_releaseCryptoIf(pCryptoIf);
16211
16212 return hrc;
16213#endif
16214}
16215
16216HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16217 const com::Utf8Str &aPassword)
16218{
16219#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16220 RT_NOREF(aId, aPassword);
16221 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16222#else
16223 AutoLimitedCaller autoCaller(this);
16224 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16225
16226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16227
16228 size_t cbPassword = aPassword.length() + 1;
16229 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16230
16231 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16232
16233 if ( mData->mAccessible
16234 && mData->mSession.mState == SessionState_Locked
16235 && mData->mSession.mLockType == LockType_VM
16236 && mData->mSession.mDirectControl != NULL)
16237 {
16238 /* get the console from the direct session */
16239 ComPtr<IConsole> console;
16240 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16241 ComAssertComRC(hrc);
16242 /* send passsword to console */
16243 console->AddEncryptionPassword(Bstr(aId).raw(),
16244 Bstr(aPassword).raw(),
16245 TRUE);
16246 }
16247
16248 if (mData->mstrKeyId == aId)
16249 {
16250 HRESULT hrc = checkEncryptionPassword(aPassword);
16251 if (FAILED(hrc))
16252 return hrc;
16253
16254 if (SUCCEEDED(hrc))
16255 {
16256 /*
16257 * Encryption is used and password is correct,
16258 * Reinit the machine if required.
16259 */
16260 BOOL fAccessible;
16261 alock.release();
16262 getAccessible(&fAccessible);
16263 alock.acquire();
16264 }
16265 }
16266
16267 /*
16268 * Add the password into the NvramStore only after
16269 * the machine becomes accessible and the NvramStore
16270 * contains key id and key store.
16271 */
16272 if (mNvramStore.isNotNull())
16273 mNvramStore->i_addPassword(aId, aPassword);
16274
16275 return S_OK;
16276#endif
16277}
16278
16279HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16280 const std::vector<com::Utf8Str> &aPasswords)
16281{
16282#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16283 RT_NOREF(aIds, aPasswords);
16284 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16285#else
16286 if (aIds.size() != aPasswords.size())
16287 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16288
16289 HRESULT hrc = S_OK;
16290 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16291 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16292
16293 return hrc;
16294#endif
16295}
16296
16297HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16298{
16299#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16300 RT_NOREF(autoCaller, aId);
16301 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16302#else
16303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16304
16305 if ( mData->mAccessible
16306 && mData->mSession.mState == SessionState_Locked
16307 && mData->mSession.mLockType == LockType_VM
16308 && mData->mSession.mDirectControl != NULL)
16309 {
16310 /* get the console from the direct session */
16311 ComPtr<IConsole> console;
16312 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16313 ComAssertComRC(hrc);
16314 /* send passsword to console */
16315 console->RemoveEncryptionPassword(Bstr(aId).raw());
16316 }
16317
16318 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16319 {
16320 if (Global::IsOnlineOrTransient(mData->mMachineState))
16321 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16322 alock.release();
16323 autoCaller.release();
16324 /* return because all passwords are purged when machine becomes inaccessible; */
16325 return i_setInaccessible();
16326 }
16327
16328 if (mNvramStore.isNotNull())
16329 mNvramStore->i_removePassword(aId);
16330 mData->mpKeyStore->deleteSecretKey(aId);
16331 return S_OK;
16332#endif
16333}
16334
16335HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16336{
16337#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16338 RT_NOREF(autoCaller);
16339 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16340#else
16341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16342
16343 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16344 {
16345 if (Global::IsOnlineOrTransient(mData->mMachineState))
16346 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16347 alock.release();
16348 autoCaller.release();
16349 /* return because all passwords are purged when machine becomes inaccessible; */
16350 return i_setInaccessible();
16351 }
16352
16353 mNvramStore->i_removeAllPasswords();
16354 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16355 return S_OK;
16356#endif
16357}
16358
16359#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16360HRESULT Machine::i_setInaccessible()
16361{
16362 if (!mData->mAccessible)
16363 return S_OK;
16364
16365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16366 VirtualBox *pParent = mParent;
16367 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16368 Guid id(i_getId());
16369
16370 alock.release();
16371
16372 uninit();
16373 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16374
16375 alock.acquire();
16376 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16377 return hrc;
16378}
16379#endif
16380
16381/* This isn't handled entirely by the wrapper generator yet. */
16382#ifdef VBOX_WITH_XPCOM
16383NS_DECL_CLASSINFO(SessionMachine)
16384NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16385
16386NS_DECL_CLASSINFO(SnapshotMachine)
16387NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16388#endif
Note: See TracBrowser for help on using the repository browser.

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