VirtualBox

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

Last change on this file since 94747 was 94747, checked in by vboxsync, 3 years ago

Main/MachineImpl: Implement a few more APIs for full VM encryption, bugref:9955

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 564.2 KB
Line 
1/* $Id: MachineImpl.cpp 94747 2022-04-28 18:29:45Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "SnapshotImpl.h"
32#include "ClientToken.h"
33#include "ProgressImpl.h"
34#include "ProgressProxyImpl.h"
35#include "MediumAttachmentImpl.h"
36#include "MediumImpl.h"
37#include "MediumLock.h"
38#include "USBControllerImpl.h"
39#include "USBDeviceFiltersImpl.h"
40#include "HostImpl.h"
41#include "SharedFolderImpl.h"
42#include "GuestOSTypeImpl.h"
43#include "VirtualBoxErrorInfoImpl.h"
44#include "StorageControllerImpl.h"
45#include "DisplayImpl.h"
46#include "DisplayUtils.h"
47#include "MachineImplCloneVM.h"
48#include "AutostartDb.h"
49#include "SystemPropertiesImpl.h"
50#include "MachineImplMoveVM.h"
51#include "ExtPackManagerImpl.h"
52#include "MachineLaunchVMCommonWorker.h"
53
54// generated header
55#include "VBoxEvents.h"
56
57#ifdef VBOX_WITH_USB
58# include "USBProxyService.h"
59#endif
60
61#include "AutoCaller.h"
62#include "HashedPw.h"
63#include "Performance.h"
64#include "StringifyEnums.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/memsafer.h>
72#include <iprt/process.h>
73#include <iprt/cpp/utils.h>
74#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
75#include <iprt/sha.h>
76#include <iprt/string.h>
77#include <iprt/ctype.h>
78
79#include <VBox/com/array.h>
80#include <VBox/com/list.h>
81#include <VBox/VBoxCryptoIf.h>
82
83#include <VBox/err.h>
84#include <VBox/param.h>
85#include <VBox/settings.h>
86#include <VBox/VMMDev.h>
87#include <VBox/vmm/ssm.h>
88
89#ifdef VBOX_WITH_GUEST_PROPS
90# include <VBox/HostServices/GuestPropertySvc.h>
91# include <VBox/com/array.h>
92#endif
93
94#ifdef VBOX_WITH_SHARED_CLIPBOARD
95# include <VBox/HostServices/VBoxClipboardSvc.h>
96#endif
97
98#include "VBox/com/MultiResult.h"
99
100#include <algorithm>
101
102#ifdef VBOX_WITH_DTRACE_R3_MAIN
103# include "dtrace/VBoxAPI.h"
104#endif
105
106#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
107# define HOSTSUFF_EXE ".exe"
108#else /* !RT_OS_WINDOWS */
109# define HOSTSUFF_EXE ""
110#endif /* !RT_OS_WINDOWS */
111
112// defines / prototypes
113/////////////////////////////////////////////////////////////////////////////
114
115#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
116# define BUF_DATA_SIZE _64K
117
118enum CipherMode
119{
120 CipherModeGcm = 0,
121 CipherModeCtr,
122 CipherModeXts,
123 CipherModeMax
124};
125
126enum AesSize
127{
128 Aes128 = 0,
129 Aes256,
130 AesMax
131};
132
133const char *g_apszCipher[AesMax][CipherModeMax] =
134{
135 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
136 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
137};
138const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
139
140static const char *getCipherString(const char *pszAlgo, const int iMode)
141{
142 if (iMode >= CipherModeMax)
143 return pszAlgo;
144
145 for (int i = 0; i < AesMax; i++)
146 {
147 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
148 return g_apszCipher[i][iMode];
149 }
150 return pszAlgo;
151}
152
153static const char *getCipherStringWithoutMode(const char *pszAlgo)
154{
155 for (int i = 0; i < AesMax; i++)
156 {
157 for (int j = 0; j < CipherModeMax; j++)
158 {
159 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
160 return g_apszCipherAlgo[i];
161 }
162 }
163 return pszAlgo;
164}
165#endif
166
167/////////////////////////////////////////////////////////////////////////////
168// Machine::Data structure
169/////////////////////////////////////////////////////////////////////////////
170
171Machine::Data::Data()
172{
173 mRegistered = FALSE;
174 pMachineConfigFile = NULL;
175 /* Contains hints on what has changed when the user is using the VM (config
176 * changes, running the VM, ...). This is used to decide if a config needs
177 * to be written to disk. */
178 flModifications = 0;
179 /* VM modification usually also trigger setting the current state to
180 * "Modified". Although this is not always the case. An e.g. is the VM
181 * initialization phase or when snapshot related data is changed. The
182 * actually behavior is controlled by the following flag. */
183 m_fAllowStateModification = false;
184 mAccessible = FALSE;
185 /* mUuid is initialized in Machine::init() */
186
187 mMachineState = MachineState_PoweredOff;
188 RTTimeNow(&mLastStateChange);
189
190 mMachineStateDeps = 0;
191 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
192 mMachineStateChangePending = 0;
193
194 mCurrentStateModified = TRUE;
195 mGuestPropertiesModified = FALSE;
196
197 mSession.mPID = NIL_RTPROCESS;
198 mSession.mLockType = LockType_Null;
199 mSession.mState = SessionState_Unlocked;
200
201#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
202 mpKeyStore = NULL;
203#endif
204}
205
206Machine::Data::~Data()
207{
208 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
209 {
210 RTSemEventMultiDestroy(mMachineStateDepsSem);
211 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
212 }
213 if (pMachineConfigFile)
214 {
215 delete pMachineConfigFile;
216 pMachineConfigFile = NULL;
217 }
218}
219
220/////////////////////////////////////////////////////////////////////////////
221// Machine::HWData structure
222/////////////////////////////////////////////////////////////////////////////
223
224Machine::HWData::HWData()
225{
226 /* default values for a newly created machine */
227 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
228 mMemorySize = 128;
229 mCPUCount = 1;
230 mCPUHotPlugEnabled = false;
231 mMemoryBalloonSize = 0;
232 mPageFusionEnabled = false;
233 mHWVirtExEnabled = true;
234 mHWVirtExNestedPagingEnabled = true;
235 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
236 mHWVirtExVPIDEnabled = true;
237 mHWVirtExUXEnabled = true;
238 mHWVirtExForceEnabled = false;
239 mHWVirtExUseNativeApi = false;
240 mHWVirtExVirtVmsaveVmload = true;
241#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
242 mPAEEnabled = true;
243#else
244 mPAEEnabled = false;
245#endif
246 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
247 mTripleFaultReset = false;
248 mAPIC = true;
249 mX2APIC = false;
250 mIBPBOnVMExit = false;
251 mIBPBOnVMEntry = false;
252 mSpecCtrl = false;
253 mSpecCtrlByHost = false;
254 mL1DFlushOnSched = true;
255 mL1DFlushOnVMEntry = false;
256 mMDSClearOnSched = true;
257 mMDSClearOnVMEntry = false;
258 mNestedHWVirt = false;
259 mHPETEnabled = false;
260 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
261 mCpuIdPortabilityLevel = 0;
262 mCpuProfile = "host";
263
264 /* default boot order: floppy - DVD - HDD */
265 mBootOrder[0] = DeviceType_Floppy;
266 mBootOrder[1] = DeviceType_DVD;
267 mBootOrder[2] = DeviceType_HardDisk;
268 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
269 mBootOrder[i] = DeviceType_Null;
270
271 mClipboardMode = ClipboardMode_Disabled;
272 mClipboardFileTransfersEnabled = FALSE;
273
274 mDnDMode = DnDMode_Disabled;
275
276 mFirmwareType = FirmwareType_BIOS;
277 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
278 mPointingHIDType = PointingHIDType_PS2Mouse;
279 mChipsetType = ChipsetType_PIIX3;
280 mIommuType = IommuType_None;
281 mParavirtProvider = ParavirtProvider_Default;
282 mEmulatedUSBCardReaderEnabled = FALSE;
283
284 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
285 mCPUAttached[i] = false;
286
287 mIOCacheEnabled = true;
288 mIOCacheSize = 5; /* 5MB */
289}
290
291Machine::HWData::~HWData()
292{
293}
294
295/////////////////////////////////////////////////////////////////////////////
296// Machine class
297/////////////////////////////////////////////////////////////////////////////
298
299// constructor / destructor
300/////////////////////////////////////////////////////////////////////////////
301
302Machine::Machine() :
303#ifdef VBOX_WITH_RESOURCE_USAGE_API
304 mCollectorGuest(NULL),
305#endif
306 mPeer(NULL),
307 mParent(NULL),
308 mSerialPorts(),
309 mParallelPorts(),
310 uRegistryNeedsSaving(0)
311{}
312
313Machine::~Machine()
314{}
315
316HRESULT Machine::FinalConstruct()
317{
318 LogFlowThisFunc(("\n"));
319 return BaseFinalConstruct();
320}
321
322void Machine::FinalRelease()
323{
324 LogFlowThisFunc(("\n"));
325 uninit();
326 BaseFinalRelease();
327}
328
329/**
330 * Initializes a new machine instance; this init() variant creates a new, empty machine.
331 * This gets called from VirtualBox::CreateMachine().
332 *
333 * @param aParent Associated parent object
334 * @param strConfigFile Local file system path to the VM settings file (can
335 * be relative to the VirtualBox config directory).
336 * @param strName name for the machine
337 * @param llGroups list of groups for the machine
338 * @param strOsType OS Type string (stored as is if aOsType is NULL).
339 * @param aOsType OS Type of this machine or NULL.
340 * @param aId UUID for the new machine.
341 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
342 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
343 * scheme (includes the UUID).
344 * @param aCipher The cipher to encrypt the VM with.
345 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
346 * @param aPassword The password to encrypt the VM with.
347 *
348 * @return Success indicator. if not S_OK, the machine object is invalid
349 */
350HRESULT Machine::init(VirtualBox *aParent,
351 const Utf8Str &strConfigFile,
352 const Utf8Str &strName,
353 const StringsList &llGroups,
354 const Utf8Str &strOsType,
355 GuestOSType *aOsType,
356 const Guid &aId,
357 bool fForceOverwrite,
358 bool fDirectoryIncludesUUID,
359 const com::Utf8Str &aCipher,
360 const com::Utf8Str &aPasswordId,
361 const com::Utf8Str &aPassword)
362{
363 LogFlowThisFuncEnter();
364 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
365
366#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
367 RT_NOREF(aCipher);
368 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
369 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
370#endif
371
372 /* Enclose the state transition NotReady->InInit->Ready */
373 AutoInitSpan autoInitSpan(this);
374 AssertReturn(autoInitSpan.isOk(), E_FAIL);
375
376 HRESULT rc = initImpl(aParent, strConfigFile);
377 if (FAILED(rc)) return rc;
378
379#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
380 com::Utf8Str strSsmKeyId;
381 com::Utf8Str strSsmKeyStore;
382 com::Utf8Str strNVRAMKeyId;
383 com::Utf8Str strNVRAMKeyStore;
384
385 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
386 {
387 /* Resolve the cryptographic interface. */
388 PCVBOXCRYPTOIF pCryptoIf = NULL;
389 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
390 if (SUCCEEDED(hrc))
391 {
392 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
393 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
394 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
395
396 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
397 {
398 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
399 if (!pszCipher)
400 {
401 hrc = setError(VBOX_E_NOT_SUPPORTED,
402 tr("The cipher '%s' is not supported"), aCipher.c_str());
403 break;
404 }
405
406 VBOXCRYPTOCTX hCryptoCtx;
407 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
408 if (RT_FAILURE(vrc))
409 {
410 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
411 break;
412 }
413
414 char *pszKeyStore;
415 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
416 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
417 AssertRC(vrc2);
418
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 *(astrKeyStore[i]) = pszKeyStore;
426 RTMemFree(pszKeyStore);
427 *(astrKeyId[i]) = aPasswordId;
428 }
429
430 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
431 Assert(hrc2 == S_OK);
432
433 if (FAILED(hrc))
434 return hrc; /* Error is set. */
435 }
436 else
437 return hrc; /* Error is set. */
438 }
439#endif
440
441 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
442 if (FAILED(rc)) return rc;
443
444 if (SUCCEEDED(rc))
445 {
446 // create an empty machine config
447 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
448
449 rc = initDataAndChildObjects();
450 }
451
452 if (SUCCEEDED(rc))
453 {
454#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
455 mSSData->strStateKeyId = strSsmKeyId;
456 mSSData->strStateKeyStore = strSsmKeyStore;
457#endif
458
459 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
460 mData->mAccessible = TRUE;
461
462 unconst(mData->mUuid) = aId;
463
464 mUserData->s.strName = strName;
465
466 if (llGroups.size())
467 mUserData->s.llGroups = llGroups;
468
469 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
470 // the "name sync" flag determines whether the machine directory gets renamed along
471 // with the machine file; say so if the settings file name is the same as the
472 // settings file parent directory (machine directory)
473 mUserData->s.fNameSync = i_isInOwnDir();
474
475 // initialize the default snapshots folder
476 rc = COMSETTER(SnapshotFolder)(NULL);
477 AssertComRC(rc);
478
479 if (aOsType)
480 {
481 /* Store OS type */
482 mUserData->s.strOsType = aOsType->i_id();
483
484 /* Let the OS type select 64-bit ness. */
485 mHWData->mLongMode = aOsType->i_is64Bit()
486 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
487
488 /* Let the OS type enable the X2APIC */
489 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
490
491 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
492 AssertComRC(rc);
493 }
494 else if (!strOsType.isEmpty())
495 {
496 /* Store OS type */
497 mUserData->s.strOsType = strOsType;
498
499 /* No guest OS type object. Pick some plausible defaults which the
500 * host can handle. There's no way to know or validate anything. */
501 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
502 mHWData->mX2APIC = false;
503 }
504
505 /* Apply BIOS defaults. */
506 mBIOSSettings->i_applyDefaults(aOsType);
507
508 /* Apply TPM defaults. */
509 mTrustedPlatformModule->i_applyDefaults(aOsType);
510
511 /* Apply record defaults. */
512 mRecordingSettings->i_applyDefaults();
513
514 /* Apply network adapters defaults */
515 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
516 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
517
518 /* Apply serial port defaults */
519 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
520 mSerialPorts[slot]->i_applyDefaults(aOsType);
521
522 /* Apply parallel port defaults */
523 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
524 mParallelPorts[slot]->i_applyDefaults();
525
526 /* Enable the VMMDev testing feature for bootsector VMs: */
527 if (aOsType && aOsType->i_id() == "VBoxBS_64")
528 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
529
530 /* At this point the changing of the current state modification
531 * flag is allowed. */
532 i_allowStateModification();
533
534 /* commit all changes made during the initialization */
535 i_commit();
536 }
537
538 /* Confirm a successful initialization when it's the case */
539 if (SUCCEEDED(rc))
540 {
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
543 {
544 size_t cbPassword = aPassword.length() + 1;
545 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
546 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
547 }
548#endif
549
550 if (mData->mAccessible)
551 autoInitSpan.setSucceeded();
552 else
553 autoInitSpan.setLimited();
554 }
555
556 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
557 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
558 mData->mRegistered,
559 mData->mAccessible,
560 rc));
561
562 LogFlowThisFuncLeave();
563
564 return rc;
565}
566
567/**
568 * Initializes a new instance with data from machine XML (formerly Init_Registered).
569 * Gets called in two modes:
570 *
571 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
572 * UUID is specified and we mark the machine as "registered";
573 *
574 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
575 * and the machine remains unregistered until RegisterMachine() is called.
576 *
577 * @param aParent Associated parent object
578 * @param strConfigFile Local file system path to the VM settings file (can
579 * be relative to the VirtualBox config directory).
580 * @param aId UUID of the machine or NULL (see above).
581 * @param strPassword Password for decrypting the config
582 *
583 * @return Success indicator. if not S_OK, the machine object is invalid
584 */
585HRESULT Machine::initFromSettings(VirtualBox *aParent,
586 const Utf8Str &strConfigFile,
587 const Guid *aId,
588 const com::Utf8Str &strPassword)
589{
590 LogFlowThisFuncEnter();
591 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
592
593 PCVBOXCRYPTOIF pCryptoIf = NULL;
594#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
595 if (strPassword.isNotEmpty())
596 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
597#else
598 if (strPassword.isNotEmpty())
599 {
600 /* Get at the crpytographic interface. */
601 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
602 if (FAILED(hrc))
603 return hrc; /* Error is set. */
604 }
605#endif
606
607 /* Enclose the state transition NotReady->InInit->Ready */
608 AutoInitSpan autoInitSpan(this);
609 AssertReturn(autoInitSpan.isOk(), E_FAIL);
610
611 HRESULT rc = initImpl(aParent, strConfigFile);
612 if (FAILED(rc)) return rc;
613
614 if (aId)
615 {
616 // loading a registered VM:
617 unconst(mData->mUuid) = *aId;
618 mData->mRegistered = TRUE;
619 // now load the settings from XML:
620 rc = i_registeredInit();
621 // this calls initDataAndChildObjects() and loadSettings()
622 }
623 else
624 {
625 // opening an unregistered VM (VirtualBox::OpenMachine()):
626 rc = initDataAndChildObjects();
627
628 if (SUCCEEDED(rc))
629 {
630 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
631 mData->mAccessible = TRUE;
632
633 try
634 {
635 // load and parse machine XML; this will throw on XML or logic errors
636 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
637 pCryptoIf,
638 strPassword.c_str());
639
640 // reject VM UUID duplicates, they can happen if someone
641 // tries to register an already known VM config again
642 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
643 true /* fPermitInaccessible */,
644 false /* aDoSetError */,
645 NULL) != VBOX_E_OBJECT_NOT_FOUND)
646 {
647 throw setError(E_FAIL,
648 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
649 mData->m_strConfigFile.c_str());
650 }
651
652 // use UUID from machine config
653 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
654
655#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
656 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
657 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
658 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
659 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
660#endif
661
662 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
663 {
664 // We just set the inaccessible state and fill the error info allowing the caller
665 // to register the machine with encrypted config even if the password is incorrect
666 mData->mAccessible = FALSE;
667
668 /* fetch the current error info */
669 mData->mAccessError = com::ErrorInfo();
670
671 throw setError(VBOX_E_PASSWORD_INCORRECT,
672 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
673 mData->pMachineConfigFile->uuid.raw());
674 }
675 else
676 {
677#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
678 if (strPassword.isNotEmpty())
679 {
680 size_t cbKey = strPassword.length() + 1; /* Include terminator */
681 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
682 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
683 }
684#endif
685
686 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
687 NULL /* puuidRegistry */);
688 if (FAILED(rc)) throw rc;
689
690 /* At this point the changing of the current state modification
691 * flag is allowed. */
692 i_allowStateModification();
693
694 i_commit();
695 }
696 }
697 catch (HRESULT err)
698 {
699 /* we assume that error info is set by the thrower */
700 rc = err;
701 }
702 catch (...)
703 {
704 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
705 }
706 }
707 }
708
709 /* Confirm a successful initialization when it's the case */
710 if (SUCCEEDED(rc))
711 {
712 if (mData->mAccessible)
713 autoInitSpan.setSucceeded();
714 else
715 {
716 autoInitSpan.setLimited();
717
718 // uninit media from this machine's media registry, or else
719 // reloading the settings will fail
720 mParent->i_unregisterMachineMedia(i_getId());
721 }
722 }
723
724#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
725 if (pCryptoIf)
726 {
727 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
728 Assert(hrc2 == S_OK);
729 }
730#endif
731
732 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
733 "rc=%08X\n",
734 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
735 mData->mRegistered, mData->mAccessible, rc));
736
737 LogFlowThisFuncLeave();
738
739 return rc;
740}
741
742/**
743 * Initializes a new instance from a machine config that is already in memory
744 * (import OVF case). Since we are importing, the UUID in the machine
745 * config is ignored and we always generate a fresh one.
746 *
747 * @param aParent Associated parent object.
748 * @param strName Name for the new machine; this overrides what is specified in config.
749 * @param strSettingsFilename File name of .vbox file.
750 * @param config Machine configuration loaded and parsed from XML.
751 *
752 * @return Success indicator. if not S_OK, the machine object is invalid
753 */
754HRESULT Machine::init(VirtualBox *aParent,
755 const Utf8Str &strName,
756 const Utf8Str &strSettingsFilename,
757 const settings::MachineConfigFile &config)
758{
759 LogFlowThisFuncEnter();
760
761 /* Enclose the state transition NotReady->InInit->Ready */
762 AutoInitSpan autoInitSpan(this);
763 AssertReturn(autoInitSpan.isOk(), E_FAIL);
764
765 HRESULT rc = initImpl(aParent, strSettingsFilename);
766 if (FAILED(rc)) return rc;
767
768 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
769 if (FAILED(rc)) return rc;
770
771 rc = initDataAndChildObjects();
772
773 if (SUCCEEDED(rc))
774 {
775 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
776 mData->mAccessible = TRUE;
777
778 // create empty machine config for instance data
779 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
780
781 // generate fresh UUID, ignore machine config
782 unconst(mData->mUuid).create();
783
784 rc = i_loadMachineDataFromSettings(config,
785 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
786
787 // override VM name as well, it may be different
788 mUserData->s.strName = strName;
789
790 if (SUCCEEDED(rc))
791 {
792 /* At this point the changing of the current state modification
793 * flag is allowed. */
794 i_allowStateModification();
795
796 /* commit all changes made during the initialization */
797 i_commit();
798 }
799 }
800
801 /* Confirm a successful initialization when it's the case */
802 if (SUCCEEDED(rc))
803 {
804 if (mData->mAccessible)
805 autoInitSpan.setSucceeded();
806 else
807 {
808 /* Ignore all errors from unregistering, they would destroy
809- * the more interesting error information we already have,
810- * pinpointing the issue with the VM config. */
811 ErrorInfoKeeper eik;
812
813 autoInitSpan.setLimited();
814
815 // uninit media from this machine's media registry, or else
816 // reloading the settings will fail
817 mParent->i_unregisterMachineMedia(i_getId());
818 }
819 }
820
821 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
822 "rc=%08X\n",
823 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
824 mData->mRegistered, mData->mAccessible, rc));
825
826 LogFlowThisFuncLeave();
827
828 return rc;
829}
830
831/**
832 * Shared code between the various init() implementations.
833 * @param aParent The VirtualBox object.
834 * @param strConfigFile Settings file.
835 * @return
836 */
837HRESULT Machine::initImpl(VirtualBox *aParent,
838 const Utf8Str &strConfigFile)
839{
840 LogFlowThisFuncEnter();
841
842 AssertReturn(aParent, E_INVALIDARG);
843 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
844
845 HRESULT rc = S_OK;
846
847 /* share the parent weakly */
848 unconst(mParent) = aParent;
849
850 /* allocate the essential machine data structure (the rest will be
851 * allocated later by initDataAndChildObjects() */
852 mData.allocate();
853
854 /* memorize the config file name (as provided) */
855 mData->m_strConfigFile = strConfigFile;
856
857 /* get the full file name */
858 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
859 if (RT_FAILURE(vrc1))
860 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
861 tr("Invalid machine settings file name '%s' (%Rrc)"),
862 strConfigFile.c_str(),
863 vrc1);
864
865#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
866 /** @todo Only create when the machine is going to be encrypted. */
867 /* Non-pageable memory is not accessible for non-VM process */
868 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
869 AssertReturn(mData->mpKeyStore, VERR_NO_MEMORY);
870#endif
871
872 LogFlowThisFuncLeave();
873
874 return rc;
875}
876
877/**
878 * Tries to create a machine settings file in the path stored in the machine
879 * instance data. Used when a new machine is created to fail gracefully if
880 * the settings file could not be written (e.g. because machine dir is read-only).
881 * @return
882 */
883HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
884{
885 HRESULT rc = S_OK;
886
887 // when we create a new machine, we must be able to create the settings file
888 RTFILE f = NIL_RTFILE;
889 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
890 if ( RT_SUCCESS(vrc)
891 || vrc == VERR_SHARING_VIOLATION
892 )
893 {
894 if (RT_SUCCESS(vrc))
895 RTFileClose(f);
896 if (!fForceOverwrite)
897 rc = setError(VBOX_E_FILE_ERROR,
898 tr("Machine settings file '%s' already exists"),
899 mData->m_strConfigFileFull.c_str());
900 else
901 {
902 /* try to delete the config file, as otherwise the creation
903 * of a new settings file will fail. */
904 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
905 if (RT_FAILURE(vrc2))
906 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
907 tr("Could not delete the existing settings file '%s' (%Rrc)"),
908 mData->m_strConfigFileFull.c_str(), vrc2);
909 }
910 }
911 else if ( vrc != VERR_FILE_NOT_FOUND
912 && vrc != VERR_PATH_NOT_FOUND
913 )
914 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
915 tr("Invalid machine settings file name '%s' (%Rrc)"),
916 mData->m_strConfigFileFull.c_str(),
917 vrc);
918 return rc;
919}
920
921/**
922 * Initializes the registered machine by loading the settings file.
923 * This method is separated from #init() in order to make it possible to
924 * retry the operation after VirtualBox startup instead of refusing to
925 * startup the whole VirtualBox server in case if the settings file of some
926 * registered VM is invalid or inaccessible.
927 *
928 * @note Must be always called from this object's write lock
929 * (unless called from #init() that doesn't need any locking).
930 * @note Locks the mUSBController method for writing.
931 * @note Subclasses must not call this method.
932 */
933HRESULT Machine::i_registeredInit()
934{
935 AssertReturn(!i_isSessionMachine(), E_FAIL);
936 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
937 AssertReturn(mData->mUuid.isValid(), E_FAIL);
938 AssertReturn(!mData->mAccessible, E_FAIL);
939
940 HRESULT rc = initDataAndChildObjects();
941
942 if (SUCCEEDED(rc))
943 {
944 /* Temporarily reset the registered flag in order to let setters
945 * potentially called from loadSettings() succeed (isMutable() used in
946 * all setters will return FALSE for a Machine instance if mRegistered
947 * is TRUE). */
948 mData->mRegistered = FALSE;
949
950 PCVBOXCRYPTOIF pCryptoIf = NULL;
951 SecretKey *pKey = NULL;
952 const char *pszPassword = NULL;
953#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
954 /* Resolve password and cryptographic support interface if machine is encrypted. */
955 if (mData->mstrKeyId.isNotEmpty())
956 {
957 /* Get at the crpytographic interface. */
958 rc = mParent->i_retainCryptoIf(&pCryptoIf);
959 if (SUCCEEDED(rc))
960 {
961 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
962 if (RT_SUCCESS(vrc))
963 pszPassword = (const char *)pKey->getKeyBuffer();
964 else
965 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
966 mData->mstrKeyId.c_str(), vrc);
967 }
968 }
969#else
970 RT_NOREF(pKey);
971#endif
972
973 if (SUCCEEDED(rc))
974 {
975 try
976 {
977 // load and parse machine XML; this will throw on XML or logic errors
978 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
979 pCryptoIf, pszPassword);
980
981 if (mData->mUuid != mData->pMachineConfigFile->uuid)
982 throw setError(E_FAIL,
983 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
984 mData->pMachineConfigFile->uuid.raw(),
985 mData->m_strConfigFileFull.c_str(),
986 mData->mUuid.toString().c_str(),
987 mParent->i_settingsFilePath().c_str());
988
989#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
990 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
991 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
992 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
993 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
994
995 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
996 throw setError(VBOX_E_PASSWORD_INCORRECT,
997 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
998 mData->pMachineConfigFile->uuid.raw());
999#endif
1000
1001 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1002 NULL /* const Guid *puuidRegistry */);
1003 if (FAILED(rc)) throw rc;
1004 }
1005 catch (HRESULT err)
1006 {
1007 /* we assume that error info is set by the thrower */
1008 rc = err;
1009 }
1010 catch (...)
1011 {
1012 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1013 }
1014
1015 /* Restore the registered flag (even on failure) */
1016 mData->mRegistered = TRUE;
1017 }
1018
1019#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1020 if (pCryptoIf)
1021 mParent->i_releaseCryptoIf(pCryptoIf);
1022 if (pKey)
1023 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1024#endif
1025 }
1026
1027 if (SUCCEEDED(rc))
1028 {
1029 /* Set mAccessible to TRUE only if we successfully locked and loaded
1030 * the settings file */
1031 mData->mAccessible = TRUE;
1032
1033 /* commit all changes made during loading the settings file */
1034 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1035 /// @todo r=klaus for some reason the settings loading logic backs up
1036 // the settings, and therefore a commit is needed. Should probably be changed.
1037 }
1038 else
1039 {
1040 /* If the machine is registered, then, instead of returning a
1041 * failure, we mark it as inaccessible and set the result to
1042 * success to give it a try later */
1043
1044 /* fetch the current error info */
1045 mData->mAccessError = com::ErrorInfo();
1046 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1047
1048 /* rollback all changes */
1049 i_rollback(false /* aNotify */);
1050
1051 // uninit media from this machine's media registry, or else
1052 // reloading the settings will fail
1053 mParent->i_unregisterMachineMedia(i_getId());
1054
1055 /* uninitialize the common part to make sure all data is reset to
1056 * default (null) values */
1057 uninitDataAndChildObjects();
1058
1059 rc = S_OK;
1060 }
1061
1062 return rc;
1063}
1064
1065/**
1066 * Uninitializes the instance.
1067 * Called either from FinalRelease() or by the parent when it gets destroyed.
1068 *
1069 * @note The caller of this method must make sure that this object
1070 * a) doesn't have active callers on the current thread and b) is not locked
1071 * by the current thread; otherwise uninit() will hang either a) due to
1072 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1073 * a dead-lock caused by this thread waiting for all callers on the other
1074 * threads are done but preventing them from doing so by holding a lock.
1075 */
1076void Machine::uninit()
1077{
1078 LogFlowThisFuncEnter();
1079
1080 Assert(!isWriteLockOnCurrentThread());
1081
1082 Assert(!uRegistryNeedsSaving);
1083 if (uRegistryNeedsSaving)
1084 {
1085 AutoCaller autoCaller(this);
1086 if (SUCCEEDED(autoCaller.rc()))
1087 {
1088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1089 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1090 }
1091 }
1092
1093 /* Enclose the state transition Ready->InUninit->NotReady */
1094 AutoUninitSpan autoUninitSpan(this);
1095 if (autoUninitSpan.uninitDone())
1096 return;
1097
1098 Assert(!i_isSnapshotMachine());
1099 Assert(!i_isSessionMachine());
1100 Assert(!!mData);
1101
1102 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1103 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1104
1105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 if (!mData->mSession.mMachine.isNull())
1108 {
1109 /* Theoretically, this can only happen if the VirtualBox server has been
1110 * terminated while there were clients running that owned open direct
1111 * sessions. Since in this case we are definitely called by
1112 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1113 * won't happen on the client watcher thread (because it has a
1114 * VirtualBox caller for the duration of the
1115 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1116 * cannot happen until the VirtualBox caller is released). This is
1117 * important, because SessionMachine::uninit() cannot correctly operate
1118 * after we return from this method (it expects the Machine instance is
1119 * still valid). We'll call it ourselves below.
1120 */
1121 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1122 (SessionMachine*)mData->mSession.mMachine));
1123
1124 if (Global::IsOnlineOrTransient(mData->mMachineState))
1125 {
1126 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1127 /* set machine state using SessionMachine reimplementation */
1128 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1129 }
1130
1131 /*
1132 * Uninitialize SessionMachine using public uninit() to indicate
1133 * an unexpected uninitialization.
1134 */
1135 mData->mSession.mMachine->uninit();
1136 /* SessionMachine::uninit() must set mSession.mMachine to null */
1137 Assert(mData->mSession.mMachine.isNull());
1138 }
1139
1140 // uninit media from this machine's media registry, if they're still there
1141 Guid uuidMachine(i_getId());
1142
1143 /* the lock is no more necessary (SessionMachine is uninitialized) */
1144 alock.release();
1145
1146 /* XXX This will fail with
1147 * "cannot be closed because it is still attached to 1 virtual machines"
1148 * because at this point we did not call uninitDataAndChildObjects() yet
1149 * and therefore also removeBackReference() for all these mediums was not called! */
1150
1151 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1152 mParent->i_unregisterMachineMedia(uuidMachine);
1153
1154 // has machine been modified?
1155 if (mData->flModifications)
1156 {
1157 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1158 i_rollback(false /* aNotify */);
1159 }
1160
1161 if (mData->mAccessible)
1162 uninitDataAndChildObjects();
1163
1164#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1165 if (mData->mpKeyStore != NULL)
1166 delete mData->mpKeyStore;
1167#endif
1168
1169 /* free the essential data structure last */
1170 mData.free();
1171
1172 LogFlowThisFuncLeave();
1173}
1174
1175// Wrapped IMachine properties
1176/////////////////////////////////////////////////////////////////////////////
1177HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1178{
1179 /* mParent is constant during life time, no need to lock */
1180 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1181 aParent = pVirtualBox;
1182
1183 return S_OK;
1184}
1185
1186
1187HRESULT Machine::getAccessible(BOOL *aAccessible)
1188{
1189 /* In some cases (medium registry related), it is necessary to be able to
1190 * go through the list of all machines. Happens when an inaccessible VM
1191 * has a sensible medium registry. */
1192 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1194
1195 HRESULT rc = S_OK;
1196
1197 if (!mData->mAccessible)
1198 {
1199 /* try to initialize the VM once more if not accessible */
1200
1201 AutoReinitSpan autoReinitSpan(this);
1202 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1203
1204#ifdef DEBUG
1205 LogFlowThisFunc(("Dumping media backreferences\n"));
1206 mParent->i_dumpAllBackRefs();
1207#endif
1208
1209 if (mData->pMachineConfigFile)
1210 {
1211 // reset the XML file to force loadSettings() (called from i_registeredInit())
1212 // to parse it again; the file might have changed
1213 delete mData->pMachineConfigFile;
1214 mData->pMachineConfigFile = NULL;
1215 }
1216
1217 rc = i_registeredInit();
1218
1219 if (SUCCEEDED(rc) && mData->mAccessible)
1220 {
1221 autoReinitSpan.setSucceeded();
1222
1223 /* make sure interesting parties will notice the accessibility
1224 * state change */
1225 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1226 mParent->i_onMachineDataChanged(mData->mUuid);
1227 }
1228 }
1229
1230 if (SUCCEEDED(rc))
1231 *aAccessible = mData->mAccessible;
1232
1233 LogFlowThisFuncLeave();
1234
1235 return rc;
1236}
1237
1238HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1239{
1240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1243 {
1244 /* return shortly */
1245 aAccessError = NULL;
1246 return S_OK;
1247 }
1248
1249 HRESULT rc = S_OK;
1250
1251 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1252 rc = errorInfo.createObject();
1253 if (SUCCEEDED(rc))
1254 {
1255 errorInfo->init(mData->mAccessError.getResultCode(),
1256 mData->mAccessError.getInterfaceID().ref(),
1257 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1258 Utf8Str(mData->mAccessError.getText()));
1259 aAccessError = errorInfo;
1260 }
1261
1262 return rc;
1263}
1264
1265HRESULT Machine::getName(com::Utf8Str &aName)
1266{
1267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 aName = mUserData->s.strName;
1270
1271 return S_OK;
1272}
1273
1274HRESULT Machine::setName(const com::Utf8Str &aName)
1275{
1276 // prohibit setting a UUID only as the machine name, or else it can
1277 // never be found by findMachine()
1278 Guid test(aName);
1279
1280 if (test.isValid())
1281 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1282
1283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1286 if (FAILED(rc)) return rc;
1287
1288 i_setModified(IsModified_MachineData);
1289 mUserData.backup();
1290 mUserData->s.strName = aName;
1291
1292 return S_OK;
1293}
1294
1295HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1296{
1297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1298
1299 aDescription = mUserData->s.strDescription;
1300
1301 return S_OK;
1302}
1303
1304HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1305{
1306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 // this can be done in principle in any state as it doesn't affect the VM
1309 // significantly, but play safe by not messing around while complex
1310 // activities are going on
1311 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1312 if (FAILED(rc)) return rc;
1313
1314 i_setModified(IsModified_MachineData);
1315 mUserData.backup();
1316 mUserData->s.strDescription = aDescription;
1317
1318 return S_OK;
1319}
1320
1321HRESULT Machine::getId(com::Guid &aId)
1322{
1323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1324
1325 aId = mData->mUuid;
1326
1327 return S_OK;
1328}
1329
1330HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1331{
1332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1333 aGroups.resize(mUserData->s.llGroups.size());
1334 size_t i = 0;
1335 for (StringsList::const_iterator
1336 it = mUserData->s.llGroups.begin();
1337 it != mUserData->s.llGroups.end();
1338 ++it, ++i)
1339 aGroups[i] = (*it);
1340
1341 return S_OK;
1342}
1343
1344HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1345{
1346 StringsList llGroups;
1347 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1348 if (FAILED(rc))
1349 return rc;
1350
1351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 rc = i_checkStateDependency(MutableOrSavedStateDep);
1354 if (FAILED(rc)) return rc;
1355
1356 i_setModified(IsModified_MachineData);
1357 mUserData.backup();
1358 mUserData->s.llGroups = llGroups;
1359
1360 return S_OK;
1361}
1362
1363HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1364{
1365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 aOSTypeId = mUserData->s.strOsType;
1368
1369 return S_OK;
1370}
1371
1372HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1373{
1374 /* look up the object by Id to check it is valid */
1375 ComObjPtr<GuestOSType> pGuestOSType;
1376 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1377
1378 /* when setting, always use the "etalon" value for consistency -- lookup
1379 * by ID is case-insensitive and the input value may have different case */
1380 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1381
1382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 HRESULT rc = i_checkStateDependency(MutableStateDep);
1385 if (FAILED(rc)) return rc;
1386
1387 i_setModified(IsModified_MachineData);
1388 mUserData.backup();
1389 mUserData->s.strOsType = osTypeId;
1390
1391 return S_OK;
1392}
1393
1394HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1395{
1396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 *aFirmwareType = mHWData->mFirmwareType;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1404{
1405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 HRESULT rc = i_checkStateDependency(MutableStateDep);
1408 if (FAILED(rc)) return rc;
1409
1410 i_setModified(IsModified_MachineData);
1411 mHWData.backup();
1412 mHWData->mFirmwareType = aFirmwareType;
1413 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1414 alock.release();
1415
1416 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1431{
1432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 HRESULT rc = i_checkStateDependency(MutableStateDep);
1435 if (FAILED(rc)) return rc;
1436
1437 i_setModified(IsModified_MachineData);
1438 mHWData.backup();
1439 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1445{
1446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 *aPointingHIDType = mHWData->mPointingHIDType;
1449
1450 return S_OK;
1451}
1452
1453HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1454{
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT rc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(rc)) return rc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mPointingHIDType = aPointingHIDType;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aChipsetType = mHWData->mChipsetType;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1477{
1478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 HRESULT rc = i_checkStateDependency(MutableStateDep);
1481 if (FAILED(rc)) return rc;
1482
1483 if (aChipsetType != mHWData->mChipsetType)
1484 {
1485 i_setModified(IsModified_MachineData);
1486 mHWData.backup();
1487 mHWData->mChipsetType = aChipsetType;
1488
1489 // Resize network adapter array, to be finalized on commit/rollback.
1490 // We must not throw away entries yet, otherwise settings are lost
1491 // without a way to roll back.
1492 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1493 size_t oldCount = mNetworkAdapters.size();
1494 if (newCount > oldCount)
1495 {
1496 mNetworkAdapters.resize(newCount);
1497 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1498 {
1499 unconst(mNetworkAdapters[slot]).createObject();
1500 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1501 }
1502 }
1503 }
1504
1505 return S_OK;
1506}
1507
1508HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1509{
1510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1511
1512 *aIommuType = mHWData->mIommuType;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::setIommuType(IommuType_T aIommuType)
1518{
1519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 HRESULT rc = i_checkStateDependency(MutableStateDep);
1522 if (FAILED(rc)) return rc;
1523
1524 if (aIommuType != mHWData->mIommuType)
1525 {
1526 if (aIommuType == IommuType_Intel)
1527 {
1528#ifndef VBOX_WITH_IOMMU_INTEL
1529 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1530 return E_UNEXPECTED;
1531#endif
1532 }
1533
1534 i_setModified(IsModified_MachineData);
1535 mHWData.backup();
1536 mHWData->mIommuType = aIommuType;
1537 }
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1543{
1544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 aParavirtDebug = mHWData->mParavirtDebug;
1547 return S_OK;
1548}
1549
1550HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1551{
1552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 HRESULT rc = i_checkStateDependency(MutableStateDep);
1555 if (FAILED(rc)) return rc;
1556
1557 /** @todo Parse/validate options? */
1558 if (aParavirtDebug != mHWData->mParavirtDebug)
1559 {
1560 i_setModified(IsModified_MachineData);
1561 mHWData.backup();
1562 mHWData->mParavirtDebug = aParavirtDebug;
1563 }
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1569{
1570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 *aParavirtProvider = mHWData->mParavirtProvider;
1573
1574 return S_OK;
1575}
1576
1577HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1578{
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 HRESULT rc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(rc)) return rc;
1583
1584 if (aParavirtProvider != mHWData->mParavirtProvider)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588 mHWData->mParavirtProvider = aParavirtProvider;
1589 }
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1595{
1596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 *aParavirtProvider = mHWData->mParavirtProvider;
1599 switch (mHWData->mParavirtProvider)
1600 {
1601 case ParavirtProvider_None:
1602 case ParavirtProvider_HyperV:
1603 case ParavirtProvider_KVM:
1604 case ParavirtProvider_Minimal:
1605 break;
1606
1607 /* Resolve dynamic provider types to the effective types. */
1608 default:
1609 {
1610 ComObjPtr<GuestOSType> pGuestOSType;
1611 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1612 pGuestOSType);
1613 if (FAILED(hrc2) || pGuestOSType.isNull())
1614 {
1615 *aParavirtProvider = ParavirtProvider_None;
1616 break;
1617 }
1618
1619 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1620 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1621
1622 switch (mHWData->mParavirtProvider)
1623 {
1624 case ParavirtProvider_Legacy:
1625 {
1626 if (fOsXGuest)
1627 *aParavirtProvider = ParavirtProvider_Minimal;
1628 else
1629 *aParavirtProvider = ParavirtProvider_None;
1630 break;
1631 }
1632
1633 case ParavirtProvider_Default:
1634 {
1635 if (fOsXGuest)
1636 *aParavirtProvider = ParavirtProvider_Minimal;
1637 else if ( mUserData->s.strOsType == "Windows11_64"
1638 || mUserData->s.strOsType == "Windows10"
1639 || mUserData->s.strOsType == "Windows10_64"
1640 || mUserData->s.strOsType == "Windows81"
1641 || mUserData->s.strOsType == "Windows81_64"
1642 || mUserData->s.strOsType == "Windows8"
1643 || mUserData->s.strOsType == "Windows8_64"
1644 || mUserData->s.strOsType == "Windows7"
1645 || mUserData->s.strOsType == "Windows7_64"
1646 || mUserData->s.strOsType == "WindowsVista"
1647 || mUserData->s.strOsType == "WindowsVista_64"
1648 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1649 || mUserData->s.strOsType.startsWith("Windows201"))
1650 && mUserData->s.strOsType.endsWith("_64"))
1651 || mUserData->s.strOsType == "Windows2012"
1652 || mUserData->s.strOsType == "Windows2012_64"
1653 || mUserData->s.strOsType == "Windows2008"
1654 || mUserData->s.strOsType == "Windows2008_64")
1655 {
1656 *aParavirtProvider = ParavirtProvider_HyperV;
1657 }
1658 else if (guestTypeFamilyId == "Linux" &&
1659 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1660 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1661 mUserData->s.strOsType != "Linux24_64")
1662 {
1663 *aParavirtProvider = ParavirtProvider_KVM;
1664 }
1665 else
1666 *aParavirtProvider = ParavirtProvider_None;
1667 break;
1668 }
1669
1670 default: AssertFailedBreak(); /* Shut up MSC. */
1671 }
1672 break;
1673 }
1674 }
1675
1676 Assert( *aParavirtProvider == ParavirtProvider_None
1677 || *aParavirtProvider == ParavirtProvider_Minimal
1678 || *aParavirtProvider == ParavirtProvider_HyperV
1679 || *aParavirtProvider == ParavirtProvider_KVM);
1680 return S_OK;
1681}
1682
1683HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1684{
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 aHardwareVersion = mHWData->mHWVersion;
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1693{
1694 /* check known version */
1695 Utf8Str hwVersion = aHardwareVersion;
1696 if ( hwVersion.compare("1") != 0
1697 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1698 return setError(E_INVALIDARG,
1699 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1700
1701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 HRESULT rc = i_checkStateDependency(MutableStateDep);
1704 if (FAILED(rc)) return rc;
1705
1706 i_setModified(IsModified_MachineData);
1707 mHWData.backup();
1708 mHWData->mHWVersion = aHardwareVersion;
1709
1710 return S_OK;
1711}
1712
1713HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1714{
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 if (!mHWData->mHardwareUUID.isZero())
1718 aHardwareUUID = mHWData->mHardwareUUID;
1719 else
1720 aHardwareUUID = mData->mUuid;
1721
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1726{
1727 if (!aHardwareUUID.isValid())
1728 return E_INVALIDARG;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 HRESULT rc = i_checkStateDependency(MutableStateDep);
1733 if (FAILED(rc)) return rc;
1734
1735 i_setModified(IsModified_MachineData);
1736 mHWData.backup();
1737 if (aHardwareUUID == mData->mUuid)
1738 mHWData->mHardwareUUID.clear();
1739 else
1740 mHWData->mHardwareUUID = aHardwareUUID;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aMemorySize = mHWData->mMemorySize;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setMemorySize(ULONG aMemorySize)
1755{
1756 /* check RAM limits */
1757 if ( aMemorySize < MM_RAM_MIN_IN_MB
1758 || aMemorySize > MM_RAM_MAX_IN_MB
1759 )
1760 return setError(E_INVALIDARG,
1761 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1762 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 HRESULT rc = i_checkStateDependency(MutableStateDep);
1767 if (FAILED(rc)) return rc;
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mMemorySize = aMemorySize;
1772
1773 return S_OK;
1774}
1775
1776HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1777{
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 *aCPUCount = mHWData->mCPUCount;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::setCPUCount(ULONG aCPUCount)
1786{
1787 /* check CPU limits */
1788 if ( aCPUCount < SchemaDefs::MinCPUCount
1789 || aCPUCount > SchemaDefs::MaxCPUCount
1790 )
1791 return setError(E_INVALIDARG,
1792 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1793 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1794
1795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1798 if (mHWData->mCPUHotPlugEnabled)
1799 {
1800 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1801 {
1802 if (mHWData->mCPUAttached[idx])
1803 return setError(E_INVALIDARG,
1804 tr("There is still a CPU attached to socket %lu."
1805 "Detach the CPU before removing the socket"),
1806 aCPUCount, idx+1);
1807 }
1808 }
1809
1810 HRESULT rc = i_checkStateDependency(MutableStateDep);
1811 if (FAILED(rc)) return rc;
1812
1813 i_setModified(IsModified_MachineData);
1814 mHWData.backup();
1815 mHWData->mCPUCount = aCPUCount;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1830{
1831 HRESULT rc = S_OK;
1832
1833 /* check throttle limits */
1834 if ( aCPUExecutionCap < 1
1835 || aCPUExecutionCap > 100
1836 )
1837 return setError(E_INVALIDARG,
1838 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1839 aCPUExecutionCap, 1, 100);
1840
1841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1842
1843 rc = i_checkStateDependency(MutableOrRunningStateDep);
1844 if (FAILED(rc)) return rc;
1845
1846 alock.release();
1847 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1848 alock.acquire();
1849 if (FAILED(rc)) return rc;
1850
1851 i_setModified(IsModified_MachineData);
1852 mHWData.backup();
1853 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1854
1855 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1856 if (Global::IsOnline(mData->mMachineState))
1857 i_saveSettings(NULL, alock);
1858
1859 return S_OK;
1860}
1861
1862HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1867
1868 return S_OK;
1869}
1870
1871HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1872{
1873 HRESULT rc = S_OK;
1874
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 rc = i_checkStateDependency(MutableStateDep);
1878 if (FAILED(rc)) return rc;
1879
1880 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1881 {
1882 if (aCPUHotPlugEnabled)
1883 {
1884 i_setModified(IsModified_MachineData);
1885 mHWData.backup();
1886
1887 /* Add the amount of CPUs currently attached */
1888 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1889 mHWData->mCPUAttached[i] = true;
1890 }
1891 else
1892 {
1893 /*
1894 * We can disable hotplug only if the amount of maximum CPUs is equal
1895 * to the amount of attached CPUs
1896 */
1897 unsigned cCpusAttached = 0;
1898 unsigned iHighestId = 0;
1899
1900 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1901 {
1902 if (mHWData->mCPUAttached[i])
1903 {
1904 cCpusAttached++;
1905 iHighestId = i;
1906 }
1907 }
1908
1909 if ( (cCpusAttached != mHWData->mCPUCount)
1910 || (iHighestId >= mHWData->mCPUCount))
1911 return setError(E_INVALIDARG,
1912 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1913
1914 i_setModified(IsModified_MachineData);
1915 mHWData.backup();
1916 }
1917 }
1918
1919 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1920
1921 return rc;
1922}
1923
1924HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1925{
1926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1929
1930 return S_OK;
1931}
1932
1933HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1934{
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1938 if (SUCCEEDED(hrc))
1939 {
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1943 }
1944 return hrc;
1945}
1946
1947HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950 aCPUProfile = mHWData->mCpuProfile;
1951 return S_OK;
1952}
1953
1954HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1955{
1956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1957 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1958 if (SUCCEEDED(hrc))
1959 {
1960 i_setModified(IsModified_MachineData);
1961 mHWData.backup();
1962 /* Empty equals 'host'. */
1963 if (aCPUProfile.isNotEmpty())
1964 mHWData->mCpuProfile = aCPUProfile;
1965 else
1966 mHWData->mCpuProfile = "host";
1967 }
1968 return hrc;
1969}
1970
1971HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1972{
1973#ifdef VBOX_WITH_USB_CARDREADER
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1977
1978 return S_OK;
1979#else
1980 NOREF(aEmulatedUSBCardReaderEnabled);
1981 return E_NOTIMPL;
1982#endif
1983}
1984
1985HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1986{
1987#ifdef VBOX_WITH_USB_CARDREADER
1988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1991 if (FAILED(rc)) return rc;
1992
1993 i_setModified(IsModified_MachineData);
1994 mHWData.backup();
1995 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1996
1997 return S_OK;
1998#else
1999 NOREF(aEmulatedUSBCardReaderEnabled);
2000 return E_NOTIMPL;
2001#endif
2002}
2003
2004HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2005{
2006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2007
2008 *aHPETEnabled = mHWData->mHPETEnabled;
2009
2010 return S_OK;
2011}
2012
2013HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2014{
2015 HRESULT rc = S_OK;
2016
2017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2018
2019 rc = i_checkStateDependency(MutableStateDep);
2020 if (FAILED(rc)) return rc;
2021
2022 i_setModified(IsModified_MachineData);
2023 mHWData.backup();
2024
2025 mHWData->mHPETEnabled = aHPETEnabled;
2026
2027 return rc;
2028}
2029
2030/** @todo this method should not be public */
2031HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2032{
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2036
2037 return S_OK;
2038}
2039
2040/**
2041 * Set the memory balloon size.
2042 *
2043 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2044 * we have to make sure that we never call IGuest from here.
2045 */
2046HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2047{
2048 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2049#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2050 /* check limits */
2051 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2052 return setError(E_INVALIDARG,
2053 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2054 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2055
2056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2059 if (FAILED(rc)) return rc;
2060
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2064
2065 return S_OK;
2066#else
2067 NOREF(aMemoryBalloonSize);
2068 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2069#endif
2070}
2071
2072HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2077 return S_OK;
2078}
2079
2080HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2081{
2082#ifdef VBOX_WITH_PAGE_SHARING
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2092 return S_OK;
2093#else
2094 NOREF(aPageFusionEnabled);
2095 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2096#endif
2097}
2098
2099HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2100{
2101 /* mBIOSSettings is constant during life time, no need to lock */
2102 aBIOSSettings = mBIOSSettings;
2103
2104 return S_OK;
2105}
2106
2107HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2108{
2109 /* mTrustedPlatformModule is constant during life time, no need to lock */
2110 aTrustedPlatformModule = mTrustedPlatformModule;
2111
2112 return S_OK;
2113}
2114
2115HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2116{
2117 /* mNvramStore is constant during life time, no need to lock */
2118 aNvramStore = mNvramStore;
2119
2120 return S_OK;
2121}
2122
2123HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2124{
2125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 aRecordingSettings = mRecordingSettings;
2128
2129 return S_OK;
2130}
2131
2132HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2133{
2134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 aGraphicsAdapter = mGraphicsAdapter;
2137
2138 return S_OK;
2139}
2140
2141HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2142{
2143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2144
2145 switch (aProperty)
2146 {
2147 case CPUPropertyType_PAE:
2148 *aValue = mHWData->mPAEEnabled;
2149 break;
2150
2151 case CPUPropertyType_LongMode:
2152 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2153 *aValue = TRUE;
2154 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2155 *aValue = FALSE;
2156#if HC_ARCH_BITS == 64
2157 else
2158 *aValue = TRUE;
2159#else
2160 else
2161 {
2162 *aValue = FALSE;
2163
2164 ComObjPtr<GuestOSType> pGuestOSType;
2165 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2166 pGuestOSType);
2167 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2168 {
2169 if (pGuestOSType->i_is64Bit())
2170 {
2171 ComObjPtr<Host> pHost = mParent->i_host();
2172 alock.release();
2173
2174 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2175 if (FAILED(hrc2))
2176 *aValue = FALSE;
2177 }
2178 }
2179 }
2180#endif
2181 break;
2182
2183 case CPUPropertyType_TripleFaultReset:
2184 *aValue = mHWData->mTripleFaultReset;
2185 break;
2186
2187 case CPUPropertyType_APIC:
2188 *aValue = mHWData->mAPIC;
2189 break;
2190
2191 case CPUPropertyType_X2APIC:
2192 *aValue = mHWData->mX2APIC;
2193 break;
2194
2195 case CPUPropertyType_IBPBOnVMExit:
2196 *aValue = mHWData->mIBPBOnVMExit;
2197 break;
2198
2199 case CPUPropertyType_IBPBOnVMEntry:
2200 *aValue = mHWData->mIBPBOnVMEntry;
2201 break;
2202
2203 case CPUPropertyType_SpecCtrl:
2204 *aValue = mHWData->mSpecCtrl;
2205 break;
2206
2207 case CPUPropertyType_SpecCtrlByHost:
2208 *aValue = mHWData->mSpecCtrlByHost;
2209 break;
2210
2211 case CPUPropertyType_HWVirt:
2212 *aValue = mHWData->mNestedHWVirt;
2213 break;
2214
2215 case CPUPropertyType_L1DFlushOnEMTScheduling:
2216 *aValue = mHWData->mL1DFlushOnSched;
2217 break;
2218
2219 case CPUPropertyType_L1DFlushOnVMEntry:
2220 *aValue = mHWData->mL1DFlushOnVMEntry;
2221 break;
2222
2223 case CPUPropertyType_MDSClearOnEMTScheduling:
2224 *aValue = mHWData->mMDSClearOnSched;
2225 break;
2226
2227 case CPUPropertyType_MDSClearOnVMEntry:
2228 *aValue = mHWData->mMDSClearOnVMEntry;
2229 break;
2230
2231 default:
2232 return E_INVALIDARG;
2233 }
2234 return S_OK;
2235}
2236
2237HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2238{
2239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2240
2241 HRESULT rc = i_checkStateDependency(MutableStateDep);
2242 if (FAILED(rc)) return rc;
2243
2244 switch (aProperty)
2245 {
2246 case CPUPropertyType_PAE:
2247 i_setModified(IsModified_MachineData);
2248 mHWData.backup();
2249 mHWData->mPAEEnabled = !!aValue;
2250 break;
2251
2252 case CPUPropertyType_LongMode:
2253 i_setModified(IsModified_MachineData);
2254 mHWData.backup();
2255 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2256 break;
2257
2258 case CPUPropertyType_TripleFaultReset:
2259 i_setModified(IsModified_MachineData);
2260 mHWData.backup();
2261 mHWData->mTripleFaultReset = !!aValue;
2262 break;
2263
2264 case CPUPropertyType_APIC:
2265 if (mHWData->mX2APIC)
2266 aValue = TRUE;
2267 i_setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 mHWData->mAPIC = !!aValue;
2270 break;
2271
2272 case CPUPropertyType_X2APIC:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mX2APIC = !!aValue;
2276 if (aValue)
2277 mHWData->mAPIC = !!aValue;
2278 break;
2279
2280 case CPUPropertyType_IBPBOnVMExit:
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 mHWData->mIBPBOnVMExit = !!aValue;
2284 break;
2285
2286 case CPUPropertyType_IBPBOnVMEntry:
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 mHWData->mIBPBOnVMEntry = !!aValue;
2290 break;
2291
2292 case CPUPropertyType_SpecCtrl:
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mSpecCtrl = !!aValue;
2296 break;
2297
2298 case CPUPropertyType_SpecCtrlByHost:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mSpecCtrlByHost = !!aValue;
2302 break;
2303
2304 case CPUPropertyType_HWVirt:
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mNestedHWVirt = !!aValue;
2308 break;
2309
2310 case CPUPropertyType_L1DFlushOnEMTScheduling:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mL1DFlushOnSched = !!aValue;
2314 break;
2315
2316 case CPUPropertyType_L1DFlushOnVMEntry:
2317 i_setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mL1DFlushOnVMEntry = !!aValue;
2320 break;
2321
2322 case CPUPropertyType_MDSClearOnEMTScheduling:
2323 i_setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mMDSClearOnSched = !!aValue;
2326 break;
2327
2328 case CPUPropertyType_MDSClearOnVMEntry:
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mMDSClearOnVMEntry = !!aValue;
2332 break;
2333
2334 default:
2335 return E_INVALIDARG;
2336 }
2337 return S_OK;
2338}
2339
2340HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2341 ULONG *aValEcx, ULONG *aValEdx)
2342{
2343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2344 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2345 {
2346 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2347 it != mHWData->mCpuIdLeafList.end();
2348 ++it)
2349 {
2350 if (aOrdinal == 0)
2351 {
2352 const settings::CpuIdLeaf &rLeaf= *it;
2353 *aIdx = rLeaf.idx;
2354 *aSubIdx = rLeaf.idxSub;
2355 *aValEax = rLeaf.uEax;
2356 *aValEbx = rLeaf.uEbx;
2357 *aValEcx = rLeaf.uEcx;
2358 *aValEdx = rLeaf.uEdx;
2359 return S_OK;
2360 }
2361 aOrdinal--;
2362 }
2363 }
2364 return E_INVALIDARG;
2365}
2366
2367HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2368{
2369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2370
2371 /*
2372 * Search the list.
2373 */
2374 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2375 {
2376 const settings::CpuIdLeaf &rLeaf= *it;
2377 if ( rLeaf.idx == aIdx
2378 && ( aSubIdx == UINT32_MAX
2379 || rLeaf.idxSub == aSubIdx) )
2380 {
2381 *aValEax = rLeaf.uEax;
2382 *aValEbx = rLeaf.uEbx;
2383 *aValEcx = rLeaf.uEcx;
2384 *aValEdx = rLeaf.uEdx;
2385 return S_OK;
2386 }
2387 }
2388
2389 return E_INVALIDARG;
2390}
2391
2392
2393HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2394{
2395 /*
2396 * Validate input before taking locks and checking state.
2397 */
2398 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2399 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2400 if ( aIdx >= UINT32_C(0x20)
2401 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2402 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2403 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2404
2405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2406 HRESULT rc = i_checkStateDependency(MutableStateDep);
2407 if (FAILED(rc)) return rc;
2408
2409 /*
2410 * Impose a maximum number of leaves.
2411 */
2412 if (mHWData->mCpuIdLeafList.size() > 256)
2413 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2414
2415 /*
2416 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2417 */
2418 i_setModified(IsModified_MachineData);
2419 mHWData.backup();
2420
2421 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2422 {
2423 settings::CpuIdLeaf &rLeaf= *it;
2424 if ( rLeaf.idx == aIdx
2425 && ( aSubIdx == UINT32_MAX
2426 || rLeaf.idxSub == aSubIdx) )
2427 it = mHWData->mCpuIdLeafList.erase(it);
2428 else
2429 ++it;
2430 }
2431
2432 settings::CpuIdLeaf NewLeaf;
2433 NewLeaf.idx = aIdx;
2434 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2435 NewLeaf.uEax = aValEax;
2436 NewLeaf.uEbx = aValEbx;
2437 NewLeaf.uEcx = aValEcx;
2438 NewLeaf.uEdx = aValEdx;
2439 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2440 return S_OK;
2441}
2442
2443HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2444{
2445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2446
2447 HRESULT rc = i_checkStateDependency(MutableStateDep);
2448 if (FAILED(rc)) return rc;
2449
2450 /*
2451 * Do the removal.
2452 */
2453 bool fModified = mHWData.isBackedUp();
2454 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2455 {
2456 settings::CpuIdLeaf &rLeaf= *it;
2457 if ( rLeaf.idx == aIdx
2458 && ( aSubIdx == UINT32_MAX
2459 || rLeaf.idxSub == aSubIdx) )
2460 {
2461 if (!fModified)
2462 {
2463 fModified = true;
2464 i_setModified(IsModified_MachineData);
2465 mHWData.backup();
2466 // Start from the beginning, since mHWData.backup() creates
2467 // a new list, causing iterator mixup. This makes sure that
2468 // the settings are not unnecessarily marked as modified,
2469 // at the price of extra list walking.
2470 it = mHWData->mCpuIdLeafList.begin();
2471 }
2472 else
2473 it = mHWData->mCpuIdLeafList.erase(it);
2474 }
2475 else
2476 ++it;
2477 }
2478
2479 return S_OK;
2480}
2481
2482HRESULT Machine::removeAllCPUIDLeaves()
2483{
2484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 HRESULT rc = i_checkStateDependency(MutableStateDep);
2487 if (FAILED(rc)) return rc;
2488
2489 if (mHWData->mCpuIdLeafList.size() > 0)
2490 {
2491 i_setModified(IsModified_MachineData);
2492 mHWData.backup();
2493
2494 mHWData->mCpuIdLeafList.clear();
2495 }
2496
2497 return S_OK;
2498}
2499HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2500{
2501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 switch(aProperty)
2504 {
2505 case HWVirtExPropertyType_Enabled:
2506 *aValue = mHWData->mHWVirtExEnabled;
2507 break;
2508
2509 case HWVirtExPropertyType_VPID:
2510 *aValue = mHWData->mHWVirtExVPIDEnabled;
2511 break;
2512
2513 case HWVirtExPropertyType_NestedPaging:
2514 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2515 break;
2516
2517 case HWVirtExPropertyType_UnrestrictedExecution:
2518 *aValue = mHWData->mHWVirtExUXEnabled;
2519 break;
2520
2521 case HWVirtExPropertyType_LargePages:
2522 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2523 break;
2524
2525 case HWVirtExPropertyType_Force:
2526 *aValue = mHWData->mHWVirtExForceEnabled;
2527 break;
2528
2529 case HWVirtExPropertyType_UseNativeApi:
2530 *aValue = mHWData->mHWVirtExUseNativeApi;
2531 break;
2532
2533 case HWVirtExPropertyType_VirtVmsaveVmload:
2534 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2535 break;
2536
2537 default:
2538 return E_INVALIDARG;
2539 }
2540 return S_OK;
2541}
2542
2543HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2544{
2545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 HRESULT rc = i_checkStateDependency(MutableStateDep);
2548 if (FAILED(rc)) return rc;
2549
2550 switch (aProperty)
2551 {
2552 case HWVirtExPropertyType_Enabled:
2553 i_setModified(IsModified_MachineData);
2554 mHWData.backup();
2555 mHWData->mHWVirtExEnabled = !!aValue;
2556 break;
2557
2558 case HWVirtExPropertyType_VPID:
2559 i_setModified(IsModified_MachineData);
2560 mHWData.backup();
2561 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2562 break;
2563
2564 case HWVirtExPropertyType_NestedPaging:
2565 i_setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2568 break;
2569
2570 case HWVirtExPropertyType_UnrestrictedExecution:
2571 i_setModified(IsModified_MachineData);
2572 mHWData.backup();
2573 mHWData->mHWVirtExUXEnabled = !!aValue;
2574 break;
2575
2576 case HWVirtExPropertyType_LargePages:
2577 i_setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2580 break;
2581
2582 case HWVirtExPropertyType_Force:
2583 i_setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExForceEnabled = !!aValue;
2586 break;
2587
2588 case HWVirtExPropertyType_UseNativeApi:
2589 i_setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mHWVirtExUseNativeApi = !!aValue;
2592 break;
2593
2594 case HWVirtExPropertyType_VirtVmsaveVmload:
2595 i_setModified(IsModified_MachineData);
2596 mHWData.backup();
2597 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2598 break;
2599
2600 default:
2601 return E_INVALIDARG;
2602 }
2603
2604 return S_OK;
2605}
2606
2607HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2608{
2609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2612
2613 return S_OK;
2614}
2615
2616HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2617{
2618 /** @todo (r=dmik):
2619 * 1. Allow to change the name of the snapshot folder containing snapshots
2620 * 2. Rename the folder on disk instead of just changing the property
2621 * value (to be smart and not to leave garbage). Note that it cannot be
2622 * done here because the change may be rolled back. Thus, the right
2623 * place is #saveSettings().
2624 */
2625
2626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2627
2628 HRESULT rc = i_checkStateDependency(MutableStateDep);
2629 if (FAILED(rc)) return rc;
2630
2631 if (!mData->mCurrentSnapshot.isNull())
2632 return setError(E_FAIL,
2633 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2634
2635 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2636
2637 if (strSnapshotFolder.isEmpty())
2638 strSnapshotFolder = "Snapshots";
2639 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2640 if (RT_FAILURE(vrc))
2641 return setErrorBoth(E_FAIL, vrc,
2642 tr("Invalid snapshot folder '%s' (%Rrc)"),
2643 strSnapshotFolder.c_str(), vrc);
2644
2645 i_setModified(IsModified_MachineData);
2646 mUserData.backup();
2647
2648 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2649
2650 return S_OK;
2651}
2652
2653HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2654{
2655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 aMediumAttachments.resize(mMediumAttachments->size());
2658 size_t i = 0;
2659 for (MediumAttachmentList::const_iterator
2660 it = mMediumAttachments->begin();
2661 it != mMediumAttachments->end();
2662 ++it, ++i)
2663 aMediumAttachments[i] = *it;
2664
2665 return S_OK;
2666}
2667
2668HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2669{
2670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2671
2672 Assert(!!mVRDEServer);
2673
2674 aVRDEServer = mVRDEServer;
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aAudioAdapter = mAudioAdapter;
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2689{
2690#ifdef VBOX_WITH_VUSB
2691 clearError();
2692 MultiResult rc(S_OK);
2693
2694# ifdef VBOX_WITH_USB
2695 rc = mParent->i_host()->i_checkUSBProxyService();
2696 if (FAILED(rc)) return rc;
2697# endif
2698
2699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2700
2701 aUSBControllers.resize(mUSBControllers->size());
2702 size_t i = 0;
2703 for (USBControllerList::const_iterator
2704 it = mUSBControllers->begin();
2705 it != mUSBControllers->end();
2706 ++it, ++i)
2707 aUSBControllers[i] = *it;
2708
2709 return S_OK;
2710#else
2711 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2712 * extended error info to indicate that USB is simply not available
2713 * (w/o treating it as a failure), for example, as in OSE */
2714 NOREF(aUSBControllers);
2715 ReturnComNotImplemented();
2716#endif /* VBOX_WITH_VUSB */
2717}
2718
2719HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2720{
2721#ifdef VBOX_WITH_VUSB
2722 clearError();
2723 MultiResult rc(S_OK);
2724
2725# ifdef VBOX_WITH_USB
2726 rc = mParent->i_host()->i_checkUSBProxyService();
2727 if (FAILED(rc)) return rc;
2728# endif
2729
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aUSBDeviceFilters = mUSBDeviceFilters;
2733 return rc;
2734#else
2735 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2736 * extended error info to indicate that USB is simply not available
2737 * (w/o treating it as a failure), for example, as in OSE */
2738 NOREF(aUSBDeviceFilters);
2739 ReturnComNotImplemented();
2740#endif /* VBOX_WITH_VUSB */
2741}
2742
2743HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 aSettingsFilePath = mData->m_strConfigFileFull;
2748
2749 return S_OK;
2750}
2751
2752HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2753{
2754 RT_NOREF(aSettingsFilePath);
2755 ReturnComNotImplemented();
2756}
2757
2758HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2763 if (FAILED(rc)) return rc;
2764
2765 if (!mData->pMachineConfigFile->fileExists())
2766 // this is a new machine, and no config file exists yet:
2767 *aSettingsModified = TRUE;
2768 else
2769 *aSettingsModified = (mData->flModifications != 0);
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 *aSessionState = mData->mSession.mState;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 aSessionName = mData->mSession.mName;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2793{
2794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2795
2796 *aSessionPID = mData->mSession.mPID;
2797
2798 return S_OK;
2799}
2800
2801HRESULT Machine::getState(MachineState_T *aState)
2802{
2803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2804
2805 *aState = mData->mMachineState;
2806 Assert(mData->mMachineState != MachineState_Null);
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2812{
2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2821{
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 aStateFilePath = mSSData->strStateFilePath;
2825
2826 return S_OK;
2827}
2828
2829HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2830{
2831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 i_getLogFolder(aLogFolder);
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 aCurrentSnapshot = mData->mCurrentSnapshot;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2852 ? 0
2853 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2854
2855 return S_OK;
2856}
2857
2858HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2859{
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 /* Note: for machines with no snapshots, we always return FALSE
2863 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2864 * reasons :) */
2865
2866 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2867 ? FALSE
2868 : mData->mCurrentStateModified;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2874{
2875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 aSharedFolders.resize(mHWData->mSharedFolders.size());
2878 size_t i = 0;
2879 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2880 it = mHWData->mSharedFolders.begin();
2881 it != mHWData->mSharedFolders.end();
2882 ++it, ++i)
2883 aSharedFolders[i] = *it;
2884
2885 return S_OK;
2886}
2887
2888HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2889{
2890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2891
2892 *aClipboardMode = mHWData->mClipboardMode;
2893
2894 return S_OK;
2895}
2896
2897HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2898{
2899 HRESULT rc = S_OK;
2900
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 rc = i_checkStateDependency(MutableOrRunningStateDep);
2904 if (FAILED(rc)) return rc;
2905
2906 alock.release();
2907 rc = i_onClipboardModeChange(aClipboardMode);
2908 alock.acquire();
2909 if (FAILED(rc)) return rc;
2910
2911 i_setModified(IsModified_MachineData);
2912 mHWData.backup();
2913 mHWData->mClipboardMode = aClipboardMode;
2914
2915 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2916 if (Global::IsOnline(mData->mMachineState))
2917 i_saveSettings(NULL, alock);
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2932{
2933 HRESULT rc = S_OK;
2934
2935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 rc = i_checkStateDependency(MutableOrRunningStateDep);
2938 if (FAILED(rc)) return rc;
2939
2940 alock.release();
2941 rc = i_onClipboardFileTransferModeChange(aEnabled);
2942 alock.acquire();
2943 if (FAILED(rc)) return rc;
2944
2945 i_setModified(IsModified_MachineData);
2946 mHWData.backup();
2947 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2948
2949 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2950 if (Global::IsOnline(mData->mMachineState))
2951 i_saveSettings(NULL, alock);
2952
2953 return S_OK;
2954}
2955
2956HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2957{
2958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 *aDnDMode = mHWData->mDnDMode;
2961
2962 return S_OK;
2963}
2964
2965HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2966{
2967 HRESULT rc = S_OK;
2968
2969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 rc = i_checkStateDependency(MutableOrRunningStateDep);
2972 if (FAILED(rc)) return rc;
2973
2974 alock.release();
2975 rc = i_onDnDModeChange(aDnDMode);
2976
2977 alock.acquire();
2978 if (FAILED(rc)) return rc;
2979
2980 i_setModified(IsModified_MachineData);
2981 mHWData.backup();
2982 mHWData->mDnDMode = aDnDMode;
2983
2984 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2985 if (Global::IsOnline(mData->mMachineState))
2986 i_saveSettings(NULL, alock);
2987
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 aStorageControllers.resize(mStorageControllers->size());
2996 size_t i = 0;
2997 for (StorageControllerList::const_iterator
2998 it = mStorageControllers->begin();
2999 it != mStorageControllers->end();
3000 ++it, ++i)
3001 aStorageControllers[i] = *it;
3002
3003 return S_OK;
3004}
3005
3006HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3007{
3008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3009
3010 *aEnabled = mUserData->s.fTeleporterEnabled;
3011
3012 return S_OK;
3013}
3014
3015HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3016{
3017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3018
3019 /* Only allow it to be set to true when PoweredOff or Aborted.
3020 (Clearing it is always permitted.) */
3021 if ( aTeleporterEnabled
3022 && mData->mRegistered
3023 && ( !i_isSessionMachine()
3024 || ( mData->mMachineState != MachineState_PoweredOff
3025 && mData->mMachineState != MachineState_Teleported
3026 && mData->mMachineState != MachineState_Aborted
3027 )
3028 )
3029 )
3030 return setError(VBOX_E_INVALID_VM_STATE,
3031 tr("The machine is not powered off (state is %s)"),
3032 Global::stringifyMachineState(mData->mMachineState));
3033
3034 i_setModified(IsModified_MachineData);
3035 mUserData.backup();
3036 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3037
3038 return S_OK;
3039}
3040
3041HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3042{
3043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3046
3047 return S_OK;
3048}
3049
3050HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3051{
3052 if (aTeleporterPort >= _64K)
3053 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3054
3055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3058 if (FAILED(rc)) return rc;
3059
3060 i_setModified(IsModified_MachineData);
3061 mUserData.backup();
3062 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3063
3064 return S_OK;
3065}
3066
3067HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3068{
3069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3072
3073 return S_OK;
3074}
3075
3076HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3077{
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3081 if (FAILED(rc)) return rc;
3082
3083 i_setModified(IsModified_MachineData);
3084 mUserData.backup();
3085 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3091{
3092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3093 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3094
3095 return S_OK;
3096}
3097
3098HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3099{
3100 /*
3101 * Hash the password first.
3102 */
3103 com::Utf8Str aT = aTeleporterPassword;
3104
3105 if (!aT.isEmpty())
3106 {
3107 if (VBoxIsPasswordHashed(&aT))
3108 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3109 VBoxHashPassword(&aT);
3110 }
3111
3112 /*
3113 * Do the update.
3114 */
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3117 if (SUCCEEDED(hrc))
3118 {
3119 i_setModified(IsModified_MachineData);
3120 mUserData.backup();
3121 mUserData->s.strTeleporterPassword = aT;
3122 }
3123
3124 return hrc;
3125}
3126
3127HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3128{
3129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3130
3131 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3132
3133 return S_OK;
3134}
3135
3136HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3137{
3138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3139
3140 /* Only allow it to be set to true when PoweredOff or Aborted.
3141 (Clearing it is always permitted.) */
3142 if ( aRTCUseUTC
3143 && mData->mRegistered
3144 && ( !i_isSessionMachine()
3145 || ( mData->mMachineState != MachineState_PoweredOff
3146 && mData->mMachineState != MachineState_Teleported
3147 && mData->mMachineState != MachineState_Aborted
3148 )
3149 )
3150 )
3151 return setError(VBOX_E_INVALID_VM_STATE,
3152 tr("The machine is not powered off (state is %s)"),
3153 Global::stringifyMachineState(mData->mMachineState));
3154
3155 i_setModified(IsModified_MachineData);
3156 mUserData.backup();
3157 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3163{
3164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3167
3168 return S_OK;
3169}
3170
3171HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3172{
3173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3174
3175 HRESULT rc = i_checkStateDependency(MutableStateDep);
3176 if (FAILED(rc)) return rc;
3177
3178 i_setModified(IsModified_MachineData);
3179 mHWData.backup();
3180 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3181
3182 return S_OK;
3183}
3184
3185HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3186{
3187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3188
3189 *aIOCacheSize = mHWData->mIOCacheSize;
3190
3191 return S_OK;
3192}
3193
3194HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3195{
3196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3197
3198 HRESULT rc = i_checkStateDependency(MutableStateDep);
3199 if (FAILED(rc)) return rc;
3200
3201 i_setModified(IsModified_MachineData);
3202 mHWData.backup();
3203 mHWData->mIOCacheSize = aIOCacheSize;
3204
3205 return S_OK;
3206}
3207
3208HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3209{
3210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3211
3212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3213 aKeyId = mSSData->strStateKeyId;
3214#else
3215 aKeyId = com::Utf8Str::Empty;
3216#endif
3217
3218 return S_OK;
3219}
3220
3221HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3222{
3223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3224
3225#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3226 aKeyStore = mSSData->strStateKeyStore;
3227#else
3228 aKeyStore = com::Utf8Str::Empty;
3229#endif
3230
3231 return S_OK;
3232}
3233
3234HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3235{
3236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3237
3238#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3239 aKeyId = mData->mstrLogKeyId;
3240#else
3241 aKeyId = com::Utf8Str::Empty;
3242#endif
3243
3244 return S_OK;
3245}
3246
3247HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3248{
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3252 aKeyStore = mData->mstrLogKeyStore;
3253#else
3254 aKeyStore = com::Utf8Str::Empty;
3255#endif
3256
3257 return S_OK;
3258}
3259
3260
3261/**
3262 * @note Locks objects!
3263 */
3264HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3265 LockType_T aLockType)
3266{
3267 /* check the session state */
3268 SessionState_T state;
3269 HRESULT rc = aSession->COMGETTER(State)(&state);
3270 if (FAILED(rc)) return rc;
3271
3272 if (state != SessionState_Unlocked)
3273 return setError(VBOX_E_INVALID_OBJECT_STATE,
3274 tr("The given session is busy"));
3275
3276 // get the client's IInternalSessionControl interface
3277 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3278 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3279 E_INVALIDARG);
3280
3281 // session name (only used in some code paths)
3282 Utf8Str strSessionName;
3283
3284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
3286 if (!mData->mRegistered)
3287 return setError(E_UNEXPECTED,
3288 tr("The machine '%s' is not registered"),
3289 mUserData->s.strName.c_str());
3290
3291 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3292
3293 SessionState_T oldState = mData->mSession.mState;
3294 /* Hack: in case the session is closing and there is a progress object
3295 * which allows waiting for the session to be closed, take the opportunity
3296 * and do a limited wait (max. 1 second). This helps a lot when the system
3297 * is busy and thus session closing can take a little while. */
3298 if ( mData->mSession.mState == SessionState_Unlocking
3299 && mData->mSession.mProgress)
3300 {
3301 alock.release();
3302 mData->mSession.mProgress->WaitForCompletion(1000);
3303 alock.acquire();
3304 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3305 }
3306
3307 // try again now
3308 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3309 // (i.e. session machine exists)
3310 && (aLockType == LockType_Shared) // caller wants a shared link to the
3311 // existing session that holds the write lock:
3312 )
3313 {
3314 // OK, share the session... we are now dealing with three processes:
3315 // 1) VBoxSVC (where this code runs);
3316 // 2) process C: the caller's client process (who wants a shared session);
3317 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3318
3319 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3320 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3321 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3322 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3323 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3324
3325 /*
3326 * Release the lock before calling the client process. It's safe here
3327 * since the only thing to do after we get the lock again is to add
3328 * the remote control to the list (which doesn't directly influence
3329 * anything).
3330 */
3331 alock.release();
3332
3333 // get the console of the session holding the write lock (this is a remote call)
3334 ComPtr<IConsole> pConsoleW;
3335 if (mData->mSession.mLockType == LockType_VM)
3336 {
3337 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3338 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3339 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3340 if (FAILED(rc))
3341 // the failure may occur w/o any error info (from RPC), so provide one
3342 return setError(VBOX_E_VM_ERROR,
3343 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3344 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3345 }
3346
3347 // share the session machine and W's console with the caller's session
3348 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3349 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3350 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3351
3352 if (FAILED(rc))
3353 // the failure may occur w/o any error info (from RPC), so provide one
3354 return setError(VBOX_E_VM_ERROR,
3355 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3356 alock.acquire();
3357
3358 // need to revalidate the state after acquiring the lock again
3359 if (mData->mSession.mState != SessionState_Locked)
3360 {
3361 pSessionControl->Uninitialize();
3362 return setError(VBOX_E_INVALID_SESSION_STATE,
3363 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3364 mUserData->s.strName.c_str());
3365 }
3366
3367 // add the caller's session to the list
3368 mData->mSession.mRemoteControls.push_back(pSessionControl);
3369 }
3370 else if ( mData->mSession.mState == SessionState_Locked
3371 || mData->mSession.mState == SessionState_Unlocking
3372 )
3373 {
3374 // sharing not permitted, or machine still unlocking:
3375 return setError(VBOX_E_INVALID_OBJECT_STATE,
3376 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3377 mUserData->s.strName.c_str());
3378 }
3379 else
3380 {
3381 // machine is not locked: then write-lock the machine (create the session machine)
3382
3383 // must not be busy
3384 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3385
3386 // get the caller's session PID
3387 RTPROCESS pid = NIL_RTPROCESS;
3388 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3389 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3390 Assert(pid != NIL_RTPROCESS);
3391
3392 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3393
3394 if (fLaunchingVMProcess)
3395 {
3396 if (mData->mSession.mPID == NIL_RTPROCESS)
3397 {
3398 // two or more clients racing for a lock, the one which set the
3399 // session state to Spawning will win, the others will get an
3400 // error as we can't decide here if waiting a little would help
3401 // (only for shared locks this would avoid an error)
3402 return setError(VBOX_E_INVALID_OBJECT_STATE,
3403 tr("The machine '%s' already has a lock request pending"),
3404 mUserData->s.strName.c_str());
3405 }
3406
3407 // this machine is awaiting for a spawning session to be opened:
3408 // then the calling process must be the one that got started by
3409 // LaunchVMProcess()
3410
3411 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3412 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3413
3414#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3415 /* Hardened windows builds spawns three processes when a VM is
3416 launched, the 3rd one is the one that will end up here. */
3417 RTPROCESS pidParent;
3418 int vrc = RTProcQueryParent(pid, &pidParent);
3419 if (RT_SUCCESS(vrc))
3420 vrc = RTProcQueryParent(pidParent, &pidParent);
3421 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3422 || vrc == VERR_ACCESS_DENIED)
3423 {
3424 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3425 mData->mSession.mPID = pid;
3426 }
3427#endif
3428
3429 if (mData->mSession.mPID != pid)
3430 return setError(E_ACCESSDENIED,
3431 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3432 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3433 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3434 }
3435
3436 // create the mutable SessionMachine from the current machine
3437 ComObjPtr<SessionMachine> sessionMachine;
3438 sessionMachine.createObject();
3439 rc = sessionMachine->init(this);
3440 AssertComRC(rc);
3441
3442 /* NOTE: doing return from this function after this point but
3443 * before the end is forbidden since it may call SessionMachine::uninit()
3444 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3445 * lock while still holding the Machine lock in alock so that a deadlock
3446 * is possible due to the wrong lock order. */
3447
3448 if (SUCCEEDED(rc))
3449 {
3450 /*
3451 * Set the session state to Spawning to protect against subsequent
3452 * attempts to open a session and to unregister the machine after
3453 * we release the lock.
3454 */
3455 SessionState_T origState = mData->mSession.mState;
3456 mData->mSession.mState = SessionState_Spawning;
3457
3458#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3459 /* Get the client token ID to be passed to the client process */
3460 Utf8Str strTokenId;
3461 sessionMachine->i_getTokenId(strTokenId);
3462 Assert(!strTokenId.isEmpty());
3463#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3464 /* Get the client token to be passed to the client process */
3465 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3466 /* The token is now "owned" by pToken, fix refcount */
3467 if (!pToken.isNull())
3468 pToken->Release();
3469#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3470
3471 /*
3472 * Release the lock before calling the client process -- it will call
3473 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3474 * because the state is Spawning, so that LaunchVMProcess() and
3475 * LockMachine() calls will fail. This method, called before we
3476 * acquire the lock again, will fail because of the wrong PID.
3477 *
3478 * Note that mData->mSession.mRemoteControls accessed outside
3479 * the lock may not be modified when state is Spawning, so it's safe.
3480 */
3481 alock.release();
3482
3483 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3484#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3485 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3486#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3487 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3488 /* Now the token is owned by the client process. */
3489 pToken.setNull();
3490#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3491 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3492
3493 /* The failure may occur w/o any error info (from RPC), so provide one */
3494 if (FAILED(rc))
3495 setError(VBOX_E_VM_ERROR,
3496 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3497
3498 // get session name, either to remember or to compare against
3499 // the already known session name.
3500 {
3501 Bstr bstrSessionName;
3502 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3503 if (SUCCEEDED(rc2))
3504 strSessionName = bstrSessionName;
3505 }
3506
3507 if ( SUCCEEDED(rc)
3508 && fLaunchingVMProcess
3509 )
3510 {
3511 /* complete the remote session initialization */
3512
3513 /* get the console from the direct session */
3514 ComPtr<IConsole> console;
3515 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3516 ComAssertComRC(rc);
3517
3518 if (SUCCEEDED(rc) && !console)
3519 {
3520 ComAssert(!!console);
3521 rc = E_FAIL;
3522 }
3523
3524 /* assign machine & console to the remote session */
3525 if (SUCCEEDED(rc))
3526 {
3527 /*
3528 * after LaunchVMProcess(), the first and the only
3529 * entry in remoteControls is that remote session
3530 */
3531 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3532 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3533 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3534
3535 /* The failure may occur w/o any error info (from RPC), so provide one */
3536 if (FAILED(rc))
3537 setError(VBOX_E_VM_ERROR,
3538 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3539 }
3540
3541 if (FAILED(rc))
3542 pSessionControl->Uninitialize();
3543 }
3544
3545 /* acquire the lock again */
3546 alock.acquire();
3547
3548 /* Restore the session state */
3549 mData->mSession.mState = origState;
3550 }
3551
3552 // finalize spawning anyway (this is why we don't return on errors above)
3553 if (fLaunchingVMProcess)
3554 {
3555 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3556 /* Note that the progress object is finalized later */
3557 /** @todo Consider checking mData->mSession.mProgress for cancellation
3558 * around here. */
3559
3560 /* We don't reset mSession.mPID here because it is necessary for
3561 * SessionMachine::uninit() to reap the child process later. */
3562
3563 if (FAILED(rc))
3564 {
3565 /* Close the remote session, remove the remote control from the list
3566 * and reset session state to Closed (@note keep the code in sync
3567 * with the relevant part in checkForSpawnFailure()). */
3568
3569 Assert(mData->mSession.mRemoteControls.size() == 1);
3570 if (mData->mSession.mRemoteControls.size() == 1)
3571 {
3572 ErrorInfoKeeper eik;
3573 mData->mSession.mRemoteControls.front()->Uninitialize();
3574 }
3575
3576 mData->mSession.mRemoteControls.clear();
3577 mData->mSession.mState = SessionState_Unlocked;
3578 }
3579 }
3580 else
3581 {
3582 /* memorize PID of the directly opened session */
3583 if (SUCCEEDED(rc))
3584 mData->mSession.mPID = pid;
3585 }
3586
3587 if (SUCCEEDED(rc))
3588 {
3589 mData->mSession.mLockType = aLockType;
3590 /* memorize the direct session control and cache IUnknown for it */
3591 mData->mSession.mDirectControl = pSessionControl;
3592 mData->mSession.mState = SessionState_Locked;
3593 if (!fLaunchingVMProcess)
3594 mData->mSession.mName = strSessionName;
3595 /* associate the SessionMachine with this Machine */
3596 mData->mSession.mMachine = sessionMachine;
3597
3598 /* request an IUnknown pointer early from the remote party for later
3599 * identity checks (it will be internally cached within mDirectControl
3600 * at least on XPCOM) */
3601 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3602 NOREF(unk);
3603 }
3604
3605 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3606 * would break the lock order */
3607 alock.release();
3608
3609 /* uninitialize the created session machine on failure */
3610 if (FAILED(rc))
3611 sessionMachine->uninit();
3612 }
3613
3614 if (SUCCEEDED(rc))
3615 {
3616 /*
3617 * tell the client watcher thread to update the set of
3618 * machines that have open sessions
3619 */
3620 mParent->i_updateClientWatcher();
3621
3622 if (oldState != SessionState_Locked)
3623 /* fire an event */
3624 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3625 }
3626
3627 return rc;
3628}
3629
3630/**
3631 * @note Locks objects!
3632 */
3633HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3634 const com::Utf8Str &aName,
3635 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3636 ComPtr<IProgress> &aProgress)
3637{
3638 Utf8Str strFrontend(aName);
3639 /* "emergencystop" doesn't need the session, so skip the checks/interface
3640 * retrieval. This code doesn't quite fit in here, but introducing a
3641 * special API method would be even more effort, and would require explicit
3642 * support by every API client. It's better to hide the feature a bit. */
3643 if (strFrontend != "emergencystop")
3644 CheckComArgNotNull(aSession);
3645
3646 HRESULT rc = S_OK;
3647 if (strFrontend.isEmpty())
3648 {
3649 Bstr bstrFrontend;
3650 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3651 if (FAILED(rc))
3652 return rc;
3653 strFrontend = bstrFrontend;
3654 if (strFrontend.isEmpty())
3655 {
3656 ComPtr<ISystemProperties> systemProperties;
3657 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3658 if (FAILED(rc))
3659 return rc;
3660 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3661 if (FAILED(rc))
3662 return rc;
3663 strFrontend = bstrFrontend;
3664 }
3665 /* paranoia - emergencystop is not a valid default */
3666 if (strFrontend == "emergencystop")
3667 strFrontend = Utf8Str::Empty;
3668 }
3669 /* default frontend: Qt GUI */
3670 if (strFrontend.isEmpty())
3671 strFrontend = "GUI/Qt";
3672
3673 if (strFrontend != "emergencystop")
3674 {
3675 /* check the session state */
3676 SessionState_T state;
3677 rc = aSession->COMGETTER(State)(&state);
3678 if (FAILED(rc))
3679 return rc;
3680
3681 if (state != SessionState_Unlocked)
3682 return setError(VBOX_E_INVALID_OBJECT_STATE,
3683 tr("The given session is busy"));
3684
3685 /* get the IInternalSessionControl interface */
3686 ComPtr<IInternalSessionControl> control(aSession);
3687 ComAssertMsgRet(!control.isNull(),
3688 ("No IInternalSessionControl interface"),
3689 E_INVALIDARG);
3690
3691 /* get the teleporter enable state for the progress object init. */
3692 BOOL fTeleporterEnabled;
3693 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3694 if (FAILED(rc))
3695 return rc;
3696
3697 /* create a progress object */
3698 ComObjPtr<ProgressProxy> progress;
3699 progress.createObject();
3700 rc = progress->init(mParent,
3701 static_cast<IMachine*>(this),
3702 Bstr(tr("Starting VM")).raw(),
3703 TRUE /* aCancelable */,
3704 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3705 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3706 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3707 2 /* uFirstOperationWeight */,
3708 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3709
3710 if (SUCCEEDED(rc))
3711 {
3712 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3713 if (SUCCEEDED(rc))
3714 {
3715 aProgress = progress;
3716
3717 /* signal the client watcher thread */
3718 mParent->i_updateClientWatcher();
3719
3720 /* fire an event */
3721 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3722 }
3723 }
3724 }
3725 else
3726 {
3727 /* no progress object - either instant success or failure */
3728 aProgress = NULL;
3729
3730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3731
3732 if (mData->mSession.mState != SessionState_Locked)
3733 return setError(VBOX_E_INVALID_OBJECT_STATE,
3734 tr("The machine '%s' is not locked by a session"),
3735 mUserData->s.strName.c_str());
3736
3737 /* must have a VM process associated - do not kill normal API clients
3738 * with an open session */
3739 if (!Global::IsOnline(mData->mMachineState))
3740 return setError(VBOX_E_INVALID_OBJECT_STATE,
3741 tr("The machine '%s' does not have a VM process"),
3742 mUserData->s.strName.c_str());
3743
3744 /* forcibly terminate the VM process */
3745 if (mData->mSession.mPID != NIL_RTPROCESS)
3746 RTProcTerminate(mData->mSession.mPID);
3747
3748 /* signal the client watcher thread, as most likely the client has
3749 * been terminated */
3750 mParent->i_updateClientWatcher();
3751 }
3752
3753 return rc;
3754}
3755
3756HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3757{
3758 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3759 return setError(E_INVALIDARG,
3760 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3761 aPosition, SchemaDefs::MaxBootPosition);
3762
3763 if (aDevice == DeviceType_USB)
3764 return setError(E_NOTIMPL,
3765 tr("Booting from USB device is currently not supported"));
3766
3767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3768
3769 HRESULT rc = i_checkStateDependency(MutableStateDep);
3770 if (FAILED(rc)) return rc;
3771
3772 i_setModified(IsModified_MachineData);
3773 mHWData.backup();
3774 mHWData->mBootOrder[aPosition - 1] = aDevice;
3775
3776 return S_OK;
3777}
3778
3779HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3780{
3781 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3782 return setError(E_INVALIDARG,
3783 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3784 aPosition, SchemaDefs::MaxBootPosition);
3785
3786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3787
3788 *aDevice = mHWData->mBootOrder[aPosition - 1];
3789
3790 return S_OK;
3791}
3792
3793HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3794 LONG aControllerPort,
3795 LONG aDevice,
3796 DeviceType_T aType,
3797 const ComPtr<IMedium> &aMedium)
3798{
3799 IMedium *aM = aMedium;
3800 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3801 aName.c_str(), aControllerPort, aDevice, aType, aM));
3802
3803 // request the host lock first, since might be calling Host methods for getting host drives;
3804 // next, protect the media tree all the while we're in here, as well as our member variables
3805 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3806 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3807
3808 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3809 if (FAILED(rc)) return rc;
3810
3811 /// @todo NEWMEDIA implicit machine registration
3812 if (!mData->mRegistered)
3813 return setError(VBOX_E_INVALID_OBJECT_STATE,
3814 tr("Cannot attach storage devices to an unregistered machine"));
3815
3816 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3817
3818 /* Check for an existing controller. */
3819 ComObjPtr<StorageController> ctl;
3820 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3821 if (FAILED(rc)) return rc;
3822
3823 StorageControllerType_T ctrlType;
3824 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3825 if (FAILED(rc))
3826 return setError(E_FAIL,
3827 tr("Could not get type of controller '%s'"),
3828 aName.c_str());
3829
3830 bool fSilent = false;
3831 Utf8Str strReconfig;
3832
3833 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3834 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3835 if ( mData->mMachineState == MachineState_Paused
3836 && strReconfig == "1")
3837 fSilent = true;
3838
3839 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3840 bool fHotplug = false;
3841 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3842 fHotplug = true;
3843
3844 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3845 return setError(VBOX_E_INVALID_VM_STATE,
3846 tr("Controller '%s' does not support hot-plugging"),
3847 aName.c_str());
3848
3849 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3850 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3851 fHotplug = true;
3852
3853 // check that the port and device are not out of range
3854 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3855 if (FAILED(rc)) return rc;
3856
3857 /* check if the device slot is already busy */
3858 MediumAttachment *pAttachTemp;
3859 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3860 aName,
3861 aControllerPort,
3862 aDevice)))
3863 {
3864 Medium *pMedium = pAttachTemp->i_getMedium();
3865 if (pMedium)
3866 {
3867 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3868 return setError(VBOX_E_OBJECT_IN_USE,
3869 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3870 pMedium->i_getLocationFull().c_str(),
3871 aControllerPort,
3872 aDevice,
3873 aName.c_str());
3874 }
3875 else
3876 return setError(VBOX_E_OBJECT_IN_USE,
3877 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3878 aControllerPort, aDevice, aName.c_str());
3879 }
3880
3881 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3882 if (aMedium && medium.isNull())
3883 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3884
3885 AutoCaller mediumCaller(medium);
3886 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3887
3888 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3889
3890 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3891 && !medium.isNull()
3892 && ( medium->i_getType() != MediumType_Readonly
3893 || medium->i_getDeviceType() != DeviceType_DVD)
3894 )
3895 return setError(VBOX_E_OBJECT_IN_USE,
3896 tr("Medium '%s' is already attached to this virtual machine"),
3897 medium->i_getLocationFull().c_str());
3898
3899 if (!medium.isNull())
3900 {
3901 MediumType_T mtype = medium->i_getType();
3902 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3903 // For DVDs it's not written to the config file, so needs no global config
3904 // version bump. For floppies it's a new attribute "type", which is ignored
3905 // by older VirtualBox version, so needs no global config version bump either.
3906 // For hard disks this type is not accepted.
3907 if (mtype == MediumType_MultiAttach)
3908 {
3909 // This type is new with VirtualBox 4.0 and therefore requires settings
3910 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3911 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3912 // two reasons: The medium type is a property of the media registry tree, which
3913 // can reside in the global config file (for pre-4.0 media); we would therefore
3914 // possibly need to bump the global config version. We don't want to do that though
3915 // because that might make downgrading to pre-4.0 impossible.
3916 // As a result, we can only use these two new types if the medium is NOT in the
3917 // global registry:
3918 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3919 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3920 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3921 )
3922 return setError(VBOX_E_INVALID_OBJECT_STATE,
3923 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3924 "to machines that were created with VirtualBox 4.0 or later"),
3925 medium->i_getLocationFull().c_str());
3926 }
3927 }
3928
3929 bool fIndirect = false;
3930 if (!medium.isNull())
3931 fIndirect = medium->i_isReadOnly();
3932 bool associate = true;
3933
3934 do
3935 {
3936 if ( aType == DeviceType_HardDisk
3937 && mMediumAttachments.isBackedUp())
3938 {
3939 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3940
3941 /* check if the medium was attached to the VM before we started
3942 * changing attachments in which case the attachment just needs to
3943 * be restored */
3944 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3945 {
3946 AssertReturn(!fIndirect, E_FAIL);
3947
3948 /* see if it's the same bus/channel/device */
3949 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3950 {
3951 /* the simplest case: restore the whole attachment
3952 * and return, nothing else to do */
3953 mMediumAttachments->push_back(pAttachTemp);
3954
3955 /* Reattach the medium to the VM. */
3956 if (fHotplug || fSilent)
3957 {
3958 mediumLock.release();
3959 treeLock.release();
3960 alock.release();
3961
3962 MediumLockList *pMediumLockList(new MediumLockList());
3963
3964 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3965 medium /* pToLockWrite */,
3966 false /* fMediumLockWriteAll */,
3967 NULL,
3968 *pMediumLockList);
3969 alock.acquire();
3970 if (FAILED(rc))
3971 delete pMediumLockList;
3972 else
3973 {
3974 mData->mSession.mLockedMedia.Unlock();
3975 alock.release();
3976 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3977 mData->mSession.mLockedMedia.Lock();
3978 alock.acquire();
3979 }
3980 alock.release();
3981
3982 if (SUCCEEDED(rc))
3983 {
3984 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3985 /* Remove lock list in case of error. */
3986 if (FAILED(rc))
3987 {
3988 mData->mSession.mLockedMedia.Unlock();
3989 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3990 mData->mSession.mLockedMedia.Lock();
3991 }
3992 }
3993 }
3994
3995 return S_OK;
3996 }
3997
3998 /* bus/channel/device differ; we need a new attachment object,
3999 * but don't try to associate it again */
4000 associate = false;
4001 break;
4002 }
4003 }
4004
4005 /* go further only if the attachment is to be indirect */
4006 if (!fIndirect)
4007 break;
4008
4009 /* perform the so called smart attachment logic for indirect
4010 * attachments. Note that smart attachment is only applicable to base
4011 * hard disks. */
4012
4013 if (medium->i_getParent().isNull())
4014 {
4015 /* first, investigate the backup copy of the current hard disk
4016 * attachments to make it possible to re-attach existing diffs to
4017 * another device slot w/o losing their contents */
4018 if (mMediumAttachments.isBackedUp())
4019 {
4020 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4021
4022 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4023 uint32_t foundLevel = 0;
4024
4025 for (MediumAttachmentList::const_iterator
4026 it = oldAtts.begin();
4027 it != oldAtts.end();
4028 ++it)
4029 {
4030 uint32_t level = 0;
4031 MediumAttachment *pAttach = *it;
4032 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4033 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4034 if (pMedium.isNull())
4035 continue;
4036
4037 if (pMedium->i_getBase(&level) == medium)
4038 {
4039 /* skip the hard disk if its currently attached (we
4040 * cannot attach the same hard disk twice) */
4041 if (i_findAttachment(*mMediumAttachments.data(),
4042 pMedium))
4043 continue;
4044
4045 /* matched device, channel and bus (i.e. attached to the
4046 * same place) will win and immediately stop the search;
4047 * otherwise the attachment that has the youngest
4048 * descendant of medium will be used
4049 */
4050 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4051 {
4052 /* the simplest case: restore the whole attachment
4053 * and return, nothing else to do */
4054 mMediumAttachments->push_back(*it);
4055
4056 /* Reattach the medium to the VM. */
4057 if (fHotplug || fSilent)
4058 {
4059 mediumLock.release();
4060 treeLock.release();
4061 alock.release();
4062
4063 MediumLockList *pMediumLockList(new MediumLockList());
4064
4065 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4066 medium /* pToLockWrite */,
4067 false /* fMediumLockWriteAll */,
4068 NULL,
4069 *pMediumLockList);
4070 alock.acquire();
4071 if (FAILED(rc))
4072 delete pMediumLockList;
4073 else
4074 {
4075 mData->mSession.mLockedMedia.Unlock();
4076 alock.release();
4077 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4078 mData->mSession.mLockedMedia.Lock();
4079 alock.acquire();
4080 }
4081 alock.release();
4082
4083 if (SUCCEEDED(rc))
4084 {
4085 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4086 /* Remove lock list in case of error. */
4087 if (FAILED(rc))
4088 {
4089 mData->mSession.mLockedMedia.Unlock();
4090 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4091 mData->mSession.mLockedMedia.Lock();
4092 }
4093 }
4094 }
4095
4096 return S_OK;
4097 }
4098 else if ( foundIt == oldAtts.end()
4099 || level > foundLevel /* prefer younger */
4100 )
4101 {
4102 foundIt = it;
4103 foundLevel = level;
4104 }
4105 }
4106 }
4107
4108 if (foundIt != oldAtts.end())
4109 {
4110 /* use the previously attached hard disk */
4111 medium = (*foundIt)->i_getMedium();
4112 mediumCaller.attach(medium);
4113 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4114 mediumLock.attach(medium);
4115 /* not implicit, doesn't require association with this VM */
4116 fIndirect = false;
4117 associate = false;
4118 /* go right to the MediumAttachment creation */
4119 break;
4120 }
4121 }
4122
4123 /* must give up the medium lock and medium tree lock as below we
4124 * go over snapshots, which needs a lock with higher lock order. */
4125 mediumLock.release();
4126 treeLock.release();
4127
4128 /* then, search through snapshots for the best diff in the given
4129 * hard disk's chain to base the new diff on */
4130
4131 ComObjPtr<Medium> base;
4132 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4133 while (snap)
4134 {
4135 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4136
4137 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4138
4139 MediumAttachment *pAttachFound = NULL;
4140 uint32_t foundLevel = 0;
4141
4142 for (MediumAttachmentList::const_iterator
4143 it = snapAtts.begin();
4144 it != snapAtts.end();
4145 ++it)
4146 {
4147 MediumAttachment *pAttach = *it;
4148 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4149 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4150 if (pMedium.isNull())
4151 continue;
4152
4153 uint32_t level = 0;
4154 if (pMedium->i_getBase(&level) == medium)
4155 {
4156 /* matched device, channel and bus (i.e. attached to the
4157 * same place) will win and immediately stop the search;
4158 * otherwise the attachment that has the youngest
4159 * descendant of medium will be used
4160 */
4161 if ( pAttach->i_getDevice() == aDevice
4162 && pAttach->i_getPort() == aControllerPort
4163 && pAttach->i_getControllerName() == aName
4164 )
4165 {
4166 pAttachFound = pAttach;
4167 break;
4168 }
4169 else if ( !pAttachFound
4170 || level > foundLevel /* prefer younger */
4171 )
4172 {
4173 pAttachFound = pAttach;
4174 foundLevel = level;
4175 }
4176 }
4177 }
4178
4179 if (pAttachFound)
4180 {
4181 base = pAttachFound->i_getMedium();
4182 break;
4183 }
4184
4185 snap = snap->i_getParent();
4186 }
4187
4188 /* re-lock medium tree and the medium, as we need it below */
4189 treeLock.acquire();
4190 mediumLock.acquire();
4191
4192 /* found a suitable diff, use it as a base */
4193 if (!base.isNull())
4194 {
4195 medium = base;
4196 mediumCaller.attach(medium);
4197 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4198 mediumLock.attach(medium);
4199 }
4200 }
4201
4202 Utf8Str strFullSnapshotFolder;
4203 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4204
4205 ComObjPtr<Medium> diff;
4206 diff.createObject();
4207 // store this diff in the same registry as the parent
4208 Guid uuidRegistryParent;
4209 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4210 {
4211 // parent image has no registry: this can happen if we're attaching a new immutable
4212 // image that has not yet been attached (medium then points to the base and we're
4213 // creating the diff image for the immutable, and the parent is not yet registered);
4214 // put the parent in the machine registry then
4215 mediumLock.release();
4216 treeLock.release();
4217 alock.release();
4218 i_addMediumToRegistry(medium);
4219 alock.acquire();
4220 treeLock.acquire();
4221 mediumLock.acquire();
4222 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4223 }
4224 rc = diff->init(mParent,
4225 medium->i_getPreferredDiffFormat(),
4226 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4227 uuidRegistryParent,
4228 DeviceType_HardDisk);
4229 if (FAILED(rc)) return rc;
4230
4231 /* Apply the normal locking logic to the entire chain. */
4232 MediumLockList *pMediumLockList(new MediumLockList());
4233 mediumLock.release();
4234 treeLock.release();
4235 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4236 diff /* pToLockWrite */,
4237 false /* fMediumLockWriteAll */,
4238 medium,
4239 *pMediumLockList);
4240 treeLock.acquire();
4241 mediumLock.acquire();
4242 if (SUCCEEDED(rc))
4243 {
4244 mediumLock.release();
4245 treeLock.release();
4246 rc = pMediumLockList->Lock();
4247 treeLock.acquire();
4248 mediumLock.acquire();
4249 if (FAILED(rc))
4250 setError(rc,
4251 tr("Could not lock medium when creating diff '%s'"),
4252 diff->i_getLocationFull().c_str());
4253 else
4254 {
4255 /* will release the lock before the potentially lengthy
4256 * operation, so protect with the special state */
4257 MachineState_T oldState = mData->mMachineState;
4258 i_setMachineState(MachineState_SettingUp);
4259
4260 mediumLock.release();
4261 treeLock.release();
4262 alock.release();
4263
4264 rc = medium->i_createDiffStorage(diff,
4265 medium->i_getPreferredDiffVariant(),
4266 pMediumLockList,
4267 NULL /* aProgress */,
4268 true /* aWait */,
4269 false /* aNotify */);
4270
4271 alock.acquire();
4272 treeLock.acquire();
4273 mediumLock.acquire();
4274
4275 i_setMachineState(oldState);
4276 }
4277 }
4278
4279 /* Unlock the media and free the associated memory. */
4280 delete pMediumLockList;
4281
4282 if (FAILED(rc)) return rc;
4283
4284 /* use the created diff for the actual attachment */
4285 medium = diff;
4286 mediumCaller.attach(medium);
4287 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4288 mediumLock.attach(medium);
4289 }
4290 while (0);
4291
4292 ComObjPtr<MediumAttachment> attachment;
4293 attachment.createObject();
4294 rc = attachment->init(this,
4295 medium,
4296 aName,
4297 aControllerPort,
4298 aDevice,
4299 aType,
4300 fIndirect,
4301 false /* fPassthrough */,
4302 false /* fTempEject */,
4303 false /* fNonRotational */,
4304 false /* fDiscard */,
4305 fHotplug /* fHotPluggable */,
4306 Utf8Str::Empty);
4307 if (FAILED(rc)) return rc;
4308
4309 if (associate && !medium.isNull())
4310 {
4311 // as the last step, associate the medium to the VM
4312 rc = medium->i_addBackReference(mData->mUuid);
4313 // here we can fail because of Deleting, or being in process of creating a Diff
4314 if (FAILED(rc)) return rc;
4315
4316 mediumLock.release();
4317 treeLock.release();
4318 alock.release();
4319 i_addMediumToRegistry(medium);
4320 alock.acquire();
4321 treeLock.acquire();
4322 mediumLock.acquire();
4323 }
4324
4325 /* success: finally remember the attachment */
4326 i_setModified(IsModified_Storage);
4327 mMediumAttachments.backup();
4328 mMediumAttachments->push_back(attachment);
4329
4330 mediumLock.release();
4331 treeLock.release();
4332 alock.release();
4333
4334 if (fHotplug || fSilent)
4335 {
4336 if (!medium.isNull())
4337 {
4338 MediumLockList *pMediumLockList(new MediumLockList());
4339
4340 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4341 medium /* pToLockWrite */,
4342 false /* fMediumLockWriteAll */,
4343 NULL,
4344 *pMediumLockList);
4345 alock.acquire();
4346 if (FAILED(rc))
4347 delete pMediumLockList;
4348 else
4349 {
4350 mData->mSession.mLockedMedia.Unlock();
4351 alock.release();
4352 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4353 mData->mSession.mLockedMedia.Lock();
4354 alock.acquire();
4355 }
4356 alock.release();
4357 }
4358
4359 if (SUCCEEDED(rc))
4360 {
4361 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4362 /* Remove lock list in case of error. */
4363 if (FAILED(rc))
4364 {
4365 mData->mSession.mLockedMedia.Unlock();
4366 mData->mSession.mLockedMedia.Remove(attachment);
4367 mData->mSession.mLockedMedia.Lock();
4368 }
4369 }
4370 }
4371
4372 /* Save modified registries, but skip this machine as it's the caller's
4373 * job to save its settings like all other settings changes. */
4374 mParent->i_unmarkRegistryModified(i_getId());
4375 mParent->i_saveModifiedRegistries();
4376
4377 if (SUCCEEDED(rc))
4378 {
4379 if (fIndirect && medium != aM)
4380 mParent->i_onMediumConfigChanged(medium);
4381 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4382 }
4383
4384 return rc;
4385}
4386
4387HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4388 LONG aDevice)
4389{
4390 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4391 aName.c_str(), aControllerPort, aDevice));
4392
4393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4394
4395 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4396 if (FAILED(rc)) return rc;
4397
4398 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4399
4400 /* Check for an existing controller. */
4401 ComObjPtr<StorageController> ctl;
4402 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4403 if (FAILED(rc)) return rc;
4404
4405 StorageControllerType_T ctrlType;
4406 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4407 if (FAILED(rc))
4408 return setError(E_FAIL,
4409 tr("Could not get type of controller '%s'"),
4410 aName.c_str());
4411
4412 bool fSilent = false;
4413 Utf8Str strReconfig;
4414
4415 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4416 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4417 if ( mData->mMachineState == MachineState_Paused
4418 && strReconfig == "1")
4419 fSilent = true;
4420
4421 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4422 bool fHotplug = false;
4423 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4424 fHotplug = true;
4425
4426 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4427 return setError(VBOX_E_INVALID_VM_STATE,
4428 tr("Controller '%s' does not support hot-plugging"),
4429 aName.c_str());
4430
4431 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4432 aName,
4433 aControllerPort,
4434 aDevice);
4435 if (!pAttach)
4436 return setError(VBOX_E_OBJECT_NOT_FOUND,
4437 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4438 aDevice, aControllerPort, aName.c_str());
4439
4440 if (fHotplug && !pAttach->i_getHotPluggable())
4441 return setError(VBOX_E_NOT_SUPPORTED,
4442 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4443 aDevice, aControllerPort, aName.c_str());
4444
4445 /*
4446 * The VM has to detach the device before we delete any implicit diffs.
4447 * If this fails we can roll back without loosing data.
4448 */
4449 if (fHotplug || fSilent)
4450 {
4451 alock.release();
4452 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4453 alock.acquire();
4454 }
4455 if (FAILED(rc)) return rc;
4456
4457 /* If we are here everything went well and we can delete the implicit now. */
4458 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4459
4460 alock.release();
4461
4462 /* Save modified registries, but skip this machine as it's the caller's
4463 * job to save its settings like all other settings changes. */
4464 mParent->i_unmarkRegistryModified(i_getId());
4465 mParent->i_saveModifiedRegistries();
4466
4467 if (SUCCEEDED(rc))
4468 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4469
4470 return rc;
4471}
4472
4473HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4474 LONG aDevice, BOOL aPassthrough)
4475{
4476 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4477 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4478
4479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4480
4481 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4482 if (FAILED(rc)) return rc;
4483
4484 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4485
4486 /* Check for an existing controller. */
4487 ComObjPtr<StorageController> ctl;
4488 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4489 if (FAILED(rc)) return rc;
4490
4491 StorageControllerType_T ctrlType;
4492 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4493 if (FAILED(rc))
4494 return setError(E_FAIL,
4495 tr("Could not get type of controller '%s'"),
4496 aName.c_str());
4497
4498 bool fSilent = false;
4499 Utf8Str strReconfig;
4500
4501 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4502 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4503 if ( mData->mMachineState == MachineState_Paused
4504 && strReconfig == "1")
4505 fSilent = true;
4506
4507 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4508 bool fHotplug = false;
4509 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4510 fHotplug = true;
4511
4512 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4513 return setError(VBOX_E_INVALID_VM_STATE,
4514 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4515 aName.c_str());
4516
4517 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4518 aName,
4519 aControllerPort,
4520 aDevice);
4521 if (!pAttach)
4522 return setError(VBOX_E_OBJECT_NOT_FOUND,
4523 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4524 aDevice, aControllerPort, aName.c_str());
4525
4526
4527 i_setModified(IsModified_Storage);
4528 mMediumAttachments.backup();
4529
4530 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4531
4532 if (pAttach->i_getType() != DeviceType_DVD)
4533 return setError(E_INVALIDARG,
4534 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4535 aDevice, aControllerPort, aName.c_str());
4536
4537 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4538
4539 pAttach->i_updatePassthrough(!!aPassthrough);
4540
4541 attLock.release();
4542 alock.release();
4543 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4544 if (SUCCEEDED(rc) && fValueChanged)
4545 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4546
4547 return rc;
4548}
4549
4550HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4551 LONG aDevice, BOOL aTemporaryEject)
4552{
4553
4554 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4555 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4556
4557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4558
4559 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4560 if (FAILED(rc)) return rc;
4561
4562 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4563 aName,
4564 aControllerPort,
4565 aDevice);
4566 if (!pAttach)
4567 return setError(VBOX_E_OBJECT_NOT_FOUND,
4568 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4569 aDevice, aControllerPort, aName.c_str());
4570
4571
4572 i_setModified(IsModified_Storage);
4573 mMediumAttachments.backup();
4574
4575 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4576
4577 if (pAttach->i_getType() != DeviceType_DVD)
4578 return setError(E_INVALIDARG,
4579 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4580 aDevice, aControllerPort, aName.c_str());
4581 pAttach->i_updateTempEject(!!aTemporaryEject);
4582
4583 return S_OK;
4584}
4585
4586HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4587 LONG aDevice, BOOL aNonRotational)
4588{
4589
4590 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4591 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4592
4593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4594
4595 HRESULT rc = i_checkStateDependency(MutableStateDep);
4596 if (FAILED(rc)) return rc;
4597
4598 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4599
4600 if (Global::IsOnlineOrTransient(mData->mMachineState))
4601 return setError(VBOX_E_INVALID_VM_STATE,
4602 tr("Invalid machine state: %s"),
4603 Global::stringifyMachineState(mData->mMachineState));
4604
4605 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4606 aName,
4607 aControllerPort,
4608 aDevice);
4609 if (!pAttach)
4610 return setError(VBOX_E_OBJECT_NOT_FOUND,
4611 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4612 aDevice, aControllerPort, aName.c_str());
4613
4614
4615 i_setModified(IsModified_Storage);
4616 mMediumAttachments.backup();
4617
4618 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4619
4620 if (pAttach->i_getType() != DeviceType_HardDisk)
4621 return setError(E_INVALIDARG,
4622 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"),
4623 aDevice, aControllerPort, aName.c_str());
4624 pAttach->i_updateNonRotational(!!aNonRotational);
4625
4626 return S_OK;
4627}
4628
4629HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4630 LONG aDevice, BOOL aDiscard)
4631{
4632
4633 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4634 aName.c_str(), aControllerPort, aDevice, aDiscard));
4635
4636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4637
4638 HRESULT rc = i_checkStateDependency(MutableStateDep);
4639 if (FAILED(rc)) return rc;
4640
4641 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4642
4643 if (Global::IsOnlineOrTransient(mData->mMachineState))
4644 return setError(VBOX_E_INVALID_VM_STATE,
4645 tr("Invalid machine state: %s"),
4646 Global::stringifyMachineState(mData->mMachineState));
4647
4648 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4649 aName,
4650 aControllerPort,
4651 aDevice);
4652 if (!pAttach)
4653 return setError(VBOX_E_OBJECT_NOT_FOUND,
4654 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4655 aDevice, aControllerPort, aName.c_str());
4656
4657
4658 i_setModified(IsModified_Storage);
4659 mMediumAttachments.backup();
4660
4661 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4662
4663 if (pAttach->i_getType() != DeviceType_HardDisk)
4664 return setError(E_INVALIDARG,
4665 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"),
4666 aDevice, aControllerPort, aName.c_str());
4667 pAttach->i_updateDiscard(!!aDiscard);
4668
4669 return S_OK;
4670}
4671
4672HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4673 LONG aDevice, BOOL aHotPluggable)
4674{
4675 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4676 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4677
4678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4679
4680 HRESULT rc = i_checkStateDependency(MutableStateDep);
4681 if (FAILED(rc)) return rc;
4682
4683 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4684
4685 if (Global::IsOnlineOrTransient(mData->mMachineState))
4686 return setError(VBOX_E_INVALID_VM_STATE,
4687 tr("Invalid machine state: %s"),
4688 Global::stringifyMachineState(mData->mMachineState));
4689
4690 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4691 aName,
4692 aControllerPort,
4693 aDevice);
4694 if (!pAttach)
4695 return setError(VBOX_E_OBJECT_NOT_FOUND,
4696 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4697 aDevice, aControllerPort, aName.c_str());
4698
4699 /* Check for an existing controller. */
4700 ComObjPtr<StorageController> ctl;
4701 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4702 if (FAILED(rc)) return rc;
4703
4704 StorageControllerType_T ctrlType;
4705 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4706 if (FAILED(rc))
4707 return setError(E_FAIL,
4708 tr("Could not get type of controller '%s'"),
4709 aName.c_str());
4710
4711 if (!i_isControllerHotplugCapable(ctrlType))
4712 return setError(VBOX_E_NOT_SUPPORTED,
4713 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4714 aName.c_str());
4715
4716 /* silently ignore attempts to modify the hot-plug status of USB devices */
4717 if (ctrlType == StorageControllerType_USB)
4718 return S_OK;
4719
4720 i_setModified(IsModified_Storage);
4721 mMediumAttachments.backup();
4722
4723 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4724
4725 if (pAttach->i_getType() == DeviceType_Floppy)
4726 return setError(E_INVALIDARG,
4727 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"),
4728 aDevice, aControllerPort, aName.c_str());
4729 pAttach->i_updateHotPluggable(!!aHotPluggable);
4730
4731 return S_OK;
4732}
4733
4734HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4735 LONG aDevice)
4736{
4737 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4738 aName.c_str(), aControllerPort, aDevice));
4739
4740 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4741}
4742
4743HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4744 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4745{
4746 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4747 aName.c_str(), aControllerPort, aDevice));
4748
4749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4750
4751 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4752 if (FAILED(rc)) return rc;
4753
4754 if (Global::IsOnlineOrTransient(mData->mMachineState))
4755 return setError(VBOX_E_INVALID_VM_STATE,
4756 tr("Invalid machine state: %s"),
4757 Global::stringifyMachineState(mData->mMachineState));
4758
4759 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4760 aName,
4761 aControllerPort,
4762 aDevice);
4763 if (!pAttach)
4764 return setError(VBOX_E_OBJECT_NOT_FOUND,
4765 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4766 aDevice, aControllerPort, aName.c_str());
4767
4768
4769 i_setModified(IsModified_Storage);
4770 mMediumAttachments.backup();
4771
4772 IBandwidthGroup *iB = aBandwidthGroup;
4773 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4774 if (aBandwidthGroup && group.isNull())
4775 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4776
4777 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4778
4779 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4780 if (strBandwidthGroupOld.isNotEmpty())
4781 {
4782 /* Get the bandwidth group object and release it - this must not fail. */
4783 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4784 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4785 Assert(SUCCEEDED(rc));
4786
4787 pBandwidthGroupOld->i_release();
4788 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4789 }
4790
4791 if (!group.isNull())
4792 {
4793 group->i_reference();
4794 pAttach->i_updateBandwidthGroup(group->i_getName());
4795 }
4796
4797 return S_OK;
4798}
4799
4800HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4801 LONG aControllerPort,
4802 LONG aDevice,
4803 DeviceType_T aType)
4804{
4805 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4806 aName.c_str(), aControllerPort, aDevice, aType));
4807
4808 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4809}
4810
4811
4812HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4813 LONG aControllerPort,
4814 LONG aDevice,
4815 BOOL aForce)
4816{
4817 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4818 aName.c_str(), aControllerPort, aForce));
4819
4820 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4821}
4822
4823HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4824 LONG aControllerPort,
4825 LONG aDevice,
4826 const ComPtr<IMedium> &aMedium,
4827 BOOL aForce)
4828{
4829 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4830 aName.c_str(), aControllerPort, aDevice, aForce));
4831
4832 // request the host lock first, since might be calling Host methods for getting host drives;
4833 // next, protect the media tree all the while we're in here, as well as our member variables
4834 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4835 this->lockHandle(),
4836 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4837
4838 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4839 if (FAILED(hrc)) return hrc;
4840
4841 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4842 aName,
4843 aControllerPort,
4844 aDevice);
4845 if (pAttach.isNull())
4846 return setError(VBOX_E_OBJECT_NOT_FOUND,
4847 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4848 aDevice, aControllerPort, aName.c_str());
4849
4850 /* Remember previously mounted medium. The medium before taking the
4851 * backup is not necessarily the same thing. */
4852 ComObjPtr<Medium> oldmedium;
4853 oldmedium = pAttach->i_getMedium();
4854
4855 IMedium *iM = aMedium;
4856 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4857 if (aMedium && pMedium.isNull())
4858 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4859
4860 AutoCaller mediumCaller(pMedium);
4861 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4862
4863 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4864 if (pMedium)
4865 {
4866 DeviceType_T mediumType = pAttach->i_getType();
4867 switch (mediumType)
4868 {
4869 case DeviceType_DVD:
4870 case DeviceType_Floppy:
4871 break;
4872
4873 default:
4874 return setError(VBOX_E_INVALID_OBJECT_STATE,
4875 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4876 aControllerPort,
4877 aDevice,
4878 aName.c_str());
4879 }
4880 }
4881
4882 i_setModified(IsModified_Storage);
4883 mMediumAttachments.backup();
4884
4885 {
4886 // The backup operation makes the pAttach reference point to the
4887 // old settings. Re-get the correct reference.
4888 pAttach = i_findAttachment(*mMediumAttachments.data(),
4889 aName,
4890 aControllerPort,
4891 aDevice);
4892 if (!oldmedium.isNull())
4893 oldmedium->i_removeBackReference(mData->mUuid);
4894 if (!pMedium.isNull())
4895 {
4896 pMedium->i_addBackReference(mData->mUuid);
4897
4898 mediumLock.release();
4899 multiLock.release();
4900 i_addMediumToRegistry(pMedium);
4901 multiLock.acquire();
4902 mediumLock.acquire();
4903 }
4904
4905 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4906 pAttach->i_updateMedium(pMedium);
4907 }
4908
4909 i_setModified(IsModified_Storage);
4910
4911 mediumLock.release();
4912 multiLock.release();
4913 HRESULT rc = i_onMediumChange(pAttach, aForce);
4914 multiLock.acquire();
4915 mediumLock.acquire();
4916
4917 /* On error roll back this change only. */
4918 if (FAILED(rc))
4919 {
4920 if (!pMedium.isNull())
4921 pMedium->i_removeBackReference(mData->mUuid);
4922 pAttach = i_findAttachment(*mMediumAttachments.data(),
4923 aName,
4924 aControllerPort,
4925 aDevice);
4926 /* If the attachment is gone in the meantime, bail out. */
4927 if (pAttach.isNull())
4928 return rc;
4929 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4930 if (!oldmedium.isNull())
4931 oldmedium->i_addBackReference(mData->mUuid);
4932 pAttach->i_updateMedium(oldmedium);
4933 }
4934
4935 mediumLock.release();
4936 multiLock.release();
4937
4938 /* Save modified registries, but skip this machine as it's the caller's
4939 * job to save its settings like all other settings changes. */
4940 mParent->i_unmarkRegistryModified(i_getId());
4941 mParent->i_saveModifiedRegistries();
4942
4943 return rc;
4944}
4945HRESULT Machine::getMedium(const com::Utf8Str &aName,
4946 LONG aControllerPort,
4947 LONG aDevice,
4948 ComPtr<IMedium> &aMedium)
4949{
4950 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4951 aName.c_str(), aControllerPort, aDevice));
4952
4953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4954
4955 aMedium = NULL;
4956
4957 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4958 aName,
4959 aControllerPort,
4960 aDevice);
4961 if (pAttach.isNull())
4962 return setError(VBOX_E_OBJECT_NOT_FOUND,
4963 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4964 aDevice, aControllerPort, aName.c_str());
4965
4966 aMedium = pAttach->i_getMedium();
4967
4968 return S_OK;
4969}
4970
4971HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4972{
4973 if (aSlot < RT_ELEMENTS(mSerialPorts))
4974 {
4975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4976 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4977 return S_OK;
4978 }
4979 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4980}
4981
4982HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4983{
4984 if (aSlot < RT_ELEMENTS(mParallelPorts))
4985 {
4986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4987 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4988 return S_OK;
4989 }
4990 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4991}
4992
4993
4994HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4995{
4996 /* Do not assert if slot is out of range, just return the advertised
4997 status. testdriver/vbox.py triggers this in logVmInfo. */
4998 if (aSlot >= mNetworkAdapters.size())
4999 return setError(E_INVALIDARG,
5000 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5001 aSlot, mNetworkAdapters.size());
5002
5003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5004
5005 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5006
5007 return S_OK;
5008}
5009
5010HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5011{
5012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5013
5014 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5015 size_t i = 0;
5016 for (settings::StringsMap::const_iterator
5017 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5018 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5019 ++it, ++i)
5020 aKeys[i] = it->first;
5021
5022 return S_OK;
5023}
5024
5025 /**
5026 * @note Locks this object for reading.
5027 */
5028HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5029 com::Utf8Str &aValue)
5030{
5031 /* start with nothing found */
5032 aValue = "";
5033
5034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5037 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5038 // found:
5039 aValue = it->second; // source is a Utf8Str
5040
5041 /* return the result to caller (may be empty) */
5042 return S_OK;
5043}
5044
5045 /**
5046 * @note Locks mParent for writing + this object for writing.
5047 */
5048HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5049{
5050 /* Because control characters in aKey have caused problems in the settings
5051 * they are rejected unless the key should be deleted. */
5052 if (!aValue.isEmpty())
5053 {
5054 for (size_t i = 0; i < aKey.length(); ++i)
5055 {
5056 char ch = aKey[i];
5057 if (RTLocCIsCntrl(ch))
5058 return E_INVALIDARG;
5059 }
5060 }
5061
5062 Utf8Str strOldValue; // empty
5063
5064 // locking note: we only hold the read lock briefly to look up the old value,
5065 // then release it and call the onExtraCanChange callbacks. There is a small
5066 // chance of a race insofar as the callback might be called twice if two callers
5067 // change the same key at the same time, but that's a much better solution
5068 // than the deadlock we had here before. The actual changing of the extradata
5069 // is then performed under the write lock and race-free.
5070
5071 // look up the old value first; if nothing has changed then we need not do anything
5072 {
5073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5074
5075 // For snapshots don't even think about allowing changes, extradata
5076 // is global for a machine, so there is nothing snapshot specific.
5077 if (i_isSnapshotMachine())
5078 return setError(VBOX_E_INVALID_VM_STATE,
5079 tr("Cannot set extradata for a snapshot"));
5080
5081 // check if the right IMachine instance is used
5082 if (mData->mRegistered && !i_isSessionMachine())
5083 return setError(VBOX_E_INVALID_VM_STATE,
5084 tr("Cannot set extradata for an immutable machine"));
5085
5086 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5087 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5088 strOldValue = it->second;
5089 }
5090
5091 bool fChanged;
5092 if ((fChanged = (strOldValue != aValue)))
5093 {
5094 // ask for permission from all listeners outside the locks;
5095 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5096 // lock to copy the list of callbacks to invoke
5097 Bstr bstrError;
5098 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5099 {
5100 const char *sep = bstrError.isEmpty() ? "" : ": ";
5101 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5102 return setError(E_ACCESSDENIED,
5103 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5104 aKey.c_str(),
5105 aValue.c_str(),
5106 sep,
5107 bstrError.raw());
5108 }
5109
5110 // data is changing and change not vetoed: then write it out under the lock
5111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5112
5113 if (aValue.isEmpty())
5114 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5115 else
5116 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5117 // creates a new key if needed
5118
5119 bool fNeedsGlobalSaveSettings = false;
5120 // This saving of settings is tricky: there is no "old state" for the
5121 // extradata items at all (unlike all other settings), so the old/new
5122 // settings comparison would give a wrong result!
5123 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5124
5125 if (fNeedsGlobalSaveSettings)
5126 {
5127 // save the global settings; for that we should hold only the VirtualBox lock
5128 alock.release();
5129 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5130 mParent->i_saveSettings();
5131 }
5132 }
5133
5134 // fire notification outside the lock
5135 if (fChanged)
5136 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5137
5138 return S_OK;
5139}
5140
5141HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5142{
5143 aProgress = NULL;
5144 NOREF(aSettingsFilePath);
5145 ReturnComNotImplemented();
5146}
5147
5148HRESULT Machine::saveSettings()
5149{
5150 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5151
5152 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5153 if (FAILED(rc)) return rc;
5154
5155 /* the settings file path may never be null */
5156 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5157
5158 /* save all VM data excluding snapshots */
5159 bool fNeedsGlobalSaveSettings = false;
5160 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5161 mlock.release();
5162
5163 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5164 {
5165 // save the global settings; for that we should hold only the VirtualBox lock
5166 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5167 rc = mParent->i_saveSettings();
5168 }
5169
5170 return rc;
5171}
5172
5173
5174HRESULT Machine::discardSettings()
5175{
5176 /*
5177 * We need to take the machine list lock here as well as the machine one
5178 * or we'll get into trouble should any media stuff require rolling back.
5179 *
5180 * Details:
5181 *
5182 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5183 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5184 * 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]
5185 * 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
5186 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5187 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5188 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5189 * 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
5190 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5191 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5192 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5193 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5194 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5195 * 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]
5196 * 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] (*)
5197 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5198 * 0:005> k
5199 * # Child-SP RetAddr Call Site
5200 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5201 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5202 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5203 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5204 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5205 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5206 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5207 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5208 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5209 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5210 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5211 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5212 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5213 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5214 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5215 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5216 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5217 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5218 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5219 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5220 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5221 *
5222 */
5223 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5225
5226 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5227 if (FAILED(rc)) return rc;
5228
5229 /*
5230 * during this rollback, the session will be notified if data has
5231 * been actually changed
5232 */
5233 i_rollback(true /* aNotify */);
5234
5235 return S_OK;
5236}
5237
5238/** @note Locks objects! */
5239HRESULT Machine::unregister(AutoCaller &autoCaller,
5240 CleanupMode_T aCleanupMode,
5241 std::vector<ComPtr<IMedium> > &aMedia)
5242{
5243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5244
5245 Guid id(i_getId());
5246
5247 if (mData->mSession.mState != SessionState_Unlocked)
5248 return setError(VBOX_E_INVALID_OBJECT_STATE,
5249 tr("Cannot unregister the machine '%s' while it is locked"),
5250 mUserData->s.strName.c_str());
5251
5252 // wait for state dependents to drop to zero
5253 i_ensureNoStateDependencies(alock);
5254
5255 if (!mData->mAccessible)
5256 {
5257 // inaccessible machines can only be unregistered; uninitialize ourselves
5258 // here because currently there may be no unregistered that are inaccessible
5259 // (this state combination is not supported). Note releasing the caller and
5260 // leaving the lock before calling uninit()
5261 alock.release();
5262 autoCaller.release();
5263
5264 uninit();
5265
5266 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5267 // calls VirtualBox::i_saveSettings()
5268
5269 return S_OK;
5270 }
5271
5272 HRESULT rc = S_OK;
5273 mData->llFilesToDelete.clear();
5274
5275 if (!mSSData->strStateFilePath.isEmpty())
5276 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5277
5278 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5279 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5280 mData->llFilesToDelete.push_back(strNVRAMFile);
5281
5282 // This list collects the medium objects from all medium attachments
5283 // which we will detach from the machine and its snapshots, in a specific
5284 // order which allows for closing all media without getting "media in use"
5285 // errors, simply by going through the list from the front to the back:
5286 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5287 // and must be closed before the parent media from the snapshots, or closing the parents
5288 // will fail because they still have children);
5289 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5290 // the root ("first") snapshot of the machine.
5291 MediaList llMedia;
5292
5293 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5294 && mMediumAttachments->size()
5295 )
5296 {
5297 // we have media attachments: detach them all and add the Medium objects to our list
5298 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5299 }
5300
5301 if (mData->mFirstSnapshot)
5302 {
5303 // add the media from the medium attachments of the snapshots to
5304 // llMedia as well, after the "main" machine media;
5305 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5306 // snapshot machine, depth first.
5307
5308 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5309 MachineState_T oldState = mData->mMachineState;
5310 mData->mMachineState = MachineState_DeletingSnapshot;
5311
5312 // make a copy of the first snapshot reference so the refcount does not
5313 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5314 // (would hang due to the AutoCaller voodoo)
5315 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5316
5317 // GO!
5318 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5319
5320 mData->mMachineState = oldState;
5321 }
5322
5323 if (FAILED(rc))
5324 {
5325 i_rollbackMedia();
5326 return rc;
5327 }
5328
5329 // commit all the media changes made above
5330 i_commitMedia();
5331
5332 mData->mRegistered = false;
5333
5334 // machine lock no longer needed
5335 alock.release();
5336
5337 /* Make sure that the settings of the current VM are not saved, because
5338 * they are rather crippled at this point to meet the cleanup expectations
5339 * and there's no point destroying the VM config on disk just because. */
5340 mParent->i_unmarkRegistryModified(id);
5341
5342 // return media to caller
5343 aMedia.resize(llMedia.size());
5344 size_t i = 0;
5345 for (MediaList::const_iterator
5346 it = llMedia.begin();
5347 it != llMedia.end();
5348 ++it, ++i)
5349 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5350
5351 mParent->i_unregisterMachine(this, aCleanupMode, id);
5352 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5353
5354 autoCaller.release();
5355 uninit();
5356
5357 return S_OK;
5358}
5359
5360/**
5361 * Task record for deleting a machine config.
5362 */
5363class Machine::DeleteConfigTask
5364 : public Machine::Task
5365{
5366public:
5367 DeleteConfigTask(Machine *m,
5368 Progress *p,
5369 const Utf8Str &t,
5370 const RTCList<ComPtr<IMedium> > &llMediums,
5371 const StringsList &llFilesToDelete)
5372 : Task(m, p, t),
5373 m_llMediums(llMediums),
5374 m_llFilesToDelete(llFilesToDelete)
5375 {}
5376
5377private:
5378 void handler()
5379 {
5380 try
5381 {
5382 m_pMachine->i_deleteConfigHandler(*this);
5383 }
5384 catch (...)
5385 {
5386 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5387 }
5388 }
5389
5390 RTCList<ComPtr<IMedium> > m_llMediums;
5391 StringsList m_llFilesToDelete;
5392
5393 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5394};
5395
5396/**
5397 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5398 * SessionMachine::taskHandler().
5399 *
5400 * @note Locks this object for writing.
5401 *
5402 * @param task
5403 * @return
5404 */
5405void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5406{
5407 LogFlowThisFuncEnter();
5408
5409 AutoCaller autoCaller(this);
5410 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5411 if (FAILED(autoCaller.rc()))
5412 {
5413 /* we might have been uninitialized because the session was accidentally
5414 * closed by the client, so don't assert */
5415 HRESULT rc = setError(E_FAIL,
5416 tr("The session has been accidentally closed"));
5417 task.m_pProgress->i_notifyComplete(rc);
5418 LogFlowThisFuncLeave();
5419 return;
5420 }
5421
5422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5423
5424 HRESULT rc = S_OK;
5425
5426 try
5427 {
5428 ULONG uLogHistoryCount = 3;
5429 ComPtr<ISystemProperties> systemProperties;
5430 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5431 if (FAILED(rc)) throw rc;
5432
5433 if (!systemProperties.isNull())
5434 {
5435 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5436 if (FAILED(rc)) throw rc;
5437 }
5438
5439 MachineState_T oldState = mData->mMachineState;
5440 i_setMachineState(MachineState_SettingUp);
5441 alock.release();
5442 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5443 {
5444 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5445 {
5446 AutoCaller mac(pMedium);
5447 if (FAILED(mac.rc())) throw mac.rc();
5448 Utf8Str strLocation = pMedium->i_getLocationFull();
5449 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5450 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5451 if (FAILED(rc)) throw rc;
5452 }
5453 if (pMedium->i_isMediumFormatFile())
5454 {
5455 ComPtr<IProgress> pProgress2;
5456 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5457 if (FAILED(rc)) throw rc;
5458 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5459 if (FAILED(rc)) throw rc;
5460 }
5461
5462 /* Close the medium, deliberately without checking the return
5463 * code, and without leaving any trace in the error info, as
5464 * a failure here is a very minor issue, which shouldn't happen
5465 * as above we even managed to delete the medium. */
5466 {
5467 ErrorInfoKeeper eik;
5468 pMedium->Close();
5469 }
5470 }
5471 i_setMachineState(oldState);
5472 alock.acquire();
5473
5474 // delete the files pushed on the task list by Machine::Delete()
5475 // (this includes saved states of the machine and snapshots and
5476 // medium storage files from the IMedium list passed in, and the
5477 // machine XML file)
5478 for (StringsList::const_iterator
5479 it = task.m_llFilesToDelete.begin();
5480 it != task.m_llFilesToDelete.end();
5481 ++it)
5482 {
5483 const Utf8Str &strFile = *it;
5484 LogFunc(("Deleting file %s\n", strFile.c_str()));
5485 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5486 if (FAILED(rc)) throw rc;
5487
5488 int vrc = RTFileDelete(strFile.c_str());
5489 if (RT_FAILURE(vrc))
5490 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5491 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5492 }
5493
5494 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5495 if (FAILED(rc)) throw rc;
5496
5497 /* delete the settings only when the file actually exists */
5498 if (mData->pMachineConfigFile->fileExists())
5499 {
5500 /* Delete any backup or uncommitted XML files. Ignore failures.
5501 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5502 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5503 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5504 RTFileDelete(otherXml.c_str());
5505 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5506 RTFileDelete(otherXml.c_str());
5507
5508 /* delete the Logs folder, nothing important should be left
5509 * there (we don't check for errors because the user might have
5510 * some private files there that we don't want to delete) */
5511 Utf8Str logFolder;
5512 getLogFolder(logFolder);
5513 Assert(logFolder.length());
5514 if (RTDirExists(logFolder.c_str()))
5515 {
5516 /* Delete all VBox.log[.N] files from the Logs folder
5517 * (this must be in sync with the rotation logic in
5518 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5519 * files that may have been created by the GUI. */
5520 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5521 RTFileDelete(log.c_str());
5522 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5523 RTFileDelete(log.c_str());
5524 for (ULONG i = uLogHistoryCount; i > 0; i--)
5525 {
5526 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5527 RTFileDelete(log.c_str());
5528 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5529 RTFileDelete(log.c_str());
5530 }
5531 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5532 RTFileDelete(log.c_str());
5533#if defined(RT_OS_WINDOWS)
5534 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5535 RTFileDelete(log.c_str());
5536 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5537 RTFileDelete(log.c_str());
5538#endif
5539
5540 RTDirRemove(logFolder.c_str());
5541 }
5542
5543 /* delete the Snapshots folder, nothing important should be left
5544 * there (we don't check for errors because the user might have
5545 * some private files there that we don't want to delete) */
5546 Utf8Str strFullSnapshotFolder;
5547 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5548 Assert(!strFullSnapshotFolder.isEmpty());
5549 if (RTDirExists(strFullSnapshotFolder.c_str()))
5550 RTDirRemove(strFullSnapshotFolder.c_str());
5551
5552 // delete the directory that contains the settings file, but only
5553 // if it matches the VM name
5554 Utf8Str settingsDir;
5555 if (i_isInOwnDir(&settingsDir))
5556 RTDirRemove(settingsDir.c_str());
5557 }
5558
5559 alock.release();
5560
5561 mParent->i_saveModifiedRegistries();
5562 }
5563 catch (HRESULT aRC) { rc = aRC; }
5564
5565 task.m_pProgress->i_notifyComplete(rc);
5566
5567 LogFlowThisFuncLeave();
5568}
5569
5570HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5571{
5572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5573
5574 HRESULT rc = i_checkStateDependency(MutableStateDep);
5575 if (FAILED(rc)) return rc;
5576
5577 if (mData->mRegistered)
5578 return setError(VBOX_E_INVALID_VM_STATE,
5579 tr("Cannot delete settings of a registered machine"));
5580
5581 // collect files to delete
5582 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5583 // machine config file
5584 if (mData->pMachineConfigFile->fileExists())
5585 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5586 // backup of machine config file
5587 Utf8Str strTmp(mData->m_strConfigFileFull);
5588 strTmp.append("-prev");
5589 if (RTFileExists(strTmp.c_str()))
5590 llFilesToDelete.push_back(strTmp);
5591
5592 RTCList<ComPtr<IMedium> > llMediums;
5593 for (size_t i = 0; i < aMedia.size(); ++i)
5594 {
5595 IMedium *pIMedium(aMedia[i]);
5596 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5597 if (pMedium.isNull())
5598 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5599 SafeArray<BSTR> ids;
5600 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5601 if (FAILED(rc)) return rc;
5602 /* At this point the medium should not have any back references
5603 * anymore. If it has it is attached to another VM and *must* not
5604 * deleted. */
5605 if (ids.size() < 1)
5606 llMediums.append(pMedium);
5607 }
5608
5609 ComObjPtr<Progress> pProgress;
5610 pProgress.createObject();
5611 rc = pProgress->init(i_getVirtualBox(),
5612 static_cast<IMachine*>(this) /* aInitiator */,
5613 tr("Deleting files"),
5614 true /* fCancellable */,
5615 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5616 tr("Collecting file inventory"));
5617 if (FAILED(rc))
5618 return rc;
5619
5620 /* create and start the task on a separate thread (note that it will not
5621 * start working until we release alock) */
5622 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5623 rc = pTask->createThread();
5624 pTask = NULL;
5625 if (FAILED(rc))
5626 return rc;
5627
5628 pProgress.queryInterfaceTo(aProgress.asOutParam());
5629
5630 LogFlowFuncLeave();
5631
5632 return S_OK;
5633}
5634
5635HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5636{
5637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5638
5639 ComObjPtr<Snapshot> pSnapshot;
5640 HRESULT rc;
5641
5642 if (aNameOrId.isEmpty())
5643 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5644 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5645 else
5646 {
5647 Guid uuid(aNameOrId);
5648 if (uuid.isValid())
5649 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5650 else
5651 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5652 }
5653 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5654
5655 return rc;
5656}
5657
5658HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5659 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5660{
5661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5662
5663 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5664 if (FAILED(rc)) return rc;
5665
5666 ComObjPtr<SharedFolder> sharedFolder;
5667 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5668 if (SUCCEEDED(rc))
5669 return setError(VBOX_E_OBJECT_IN_USE,
5670 tr("Shared folder named '%s' already exists"),
5671 aName.c_str());
5672
5673 sharedFolder.createObject();
5674 rc = sharedFolder->init(i_getMachine(),
5675 aName,
5676 aHostPath,
5677 !!aWritable,
5678 !!aAutomount,
5679 aAutoMountPoint,
5680 true /* fFailOnError */);
5681 if (FAILED(rc)) return rc;
5682
5683 i_setModified(IsModified_SharedFolders);
5684 mHWData.backup();
5685 mHWData->mSharedFolders.push_back(sharedFolder);
5686
5687 /* inform the direct session if any */
5688 alock.release();
5689 i_onSharedFolderChange();
5690
5691 return S_OK;
5692}
5693
5694HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5695{
5696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5697
5698 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5699 if (FAILED(rc)) return rc;
5700
5701 ComObjPtr<SharedFolder> sharedFolder;
5702 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5703 if (FAILED(rc)) return rc;
5704
5705 i_setModified(IsModified_SharedFolders);
5706 mHWData.backup();
5707 mHWData->mSharedFolders.remove(sharedFolder);
5708
5709 /* inform the direct session if any */
5710 alock.release();
5711 i_onSharedFolderChange();
5712
5713 return S_OK;
5714}
5715
5716HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5717{
5718 /* start with No */
5719 *aCanShow = FALSE;
5720
5721 ComPtr<IInternalSessionControl> directControl;
5722 {
5723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5724
5725 if (mData->mSession.mState != SessionState_Locked)
5726 return setError(VBOX_E_INVALID_VM_STATE,
5727 tr("Machine is not locked for session (session state: %s)"),
5728 Global::stringifySessionState(mData->mSession.mState));
5729
5730 if (mData->mSession.mLockType == LockType_VM)
5731 directControl = mData->mSession.mDirectControl;
5732 }
5733
5734 /* ignore calls made after #OnSessionEnd() is called */
5735 if (!directControl)
5736 return S_OK;
5737
5738 LONG64 dummy;
5739 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5740}
5741
5742HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5743{
5744 ComPtr<IInternalSessionControl> directControl;
5745 {
5746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5747
5748 if (mData->mSession.mState != SessionState_Locked)
5749 return setError(E_FAIL,
5750 tr("Machine is not locked for session (session state: %s)"),
5751 Global::stringifySessionState(mData->mSession.mState));
5752
5753 if (mData->mSession.mLockType == LockType_VM)
5754 directControl = mData->mSession.mDirectControl;
5755 }
5756
5757 /* ignore calls made after #OnSessionEnd() is called */
5758 if (!directControl)
5759 return S_OK;
5760
5761 BOOL dummy;
5762 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5763}
5764
5765#ifdef VBOX_WITH_GUEST_PROPS
5766/**
5767 * Look up a guest property in VBoxSVC's internal structures.
5768 */
5769HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5770 com::Utf8Str &aValue,
5771 LONG64 *aTimestamp,
5772 com::Utf8Str &aFlags) const
5773{
5774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5775
5776 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5777 if (it != mHWData->mGuestProperties.end())
5778 {
5779 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5780 aValue = it->second.strValue;
5781 *aTimestamp = it->second.mTimestamp;
5782 GuestPropWriteFlags(it->second.mFlags, szFlags);
5783 aFlags = Utf8Str(szFlags);
5784 }
5785
5786 return S_OK;
5787}
5788
5789/**
5790 * Query the VM that a guest property belongs to for the property.
5791 * @returns E_ACCESSDENIED if the VM process is not available or not
5792 * currently handling queries and the lookup should then be done in
5793 * VBoxSVC.
5794 */
5795HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5796 com::Utf8Str &aValue,
5797 LONG64 *aTimestamp,
5798 com::Utf8Str &aFlags) const
5799{
5800 HRESULT rc = S_OK;
5801 Bstr bstrValue;
5802 Bstr bstrFlags;
5803
5804 ComPtr<IInternalSessionControl> directControl;
5805 {
5806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5807 if (mData->mSession.mLockType == LockType_VM)
5808 directControl = mData->mSession.mDirectControl;
5809 }
5810
5811 /* ignore calls made after #OnSessionEnd() is called */
5812 if (!directControl)
5813 rc = E_ACCESSDENIED;
5814 else
5815 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5816 0 /* accessMode */,
5817 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5818
5819 aValue = bstrValue;
5820 aFlags = bstrFlags;
5821
5822 return rc;
5823}
5824#endif // VBOX_WITH_GUEST_PROPS
5825
5826HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5827 com::Utf8Str &aValue,
5828 LONG64 *aTimestamp,
5829 com::Utf8Str &aFlags)
5830{
5831#ifndef VBOX_WITH_GUEST_PROPS
5832 ReturnComNotImplemented();
5833#else // VBOX_WITH_GUEST_PROPS
5834
5835 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5836
5837 if (rc == E_ACCESSDENIED)
5838 /* The VM is not running or the service is not (yet) accessible */
5839 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5840 return rc;
5841#endif // VBOX_WITH_GUEST_PROPS
5842}
5843
5844HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5845{
5846 LONG64 dummyTimestamp;
5847 com::Utf8Str dummyFlags;
5848 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5849 return rc;
5850
5851}
5852HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5853{
5854 com::Utf8Str dummyFlags;
5855 com::Utf8Str dummyValue;
5856 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5857 return rc;
5858}
5859
5860#ifdef VBOX_WITH_GUEST_PROPS
5861/**
5862 * Set a guest property in VBoxSVC's internal structures.
5863 */
5864HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5865 const com::Utf8Str &aFlags, bool fDelete)
5866{
5867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5868 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5869 if (FAILED(rc)) return rc;
5870
5871 try
5872 {
5873 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5874 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5875 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5876
5877 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5878 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5879
5880 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5881 if (it == mHWData->mGuestProperties.end())
5882 {
5883 if (!fDelete)
5884 {
5885 i_setModified(IsModified_MachineData);
5886 mHWData.backupEx();
5887
5888 RTTIMESPEC time;
5889 HWData::GuestProperty prop;
5890 prop.strValue = aValue;
5891 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5892 prop.mFlags = fFlags;
5893 mHWData->mGuestProperties[aName] = prop;
5894 }
5895 }
5896 else
5897 {
5898 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5899 {
5900 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5901 }
5902 else
5903 {
5904 i_setModified(IsModified_MachineData);
5905 mHWData.backupEx();
5906
5907 /* The backupEx() operation invalidates our iterator,
5908 * so get a new one. */
5909 it = mHWData->mGuestProperties.find(aName);
5910 Assert(it != mHWData->mGuestProperties.end());
5911
5912 if (!fDelete)
5913 {
5914 RTTIMESPEC time;
5915 it->second.strValue = aValue;
5916 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5917 it->second.mFlags = fFlags;
5918 }
5919 else
5920 mHWData->mGuestProperties.erase(it);
5921 }
5922 }
5923
5924 if (SUCCEEDED(rc))
5925 {
5926 alock.release();
5927
5928 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5929 }
5930 }
5931 catch (std::bad_alloc &)
5932 {
5933 rc = E_OUTOFMEMORY;
5934 }
5935
5936 return rc;
5937}
5938
5939/**
5940 * Set a property on the VM that that property belongs to.
5941 * @returns E_ACCESSDENIED if the VM process is not available or not
5942 * currently handling queries and the setting should then be done in
5943 * VBoxSVC.
5944 */
5945HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5946 const com::Utf8Str &aFlags, bool fDelete)
5947{
5948 HRESULT rc;
5949
5950 try
5951 {
5952 ComPtr<IInternalSessionControl> directControl;
5953 {
5954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5955 if (mData->mSession.mLockType == LockType_VM)
5956 directControl = mData->mSession.mDirectControl;
5957 }
5958
5959 Bstr dummy1; /* will not be changed (setter) */
5960 Bstr dummy2; /* will not be changed (setter) */
5961 LONG64 dummy64;
5962 if (!directControl)
5963 rc = E_ACCESSDENIED;
5964 else
5965 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5966 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5967 fDelete ? 2 : 1 /* accessMode */,
5968 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5969 }
5970 catch (std::bad_alloc &)
5971 {
5972 rc = E_OUTOFMEMORY;
5973 }
5974
5975 return rc;
5976}
5977#endif // VBOX_WITH_GUEST_PROPS
5978
5979HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5980 const com::Utf8Str &aFlags)
5981{
5982#ifndef VBOX_WITH_GUEST_PROPS
5983 ReturnComNotImplemented();
5984#else // VBOX_WITH_GUEST_PROPS
5985 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5986 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5987
5988 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5989 if (rc == E_ACCESSDENIED)
5990 /* The VM is not running or the service is not (yet) accessible */
5991 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5992 return rc;
5993#endif // VBOX_WITH_GUEST_PROPS
5994}
5995
5996HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5997{
5998 return setGuestProperty(aProperty, aValue, "");
5999}
6000
6001HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6002{
6003#ifndef VBOX_WITH_GUEST_PROPS
6004 ReturnComNotImplemented();
6005#else // VBOX_WITH_GUEST_PROPS
6006 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6007 if (rc == E_ACCESSDENIED)
6008 /* The VM is not running or the service is not (yet) accessible */
6009 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6010 return rc;
6011#endif // VBOX_WITH_GUEST_PROPS
6012}
6013
6014#ifdef VBOX_WITH_GUEST_PROPS
6015/**
6016 * Enumerate the guest properties in VBoxSVC's internal structures.
6017 */
6018HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6019 std::vector<com::Utf8Str> &aNames,
6020 std::vector<com::Utf8Str> &aValues,
6021 std::vector<LONG64> &aTimestamps,
6022 std::vector<com::Utf8Str> &aFlags)
6023{
6024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6025 Utf8Str strPatterns(aPatterns);
6026
6027 /*
6028 * Look for matching patterns and build up a list.
6029 */
6030 HWData::GuestPropertyMap propMap;
6031 for (HWData::GuestPropertyMap::const_iterator
6032 it = mHWData->mGuestProperties.begin();
6033 it != mHWData->mGuestProperties.end();
6034 ++it)
6035 {
6036 if ( strPatterns.isEmpty()
6037 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6038 RTSTR_MAX,
6039 it->first.c_str(),
6040 RTSTR_MAX,
6041 NULL)
6042 )
6043 propMap.insert(*it);
6044 }
6045
6046 alock.release();
6047
6048 /*
6049 * And build up the arrays for returning the property information.
6050 */
6051 size_t cEntries = propMap.size();
6052
6053 aNames.resize(cEntries);
6054 aValues.resize(cEntries);
6055 aTimestamps.resize(cEntries);
6056 aFlags.resize(cEntries);
6057
6058 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6059 size_t i = 0;
6060 for (HWData::GuestPropertyMap::const_iterator
6061 it = propMap.begin();
6062 it != propMap.end();
6063 ++it, ++i)
6064 {
6065 aNames[i] = it->first;
6066 aValues[i] = it->second.strValue;
6067 aTimestamps[i] = it->second.mTimestamp;
6068 GuestPropWriteFlags(it->second.mFlags, szFlags);
6069 aFlags[i] = Utf8Str(szFlags);
6070
6071 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
6072 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
6073 }
6074
6075 return S_OK;
6076}
6077
6078/**
6079 * Enumerate the properties managed by a VM.
6080 * @returns E_ACCESSDENIED if the VM process is not available or not
6081 * currently handling queries and the setting should then be done in
6082 * VBoxSVC.
6083 */
6084HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6085 std::vector<com::Utf8Str> &aNames,
6086 std::vector<com::Utf8Str> &aValues,
6087 std::vector<LONG64> &aTimestamps,
6088 std::vector<com::Utf8Str> &aFlags)
6089{
6090 HRESULT rc;
6091 ComPtr<IInternalSessionControl> directControl;
6092 {
6093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6094 if (mData->mSession.mLockType == LockType_VM)
6095 directControl = mData->mSession.mDirectControl;
6096 }
6097
6098 com::SafeArray<BSTR> bNames;
6099 com::SafeArray<BSTR> bValues;
6100 com::SafeArray<LONG64> bTimestamps;
6101 com::SafeArray<BSTR> bFlags;
6102
6103 if (!directControl)
6104 rc = E_ACCESSDENIED;
6105 else
6106 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6107 ComSafeArrayAsOutParam(bNames),
6108 ComSafeArrayAsOutParam(bValues),
6109 ComSafeArrayAsOutParam(bTimestamps),
6110 ComSafeArrayAsOutParam(bFlags));
6111 size_t i;
6112 aNames.resize(bNames.size());
6113 for (i = 0; i < bNames.size(); ++i)
6114 aNames[i] = Utf8Str(bNames[i]);
6115 aValues.resize(bValues.size());
6116 for (i = 0; i < bValues.size(); ++i)
6117 aValues[i] = Utf8Str(bValues[i]);
6118 aTimestamps.resize(bTimestamps.size());
6119 for (i = 0; i < bTimestamps.size(); ++i)
6120 aTimestamps[i] = bTimestamps[i];
6121 aFlags.resize(bFlags.size());
6122 for (i = 0; i < bFlags.size(); ++i)
6123 aFlags[i] = Utf8Str(bFlags[i]);
6124
6125 return rc;
6126}
6127#endif // VBOX_WITH_GUEST_PROPS
6128HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6129 std::vector<com::Utf8Str> &aNames,
6130 std::vector<com::Utf8Str> &aValues,
6131 std::vector<LONG64> &aTimestamps,
6132 std::vector<com::Utf8Str> &aFlags)
6133{
6134#ifndef VBOX_WITH_GUEST_PROPS
6135 ReturnComNotImplemented();
6136#else // VBOX_WITH_GUEST_PROPS
6137
6138 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6139
6140 if (rc == E_ACCESSDENIED)
6141 /* The VM is not running or the service is not (yet) accessible */
6142 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6143 return rc;
6144#endif // VBOX_WITH_GUEST_PROPS
6145}
6146
6147HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6148 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6149{
6150 MediumAttachmentList atts;
6151
6152 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6153 if (FAILED(rc)) return rc;
6154
6155 aMediumAttachments.resize(atts.size());
6156 size_t i = 0;
6157 for (MediumAttachmentList::const_iterator
6158 it = atts.begin();
6159 it != atts.end();
6160 ++it, ++i)
6161 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6162
6163 return S_OK;
6164}
6165
6166HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6167 LONG aControllerPort,
6168 LONG aDevice,
6169 ComPtr<IMediumAttachment> &aAttachment)
6170{
6171 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6172 aName.c_str(), aControllerPort, aDevice));
6173
6174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6175
6176 aAttachment = NULL;
6177
6178 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6179 aName,
6180 aControllerPort,
6181 aDevice);
6182 if (pAttach.isNull())
6183 return setError(VBOX_E_OBJECT_NOT_FOUND,
6184 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6185 aDevice, aControllerPort, aName.c_str());
6186
6187 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6188
6189 return S_OK;
6190}
6191
6192
6193HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6194 StorageBus_T aConnectionType,
6195 ComPtr<IStorageController> &aController)
6196{
6197 if ( (aConnectionType <= StorageBus_Null)
6198 || (aConnectionType > StorageBus_VirtioSCSI))
6199 return setError(E_INVALIDARG,
6200 tr("Invalid connection type: %d"),
6201 aConnectionType);
6202
6203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6204
6205 HRESULT rc = i_checkStateDependency(MutableStateDep);
6206 if (FAILED(rc)) return rc;
6207
6208 /* try to find one with the name first. */
6209 ComObjPtr<StorageController> ctrl;
6210
6211 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6212 if (SUCCEEDED(rc))
6213 return setError(VBOX_E_OBJECT_IN_USE,
6214 tr("Storage controller named '%s' already exists"),
6215 aName.c_str());
6216
6217 ctrl.createObject();
6218
6219 /* get a new instance number for the storage controller */
6220 ULONG ulInstance = 0;
6221 bool fBootable = true;
6222 for (StorageControllerList::const_iterator
6223 it = mStorageControllers->begin();
6224 it != mStorageControllers->end();
6225 ++it)
6226 {
6227 if ((*it)->i_getStorageBus() == aConnectionType)
6228 {
6229 ULONG ulCurInst = (*it)->i_getInstance();
6230
6231 if (ulCurInst >= ulInstance)
6232 ulInstance = ulCurInst + 1;
6233
6234 /* Only one controller of each type can be marked as bootable. */
6235 if ((*it)->i_getBootable())
6236 fBootable = false;
6237 }
6238 }
6239
6240 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6241 if (FAILED(rc)) return rc;
6242
6243 i_setModified(IsModified_Storage);
6244 mStorageControllers.backup();
6245 mStorageControllers->push_back(ctrl);
6246
6247 ctrl.queryInterfaceTo(aController.asOutParam());
6248
6249 /* inform the direct session if any */
6250 alock.release();
6251 i_onStorageControllerChange(i_getId(), aName);
6252
6253 return S_OK;
6254}
6255
6256HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6257 ComPtr<IStorageController> &aStorageController)
6258{
6259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6260
6261 ComObjPtr<StorageController> ctrl;
6262
6263 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6264 if (SUCCEEDED(rc))
6265 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6266
6267 return rc;
6268}
6269
6270HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6271 ULONG aInstance,
6272 ComPtr<IStorageController> &aStorageController)
6273{
6274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6275
6276 for (StorageControllerList::const_iterator
6277 it = mStorageControllers->begin();
6278 it != mStorageControllers->end();
6279 ++it)
6280 {
6281 if ( (*it)->i_getStorageBus() == aConnectionType
6282 && (*it)->i_getInstance() == aInstance)
6283 {
6284 (*it).queryInterfaceTo(aStorageController.asOutParam());
6285 return S_OK;
6286 }
6287 }
6288
6289 return setError(VBOX_E_OBJECT_NOT_FOUND,
6290 tr("Could not find a storage controller with instance number '%lu'"),
6291 aInstance);
6292}
6293
6294HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6295{
6296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6297
6298 HRESULT rc = i_checkStateDependency(MutableStateDep);
6299 if (FAILED(rc)) return rc;
6300
6301 ComObjPtr<StorageController> ctrl;
6302
6303 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6304 if (SUCCEEDED(rc))
6305 {
6306 /* Ensure that only one controller of each type is marked as bootable. */
6307 if (aBootable == TRUE)
6308 {
6309 for (StorageControllerList::const_iterator
6310 it = mStorageControllers->begin();
6311 it != mStorageControllers->end();
6312 ++it)
6313 {
6314 ComObjPtr<StorageController> aCtrl = (*it);
6315
6316 if ( (aCtrl->i_getName() != aName)
6317 && aCtrl->i_getBootable() == TRUE
6318 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6319 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6320 {
6321 aCtrl->i_setBootable(FALSE);
6322 break;
6323 }
6324 }
6325 }
6326
6327 if (SUCCEEDED(rc))
6328 {
6329 ctrl->i_setBootable(aBootable);
6330 i_setModified(IsModified_Storage);
6331 }
6332 }
6333
6334 if (SUCCEEDED(rc))
6335 {
6336 /* inform the direct session if any */
6337 alock.release();
6338 i_onStorageControllerChange(i_getId(), aName);
6339 }
6340
6341 return rc;
6342}
6343
6344HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6345{
6346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 HRESULT rc = i_checkStateDependency(MutableStateDep);
6349 if (FAILED(rc)) return rc;
6350
6351 ComObjPtr<StorageController> ctrl;
6352 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6353 if (FAILED(rc)) return rc;
6354
6355 MediumAttachmentList llDetachedAttachments;
6356 {
6357 /* find all attached devices to the appropriate storage controller and detach them all */
6358 // make a temporary list because detachDevice invalidates iterators into
6359 // mMediumAttachments
6360 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6361
6362 for (MediumAttachmentList::const_iterator
6363 it = llAttachments2.begin();
6364 it != llAttachments2.end();
6365 ++it)
6366 {
6367 MediumAttachment *pAttachTemp = *it;
6368
6369 AutoCaller localAutoCaller(pAttachTemp);
6370 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6371
6372 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6373
6374 if (pAttachTemp->i_getControllerName() == aName)
6375 {
6376 llDetachedAttachments.push_back(pAttachTemp);
6377 rc = i_detachDevice(pAttachTemp, alock, NULL);
6378 if (FAILED(rc)) return rc;
6379 }
6380 }
6381 }
6382
6383 /* send event about detached devices before removing parent controller */
6384 for (MediumAttachmentList::const_iterator
6385 it = llDetachedAttachments.begin();
6386 it != llDetachedAttachments.end();
6387 ++it)
6388 {
6389 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6390 }
6391
6392 /* We can remove it now. */
6393 i_setModified(IsModified_Storage);
6394 mStorageControllers.backup();
6395
6396 ctrl->i_unshare();
6397
6398 mStorageControllers->remove(ctrl);
6399
6400 /* inform the direct session if any */
6401 alock.release();
6402 i_onStorageControllerChange(i_getId(), aName);
6403
6404 return S_OK;
6405}
6406
6407HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6408 ComPtr<IUSBController> &aController)
6409{
6410 if ( (aType <= USBControllerType_Null)
6411 || (aType >= USBControllerType_Last))
6412 return setError(E_INVALIDARG,
6413 tr("Invalid USB controller type: %d"),
6414 aType);
6415
6416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 HRESULT rc = i_checkStateDependency(MutableStateDep);
6419 if (FAILED(rc)) return rc;
6420
6421 /* try to find one with the same type first. */
6422 ComObjPtr<USBController> ctrl;
6423
6424 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6425 if (SUCCEEDED(rc))
6426 return setError(VBOX_E_OBJECT_IN_USE,
6427 tr("USB controller named '%s' already exists"),
6428 aName.c_str());
6429
6430 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6431 ULONG maxInstances;
6432 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6433 if (FAILED(rc))
6434 return rc;
6435
6436 ULONG cInstances = i_getUSBControllerCountByType(aType);
6437 if (cInstances >= maxInstances)
6438 return setError(E_INVALIDARG,
6439 tr("Too many USB controllers of this type"));
6440
6441 ctrl.createObject();
6442
6443 rc = ctrl->init(this, aName, aType);
6444 if (FAILED(rc)) return rc;
6445
6446 i_setModified(IsModified_USB);
6447 mUSBControllers.backup();
6448 mUSBControllers->push_back(ctrl);
6449
6450 ctrl.queryInterfaceTo(aController.asOutParam());
6451
6452 /* inform the direct session if any */
6453 alock.release();
6454 i_onUSBControllerChange();
6455
6456 return S_OK;
6457}
6458
6459HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6460{
6461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6462
6463 ComObjPtr<USBController> ctrl;
6464
6465 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6466 if (SUCCEEDED(rc))
6467 ctrl.queryInterfaceTo(aController.asOutParam());
6468
6469 return rc;
6470}
6471
6472HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6473 ULONG *aControllers)
6474{
6475 if ( (aType <= USBControllerType_Null)
6476 || (aType >= USBControllerType_Last))
6477 return setError(E_INVALIDARG,
6478 tr("Invalid USB controller type: %d"),
6479 aType);
6480
6481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6482
6483 ComObjPtr<USBController> ctrl;
6484
6485 *aControllers = i_getUSBControllerCountByType(aType);
6486
6487 return S_OK;
6488}
6489
6490HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6491{
6492
6493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6494
6495 HRESULT rc = i_checkStateDependency(MutableStateDep);
6496 if (FAILED(rc)) return rc;
6497
6498 ComObjPtr<USBController> ctrl;
6499 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6500 if (FAILED(rc)) return rc;
6501
6502 i_setModified(IsModified_USB);
6503 mUSBControllers.backup();
6504
6505 ctrl->i_unshare();
6506
6507 mUSBControllers->remove(ctrl);
6508
6509 /* inform the direct session if any */
6510 alock.release();
6511 i_onUSBControllerChange();
6512
6513 return S_OK;
6514}
6515
6516HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6517 ULONG *aOriginX,
6518 ULONG *aOriginY,
6519 ULONG *aWidth,
6520 ULONG *aHeight,
6521 BOOL *aEnabled)
6522{
6523 uint32_t u32OriginX= 0;
6524 uint32_t u32OriginY= 0;
6525 uint32_t u32Width = 0;
6526 uint32_t u32Height = 0;
6527 uint16_t u16Flags = 0;
6528
6529 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6530 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6531 if (RT_FAILURE(vrc))
6532 {
6533#ifdef RT_OS_WINDOWS
6534 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6535 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6536 * So just assign fEnable to TRUE again.
6537 * The right fix would be to change GUI API wrappers to make sure that parameters
6538 * are changed only if API succeeds.
6539 */
6540 *aEnabled = TRUE;
6541#endif
6542 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6543 tr("Saved guest size is not available (%Rrc)"),
6544 vrc);
6545 }
6546
6547 *aOriginX = u32OriginX;
6548 *aOriginY = u32OriginY;
6549 *aWidth = u32Width;
6550 *aHeight = u32Height;
6551 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6552
6553 return S_OK;
6554}
6555
6556HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6557 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6558{
6559 if (aScreenId != 0)
6560 return E_NOTIMPL;
6561
6562 if ( aBitmapFormat != BitmapFormat_BGR0
6563 && aBitmapFormat != BitmapFormat_BGRA
6564 && aBitmapFormat != BitmapFormat_RGBA
6565 && aBitmapFormat != BitmapFormat_PNG)
6566 return setError(E_NOTIMPL,
6567 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6568
6569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6570
6571 uint8_t *pu8Data = NULL;
6572 uint32_t cbData = 0;
6573 uint32_t u32Width = 0;
6574 uint32_t u32Height = 0;
6575
6576 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6577
6578 if (RT_FAILURE(vrc))
6579 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6580 tr("Saved thumbnail data is not available (%Rrc)"),
6581 vrc);
6582
6583 HRESULT hr = S_OK;
6584
6585 *aWidth = u32Width;
6586 *aHeight = u32Height;
6587
6588 if (cbData > 0)
6589 {
6590 /* Convert pixels to the format expected by the API caller. */
6591 if (aBitmapFormat == BitmapFormat_BGR0)
6592 {
6593 /* [0] B, [1] G, [2] R, [3] 0. */
6594 aData.resize(cbData);
6595 memcpy(&aData.front(), pu8Data, cbData);
6596 }
6597 else if (aBitmapFormat == BitmapFormat_BGRA)
6598 {
6599 /* [0] B, [1] G, [2] R, [3] A. */
6600 aData.resize(cbData);
6601 for (uint32_t i = 0; i < cbData; i += 4)
6602 {
6603 aData[i] = pu8Data[i];
6604 aData[i + 1] = pu8Data[i + 1];
6605 aData[i + 2] = pu8Data[i + 2];
6606 aData[i + 3] = 0xff;
6607 }
6608 }
6609 else if (aBitmapFormat == BitmapFormat_RGBA)
6610 {
6611 /* [0] R, [1] G, [2] B, [3] A. */
6612 aData.resize(cbData);
6613 for (uint32_t i = 0; i < cbData; i += 4)
6614 {
6615 aData[i] = pu8Data[i + 2];
6616 aData[i + 1] = pu8Data[i + 1];
6617 aData[i + 2] = pu8Data[i];
6618 aData[i + 3] = 0xff;
6619 }
6620 }
6621 else if (aBitmapFormat == BitmapFormat_PNG)
6622 {
6623 uint8_t *pu8PNG = NULL;
6624 uint32_t cbPNG = 0;
6625 uint32_t cxPNG = 0;
6626 uint32_t cyPNG = 0;
6627
6628 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6629
6630 if (RT_SUCCESS(vrc))
6631 {
6632 aData.resize(cbPNG);
6633 if (cbPNG)
6634 memcpy(&aData.front(), pu8PNG, cbPNG);
6635 }
6636 else
6637 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6638 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6639 vrc);
6640
6641 RTMemFree(pu8PNG);
6642 }
6643 }
6644
6645 freeSavedDisplayScreenshot(pu8Data);
6646
6647 return hr;
6648}
6649
6650HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6651 ULONG *aWidth,
6652 ULONG *aHeight,
6653 std::vector<BitmapFormat_T> &aBitmapFormats)
6654{
6655 if (aScreenId != 0)
6656 return E_NOTIMPL;
6657
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 uint8_t *pu8Data = NULL;
6661 uint32_t cbData = 0;
6662 uint32_t u32Width = 0;
6663 uint32_t u32Height = 0;
6664
6665 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6666
6667 if (RT_FAILURE(vrc))
6668 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6669 tr("Saved screenshot data is not available (%Rrc)"),
6670 vrc);
6671
6672 *aWidth = u32Width;
6673 *aHeight = u32Height;
6674 aBitmapFormats.resize(1);
6675 aBitmapFormats[0] = BitmapFormat_PNG;
6676
6677 freeSavedDisplayScreenshot(pu8Data);
6678
6679 return S_OK;
6680}
6681
6682HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6683 BitmapFormat_T aBitmapFormat,
6684 ULONG *aWidth,
6685 ULONG *aHeight,
6686 std::vector<BYTE> &aData)
6687{
6688 if (aScreenId != 0)
6689 return E_NOTIMPL;
6690
6691 if (aBitmapFormat != BitmapFormat_PNG)
6692 return E_NOTIMPL;
6693
6694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6695
6696 uint8_t *pu8Data = NULL;
6697 uint32_t cbData = 0;
6698 uint32_t u32Width = 0;
6699 uint32_t u32Height = 0;
6700
6701 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6702
6703 if (RT_FAILURE(vrc))
6704 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6705 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6706 vrc);
6707
6708 *aWidth = u32Width;
6709 *aHeight = u32Height;
6710
6711 aData.resize(cbData);
6712 if (cbData)
6713 memcpy(&aData.front(), pu8Data, cbData);
6714
6715 freeSavedDisplayScreenshot(pu8Data);
6716
6717 return S_OK;
6718}
6719
6720HRESULT Machine::hotPlugCPU(ULONG aCpu)
6721{
6722 HRESULT rc = S_OK;
6723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 if (!mHWData->mCPUHotPlugEnabled)
6726 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6727
6728 if (aCpu >= mHWData->mCPUCount)
6729 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6730
6731 if (mHWData->mCPUAttached[aCpu])
6732 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6733
6734 rc = i_checkStateDependency(MutableOrRunningStateDep);
6735 if (FAILED(rc)) return rc;
6736
6737 alock.release();
6738 rc = i_onCPUChange(aCpu, false);
6739 alock.acquire();
6740 if (FAILED(rc)) return rc;
6741
6742 i_setModified(IsModified_MachineData);
6743 mHWData.backup();
6744 mHWData->mCPUAttached[aCpu] = true;
6745
6746 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6747 if (Global::IsOnline(mData->mMachineState))
6748 i_saveSettings(NULL, alock);
6749
6750 return S_OK;
6751}
6752
6753HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6754{
6755 HRESULT rc = S_OK;
6756
6757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 if (!mHWData->mCPUHotPlugEnabled)
6760 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6761
6762 if (aCpu >= SchemaDefs::MaxCPUCount)
6763 return setError(E_INVALIDARG,
6764 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6765 SchemaDefs::MaxCPUCount);
6766
6767 if (!mHWData->mCPUAttached[aCpu])
6768 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6769
6770 /* CPU 0 can't be detached */
6771 if (aCpu == 0)
6772 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6773
6774 rc = i_checkStateDependency(MutableOrRunningStateDep);
6775 if (FAILED(rc)) return rc;
6776
6777 alock.release();
6778 rc = i_onCPUChange(aCpu, true);
6779 alock.acquire();
6780 if (FAILED(rc)) return rc;
6781
6782 i_setModified(IsModified_MachineData);
6783 mHWData.backup();
6784 mHWData->mCPUAttached[aCpu] = false;
6785
6786 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6787 if (Global::IsOnline(mData->mMachineState))
6788 i_saveSettings(NULL, alock);
6789
6790 return S_OK;
6791}
6792
6793HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6794{
6795 *aAttached = false;
6796
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 /* If hotplug is enabled the CPU is always enabled. */
6800 if (!mHWData->mCPUHotPlugEnabled)
6801 {
6802 if (aCpu < mHWData->mCPUCount)
6803 *aAttached = true;
6804 }
6805 else
6806 {
6807 if (aCpu < SchemaDefs::MaxCPUCount)
6808 *aAttached = mHWData->mCPUAttached[aCpu];
6809 }
6810
6811 return S_OK;
6812}
6813
6814HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6815{
6816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6817
6818 Utf8Str log = i_getLogFilename(aIdx);
6819 if (!RTFileExists(log.c_str()))
6820 log.setNull();
6821 aFilename = log;
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6827{
6828 if (aSize < 0)
6829 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6830
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 HRESULT rc = S_OK;
6834 Utf8Str log = i_getLogFilename(aIdx);
6835
6836 /* do not unnecessarily hold the lock while doing something which does
6837 * not need the lock and potentially takes a long time. */
6838 alock.release();
6839
6840 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6841 * keeps the SOAP reply size under 1M for the webservice (we're using
6842 * base64 encoded strings for binary data for years now, avoiding the
6843 * expansion of each byte array element to approx. 25 bytes of XML. */
6844 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6845 aData.resize(cbData);
6846
6847 RTFILE LogFile;
6848 int vrc = RTFileOpen(&LogFile, log.c_str(),
6849 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6850 if (RT_SUCCESS(vrc))
6851 {
6852 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6853 if (RT_SUCCESS(vrc))
6854 aData.resize(cbData);
6855 else
6856 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6857 tr("Could not read log file '%s' (%Rrc)"),
6858 log.c_str(), vrc);
6859 RTFileClose(LogFile);
6860 }
6861 else
6862 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6863 tr("Could not open log file '%s' (%Rrc)"),
6864 log.c_str(), vrc);
6865
6866 if (FAILED(rc))
6867 aData.resize(0);
6868
6869 return rc;
6870}
6871
6872
6873/**
6874 * Currently this method doesn't attach device to the running VM,
6875 * just makes sure it's plugged on next VM start.
6876 */
6877HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6878{
6879 // lock scope
6880 {
6881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6882
6883 HRESULT rc = i_checkStateDependency(MutableStateDep);
6884 if (FAILED(rc)) return rc;
6885
6886 ChipsetType_T aChipset = ChipsetType_PIIX3;
6887 COMGETTER(ChipsetType)(&aChipset);
6888
6889 if (aChipset != ChipsetType_ICH9)
6890 {
6891 return setError(E_INVALIDARG,
6892 tr("Host PCI attachment only supported with ICH9 chipset"));
6893 }
6894
6895 // check if device with this host PCI address already attached
6896 for (HWData::PCIDeviceAssignmentList::const_iterator
6897 it = mHWData->mPCIDeviceAssignments.begin();
6898 it != mHWData->mPCIDeviceAssignments.end();
6899 ++it)
6900 {
6901 LONG iHostAddress = -1;
6902 ComPtr<PCIDeviceAttachment> pAttach;
6903 pAttach = *it;
6904 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6905 if (iHostAddress == aHostAddress)
6906 return setError(E_INVALIDARG,
6907 tr("Device with host PCI address already attached to this VM"));
6908 }
6909
6910 ComObjPtr<PCIDeviceAttachment> pda;
6911 char name[32];
6912
6913 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6914 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6915 pda.createObject();
6916 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6917 i_setModified(IsModified_MachineData);
6918 mHWData.backup();
6919 mHWData->mPCIDeviceAssignments.push_back(pda);
6920 }
6921
6922 return S_OK;
6923}
6924
6925/**
6926 * Currently this method doesn't detach device from the running VM,
6927 * just makes sure it's not plugged on next VM start.
6928 */
6929HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6930{
6931 ComObjPtr<PCIDeviceAttachment> pAttach;
6932 bool fRemoved = false;
6933 HRESULT rc;
6934
6935 // lock scope
6936 {
6937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6938
6939 rc = i_checkStateDependency(MutableStateDep);
6940 if (FAILED(rc)) return rc;
6941
6942 for (HWData::PCIDeviceAssignmentList::const_iterator
6943 it = mHWData->mPCIDeviceAssignments.begin();
6944 it != mHWData->mPCIDeviceAssignments.end();
6945 ++it)
6946 {
6947 LONG iHostAddress = -1;
6948 pAttach = *it;
6949 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6950 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6951 {
6952 i_setModified(IsModified_MachineData);
6953 mHWData.backup();
6954 mHWData->mPCIDeviceAssignments.remove(pAttach);
6955 fRemoved = true;
6956 break;
6957 }
6958 }
6959 }
6960
6961
6962 /* Fire event outside of the lock */
6963 if (fRemoved)
6964 {
6965 Assert(!pAttach.isNull());
6966 ComPtr<IEventSource> es;
6967 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6968 Assert(SUCCEEDED(rc));
6969 Bstr mid;
6970 rc = this->COMGETTER(Id)(mid.asOutParam());
6971 Assert(SUCCEEDED(rc));
6972 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6973 }
6974
6975 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6976 tr("No host PCI device %08x attached"),
6977 aHostAddress
6978 );
6979}
6980
6981HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6982{
6983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6984
6985 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6986 size_t i = 0;
6987 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6988 it = mHWData->mPCIDeviceAssignments.begin();
6989 it != mHWData->mPCIDeviceAssignments.end();
6990 ++it, ++i)
6991 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6992
6993 return S_OK;
6994}
6995
6996HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6997{
6998 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6999
7000 return S_OK;
7001}
7002
7003HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7004{
7005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7006
7007 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7008
7009 return S_OK;
7010}
7011
7012HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7013{
7014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7015 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7016 if (SUCCEEDED(hrc))
7017 {
7018 hrc = mHWData.backupEx();
7019 if (SUCCEEDED(hrc))
7020 {
7021 i_setModified(IsModified_MachineData);
7022 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7023 }
7024 }
7025 return hrc;
7026}
7027
7028HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7029{
7030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7031 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7032 return S_OK;
7033}
7034
7035HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7036{
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7039 if (SUCCEEDED(hrc))
7040 {
7041 hrc = mHWData.backupEx();
7042 if (SUCCEEDED(hrc))
7043 {
7044 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7045 if (SUCCEEDED(hrc))
7046 i_setModified(IsModified_MachineData);
7047 }
7048 }
7049 return hrc;
7050}
7051
7052HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7053{
7054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7055
7056 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7057
7058 return S_OK;
7059}
7060
7061HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7062{
7063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7064 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7065 if (SUCCEEDED(hrc))
7066 {
7067 hrc = mHWData.backupEx();
7068 if (SUCCEEDED(hrc))
7069 {
7070 i_setModified(IsModified_MachineData);
7071 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7072 }
7073 }
7074 return hrc;
7075}
7076
7077HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7078{
7079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7080
7081 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7082
7083 return S_OK;
7084}
7085
7086HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7087{
7088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7091 if ( SUCCEEDED(hrc)
7092 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7093 {
7094 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7095 int vrc;
7096
7097 if (aAutostartEnabled)
7098 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7099 else
7100 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7101
7102 if (RT_SUCCESS(vrc))
7103 {
7104 hrc = mHWData.backupEx();
7105 if (SUCCEEDED(hrc))
7106 {
7107 i_setModified(IsModified_MachineData);
7108 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7109 }
7110 }
7111 else if (vrc == VERR_NOT_SUPPORTED)
7112 hrc = setError(VBOX_E_NOT_SUPPORTED,
7113 tr("The VM autostart feature is not supported on this platform"));
7114 else if (vrc == VERR_PATH_NOT_FOUND)
7115 hrc = setError(E_FAIL,
7116 tr("The path to the autostart database is not set"));
7117 else
7118 hrc = setError(E_UNEXPECTED,
7119 aAutostartEnabled ?
7120 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7121 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7122 mUserData->s.strName.c_str(), vrc);
7123 }
7124 return hrc;
7125}
7126
7127HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7128{
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7132
7133 return S_OK;
7134}
7135
7136HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7137{
7138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7139 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7140 if (SUCCEEDED(hrc))
7141 {
7142 hrc = mHWData.backupEx();
7143 if (SUCCEEDED(hrc))
7144 {
7145 i_setModified(IsModified_MachineData);
7146 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7147 }
7148 }
7149 return hrc;
7150}
7151
7152HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7153{
7154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7155
7156 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7157
7158 return S_OK;
7159}
7160
7161HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7162{
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7165 if ( SUCCEEDED(hrc)
7166 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7167 {
7168 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7169 int vrc;
7170
7171 if (aAutostopType != AutostopType_Disabled)
7172 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7173 else
7174 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7175
7176 if (RT_SUCCESS(vrc))
7177 {
7178 hrc = mHWData.backupEx();
7179 if (SUCCEEDED(hrc))
7180 {
7181 i_setModified(IsModified_MachineData);
7182 mHWData->mAutostart.enmAutostopType = aAutostopType;
7183 }
7184 }
7185 else if (vrc == VERR_NOT_SUPPORTED)
7186 hrc = setError(VBOX_E_NOT_SUPPORTED,
7187 tr("The VM autostop feature is not supported on this platform"));
7188 else if (vrc == VERR_PATH_NOT_FOUND)
7189 hrc = setError(E_FAIL,
7190 tr("The path to the autostart database is not set"));
7191 else
7192 hrc = setError(E_UNEXPECTED,
7193 aAutostopType != AutostopType_Disabled ?
7194 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7195 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7196 mUserData->s.strName.c_str(), vrc);
7197 }
7198 return hrc;
7199}
7200
7201HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7202{
7203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 aDefaultFrontend = mHWData->mDefaultFrontend;
7206
7207 return S_OK;
7208}
7209
7210HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7211{
7212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7213 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7214 if (SUCCEEDED(hrc))
7215 {
7216 hrc = mHWData.backupEx();
7217 if (SUCCEEDED(hrc))
7218 {
7219 i_setModified(IsModified_MachineData);
7220 mHWData->mDefaultFrontend = aDefaultFrontend;
7221 }
7222 }
7223 return hrc;
7224}
7225
7226HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7227{
7228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7229 size_t cbIcon = mUserData->s.ovIcon.size();
7230 aIcon.resize(cbIcon);
7231 if (cbIcon)
7232 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7233 return S_OK;
7234}
7235
7236HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7237{
7238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7239 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7240 if (SUCCEEDED(hrc))
7241 {
7242 i_setModified(IsModified_MachineData);
7243 mUserData.backup();
7244 size_t cbIcon = aIcon.size();
7245 mUserData->s.ovIcon.resize(cbIcon);
7246 if (cbIcon)
7247 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7248 }
7249 return hrc;
7250}
7251
7252HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7253{
7254#ifdef VBOX_WITH_USB
7255 *aUSBProxyAvailable = true;
7256#else
7257 *aUSBProxyAvailable = false;
7258#endif
7259 return S_OK;
7260}
7261
7262HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7263{
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265
7266 *aVMProcessPriority = mUserData->s.enmVMPriority;
7267
7268 return S_OK;
7269}
7270
7271HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7272{
7273 RT_NOREF(aVMProcessPriority);
7274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7275 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7276 if (SUCCEEDED(hrc))
7277 {
7278 hrc = mUserData.backupEx();
7279 if (SUCCEEDED(hrc))
7280 {
7281 i_setModified(IsModified_MachineData);
7282 mUserData->s.enmVMPriority = aVMProcessPriority;
7283 }
7284 }
7285 alock.release();
7286 if (SUCCEEDED(hrc))
7287 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7288 return hrc;
7289}
7290
7291HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7292 ComPtr<IProgress> &aProgress)
7293{
7294 ComObjPtr<Progress> pP;
7295 Progress *ppP = pP;
7296 IProgress *iP = static_cast<IProgress *>(ppP);
7297 IProgress **pProgress = &iP;
7298
7299 IMachine *pTarget = aTarget;
7300
7301 /* Convert the options. */
7302 RTCList<CloneOptions_T> optList;
7303 if (aOptions.size())
7304 for (size_t i = 0; i < aOptions.size(); ++i)
7305 optList.append(aOptions[i]);
7306
7307 if (optList.contains(CloneOptions_Link))
7308 {
7309 if (!i_isSnapshotMachine())
7310 return setError(E_INVALIDARG,
7311 tr("Linked clone can only be created from a snapshot"));
7312 if (aMode != CloneMode_MachineState)
7313 return setError(E_INVALIDARG,
7314 tr("Linked clone can only be created for a single machine state"));
7315 }
7316 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7317
7318 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7319
7320 HRESULT rc = pWorker->start(pProgress);
7321
7322 pP = static_cast<Progress *>(*pProgress);
7323 pP.queryInterfaceTo(aProgress.asOutParam());
7324
7325 return rc;
7326
7327}
7328
7329HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7330 const com::Utf8Str &aType,
7331 ComPtr<IProgress> &aProgress)
7332{
7333 LogFlowThisFuncEnter();
7334
7335 ComObjPtr<Progress> ptrProgress;
7336 HRESULT hrc = ptrProgress.createObject();
7337 if (SUCCEEDED(hrc))
7338 {
7339 com::Utf8Str strDefaultPath;
7340 if (aTargetPath.isEmpty())
7341 i_calculateFullPath(".", strDefaultPath);
7342
7343 /* Initialize our worker task */
7344 MachineMoveVM *pTask = NULL;
7345 try
7346 {
7347 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7348 }
7349 catch (std::bad_alloc &)
7350 {
7351 return E_OUTOFMEMORY;
7352 }
7353
7354 hrc = pTask->init();//no exceptions are thrown
7355
7356 if (SUCCEEDED(hrc))
7357 {
7358 hrc = pTask->createThread();
7359 pTask = NULL; /* Consumed by createThread(). */
7360 if (SUCCEEDED(hrc))
7361 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7362 else
7363 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7364 }
7365 else
7366 delete pTask;
7367 }
7368
7369 LogFlowThisFuncLeave();
7370 return hrc;
7371
7372}
7373
7374HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7375{
7376 NOREF(aProgress);
7377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7378
7379 // This check should always fail.
7380 HRESULT rc = i_checkStateDependency(MutableStateDep);
7381 if (FAILED(rc)) return rc;
7382
7383 AssertFailedReturn(E_NOTIMPL);
7384}
7385
7386HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7387{
7388 NOREF(aSavedStateFile);
7389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7390
7391 // This check should always fail.
7392 HRESULT rc = i_checkStateDependency(MutableStateDep);
7393 if (FAILED(rc)) return rc;
7394
7395 AssertFailedReturn(E_NOTIMPL);
7396}
7397
7398HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7399{
7400 NOREF(aFRemoveFile);
7401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7402
7403 // This check should always fail.
7404 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7405 if (FAILED(rc)) return rc;
7406
7407 AssertFailedReturn(E_NOTIMPL);
7408}
7409
7410// public methods for internal purposes
7411/////////////////////////////////////////////////////////////////////////////
7412
7413/**
7414 * Adds the given IsModified_* flag to the dirty flags of the machine.
7415 * This must be called either during i_loadSettings or under the machine write lock.
7416 * @param fl Flag
7417 * @param fAllowStateModification If state modifications are allowed.
7418 */
7419void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7420{
7421 mData->flModifications |= fl;
7422 if (fAllowStateModification && i_isStateModificationAllowed())
7423 mData->mCurrentStateModified = true;
7424}
7425
7426/**
7427 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7428 * care of the write locking.
7429 *
7430 * @param fModification The flag to add.
7431 * @param fAllowStateModification If state modifications are allowed.
7432 */
7433void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7434{
7435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7436 i_setModified(fModification, fAllowStateModification);
7437}
7438
7439/**
7440 * Saves the registry entry of this machine to the given configuration node.
7441 *
7442 * @param data Machine registry data.
7443 *
7444 * @note locks this object for reading.
7445 */
7446HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7447{
7448 AutoLimitedCaller autoCaller(this);
7449 AssertComRCReturnRC(autoCaller.rc());
7450
7451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 data.uuid = mData->mUuid;
7454 data.strSettingsFile = mData->m_strConfigFile;
7455
7456 return S_OK;
7457}
7458
7459/**
7460 * Calculates the absolute path of the given path taking the directory of the
7461 * machine settings file as the current directory.
7462 *
7463 * @param strPath Path to calculate the absolute path for.
7464 * @param aResult Where to put the result (used only on success, can be the
7465 * same Utf8Str instance as passed in @a aPath).
7466 * @return IPRT result.
7467 *
7468 * @note Locks this object for reading.
7469 */
7470int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7471{
7472 AutoCaller autoCaller(this);
7473 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7474
7475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7476
7477 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7478
7479 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7480
7481 strSettingsDir.stripFilename();
7482 char szFolder[RTPATH_MAX];
7483 size_t cbFolder = sizeof(szFolder);
7484 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7485 if (RT_SUCCESS(vrc))
7486 aResult = szFolder;
7487
7488 return vrc;
7489}
7490
7491/**
7492 * Copies strSource to strTarget, making it relative to the machine folder
7493 * if it is a subdirectory thereof, or simply copying it otherwise.
7494 *
7495 * @param strSource Path to evaluate and copy.
7496 * @param strTarget Buffer to receive target path.
7497 *
7498 * @note Locks this object for reading.
7499 */
7500void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7501 Utf8Str &strTarget)
7502{
7503 AutoCaller autoCaller(this);
7504 AssertComRCReturn(autoCaller.rc(), (void)0);
7505
7506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7507
7508 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7509 // use strTarget as a temporary buffer to hold the machine settings dir
7510 strTarget = mData->m_strConfigFileFull;
7511 strTarget.stripFilename();
7512 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7513 {
7514 // is relative: then append what's left
7515 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7516 // for empty paths (only possible for subdirs) use "." to avoid
7517 // triggering default settings for not present config attributes.
7518 if (strTarget.isEmpty())
7519 strTarget = ".";
7520 }
7521 else
7522 // is not relative: then overwrite
7523 strTarget = strSource;
7524}
7525
7526/**
7527 * Returns the full path to the machine's log folder in the
7528 * \a aLogFolder argument.
7529 */
7530void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7531{
7532 AutoCaller autoCaller(this);
7533 AssertComRCReturnVoid(autoCaller.rc());
7534
7535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7536
7537 char szTmp[RTPATH_MAX];
7538 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7539 if (RT_SUCCESS(vrc))
7540 {
7541 if (szTmp[0] && !mUserData.isNull())
7542 {
7543 char szTmp2[RTPATH_MAX];
7544 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7545 if (RT_SUCCESS(vrc))
7546 aLogFolder.printf("%s%c%s",
7547 szTmp2,
7548 RTPATH_DELIMITER,
7549 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7550 }
7551 else
7552 vrc = VERR_PATH_IS_RELATIVE;
7553 }
7554
7555 if (RT_FAILURE(vrc))
7556 {
7557 // fallback if VBOX_USER_LOGHOME is not set or invalid
7558 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7559 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7560 aLogFolder.append(RTPATH_DELIMITER);
7561 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7562 }
7563}
7564
7565/**
7566 * Returns the full path to the machine's log file for an given index.
7567 */
7568Utf8Str Machine::i_getLogFilename(ULONG idx)
7569{
7570 Utf8Str logFolder;
7571 getLogFolder(logFolder);
7572 Assert(logFolder.length());
7573
7574 Utf8Str log;
7575 if (idx == 0)
7576 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7577#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7578 else if (idx == 1)
7579 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7580 else
7581 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7582#else
7583 else
7584 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7585#endif
7586 return log;
7587}
7588
7589/**
7590 * Returns the full path to the machine's hardened log file.
7591 */
7592Utf8Str Machine::i_getHardeningLogFilename(void)
7593{
7594 Utf8Str strFilename;
7595 getLogFolder(strFilename);
7596 Assert(strFilename.length());
7597 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7598 return strFilename;
7599}
7600
7601/**
7602 * Returns the default NVRAM filename based on the location of the VM config.
7603 * Note that this is a relative path.
7604 */
7605Utf8Str Machine::i_getDefaultNVRAMFilename()
7606{
7607 AutoCaller autoCaller(this);
7608 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7609
7610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7611
7612 if (i_isSnapshotMachine())
7613 return Utf8Str::Empty;
7614
7615 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7616 strNVRAMFilePath.stripPath();
7617 strNVRAMFilePath.stripSuffix();
7618 strNVRAMFilePath += ".nvram";
7619
7620 return strNVRAMFilePath;
7621}
7622
7623/**
7624 * Returns the NVRAM filename for a new snapshot. This intentionally works
7625 * similarly to the saved state file naming. Note that this is usually
7626 * a relative path, unless the snapshot folder is absolute.
7627 */
7628Utf8Str Machine::i_getSnapshotNVRAMFilename()
7629{
7630 AutoCaller autoCaller(this);
7631 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7632
7633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7634
7635 RTTIMESPEC ts;
7636 RTTimeNow(&ts);
7637 RTTIME time;
7638 RTTimeExplode(&time, &ts);
7639
7640 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7641 strNVRAMFilePath += RTPATH_DELIMITER;
7642 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7643 time.i32Year, time.u8Month, time.u8MonthDay,
7644 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7645
7646 return strNVRAMFilePath;
7647}
7648
7649/**
7650 * Returns the version of the settings file.
7651 */
7652SettingsVersion_T Machine::i_getSettingsVersion(void)
7653{
7654 AutoCaller autoCaller(this);
7655 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7656
7657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7658
7659 return mData->pMachineConfigFile->getSettingsVersion();
7660}
7661
7662/**
7663 * Composes a unique saved state filename based on the current system time. The filename is
7664 * granular to the second so this will work so long as no more than one snapshot is taken on
7665 * a machine per second.
7666 *
7667 * Before version 4.1, we used this formula for saved state files:
7668 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7669 * which no longer works because saved state files can now be shared between the saved state of the
7670 * "saved" machine and an online snapshot, and the following would cause problems:
7671 * 1) save machine
7672 * 2) create online snapshot from that machine state --> reusing saved state file
7673 * 3) save machine again --> filename would be reused, breaking the online snapshot
7674 *
7675 * So instead we now use a timestamp.
7676 *
7677 * @param strStateFilePath
7678 */
7679
7680void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7681{
7682 AutoCaller autoCaller(this);
7683 AssertComRCReturnVoid(autoCaller.rc());
7684
7685 {
7686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7687 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7688 }
7689
7690 RTTIMESPEC ts;
7691 RTTimeNow(&ts);
7692 RTTIME time;
7693 RTTimeExplode(&time, &ts);
7694
7695 strStateFilePath += RTPATH_DELIMITER;
7696 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7697 time.i32Year, time.u8Month, time.u8MonthDay,
7698 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7699}
7700
7701/**
7702 * Returns whether at least one USB controller is present for the VM.
7703 */
7704bool Machine::i_isUSBControllerPresent()
7705{
7706 AutoCaller autoCaller(this);
7707 AssertComRCReturn(autoCaller.rc(), false);
7708
7709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7710
7711 return (mUSBControllers->size() > 0);
7712}
7713
7714
7715/**
7716 * @note Locks this object for writing, calls the client process
7717 * (inside the lock).
7718 */
7719HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7720 const Utf8Str &strFrontend,
7721 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7722 ProgressProxy *aProgress)
7723{
7724 LogFlowThisFuncEnter();
7725
7726 AssertReturn(aControl, E_FAIL);
7727 AssertReturn(aProgress, E_FAIL);
7728 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7729
7730 AutoCaller autoCaller(this);
7731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7732
7733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7734
7735 if (!mData->mRegistered)
7736 return setError(E_UNEXPECTED,
7737 tr("The machine '%s' is not registered"),
7738 mUserData->s.strName.c_str());
7739
7740 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7741
7742 /* The process started when launching a VM with separate UI/VM processes is always
7743 * the UI process, i.e. needs special handling as it won't claim the session. */
7744 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7745
7746 if (fSeparate)
7747 {
7748 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7749 return setError(VBOX_E_INVALID_OBJECT_STATE,
7750 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7751 mUserData->s.strName.c_str());
7752 }
7753 else
7754 {
7755 if ( mData->mSession.mState == SessionState_Locked
7756 || mData->mSession.mState == SessionState_Spawning
7757 || mData->mSession.mState == SessionState_Unlocking)
7758 return setError(VBOX_E_INVALID_OBJECT_STATE,
7759 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7760 mUserData->s.strName.c_str());
7761
7762 /* may not be busy */
7763 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7764 }
7765
7766 /* Hardening logging */
7767#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7768 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7769 {
7770 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7771 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7772 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7773 {
7774 Utf8Str strStartupLogDir = strHardeningLogFile;
7775 strStartupLogDir.stripFilename();
7776 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7777 file without stripping the file. */
7778 }
7779 strSupHardeningLogArg.append(strHardeningLogFile);
7780
7781 /* Remove legacy log filename to avoid confusion. */
7782 Utf8Str strOldStartupLogFile;
7783 getLogFolder(strOldStartupLogFile);
7784 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7785 RTFileDelete(strOldStartupLogFile.c_str());
7786 }
7787#else
7788 Utf8Str strSupHardeningLogArg;
7789#endif
7790
7791 Utf8Str strAppOverride;
7792#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7793 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7794#endif
7795
7796 bool fUseVBoxSDS = false;
7797 Utf8Str strCanonicalName;
7798 if (false)
7799 { }
7800#ifdef VBOX_WITH_QTGUI
7801 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7802 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7803 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7804 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7805 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7806 {
7807 strCanonicalName = "GUI/Qt";
7808 fUseVBoxSDS = true;
7809 }
7810#endif
7811#ifdef VBOX_WITH_VBOXSDL
7812 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7813 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7814 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7815 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7816 {
7817 strCanonicalName = "GUI/SDL";
7818 fUseVBoxSDS = true;
7819 }
7820#endif
7821#ifdef VBOX_WITH_HEADLESS
7822 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7823 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7824 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7825 {
7826 strCanonicalName = "headless";
7827 }
7828#endif
7829 else
7830 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7831
7832 Utf8Str idStr = mData->mUuid.toString();
7833 Utf8Str const &strMachineName = mUserData->s.strName;
7834 RTPROCESS pid = NIL_RTPROCESS;
7835
7836#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7837 RT_NOREF(fUseVBoxSDS);
7838#else
7839 DWORD idCallerSession = ~(DWORD)0;
7840 if (fUseVBoxSDS)
7841 {
7842 /*
7843 * The VBoxSDS should be used for process launching the VM with
7844 * GUI only if the caller and the VBoxSDS are in different Windows
7845 * sessions and the caller in the interactive one.
7846 */
7847 fUseVBoxSDS = false;
7848
7849 /* Get windows session of the current process. The process token used
7850 due to several reasons:
7851 1. The token is absent for the current thread except someone set it
7852 for us.
7853 2. Needs to get the id of the session where the process is started.
7854 We only need to do this once, though. */
7855 static DWORD s_idCurrentSession = ~(DWORD)0;
7856 DWORD idCurrentSession = s_idCurrentSession;
7857 if (idCurrentSession == ~(DWORD)0)
7858 {
7859 HANDLE hCurrentProcessToken = NULL;
7860 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7861 {
7862 DWORD cbIgn = 0;
7863 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7864 s_idCurrentSession = idCurrentSession;
7865 else
7866 {
7867 idCurrentSession = ~(DWORD)0;
7868 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7869 }
7870 CloseHandle(hCurrentProcessToken);
7871 }
7872 else
7873 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7874 }
7875
7876 /* get the caller's session */
7877 HRESULT hrc = CoImpersonateClient();
7878 if (SUCCEEDED(hrc))
7879 {
7880 HANDLE hCallerThreadToken;
7881 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7882 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7883 &hCallerThreadToken))
7884 {
7885 SetLastError(NO_ERROR);
7886 DWORD cbIgn = 0;
7887 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7888 {
7889 /* Only need to use SDS if the session ID differs: */
7890 if (idCurrentSession != idCallerSession)
7891 {
7892 fUseVBoxSDS = false;
7893
7894 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7895 DWORD cbTokenGroups = 0;
7896 PTOKEN_GROUPS pTokenGroups = NULL;
7897 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7898 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7899 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7900 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7901 {
7902 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7903 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7904 PSID pInteractiveSid = NULL;
7905 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7906 {
7907 /* Iterate over the groups looking for the interactive SID: */
7908 fUseVBoxSDS = false;
7909 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7910 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7911 {
7912 fUseVBoxSDS = true;
7913 break;
7914 }
7915 FreeSid(pInteractiveSid);
7916 }
7917 }
7918 else
7919 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7920 RTMemTmpFree(pTokenGroups);
7921 }
7922 }
7923 else
7924 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7925 CloseHandle(hCallerThreadToken);
7926 }
7927 else
7928 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7929 CoRevertToSelf();
7930 }
7931 else
7932 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7933 }
7934 if (fUseVBoxSDS)
7935 {
7936 /* connect to VBoxSDS */
7937 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7938 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7939 if (FAILED(rc))
7940 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7941 strMachineName.c_str());
7942
7943 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7944 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7945 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7946 service to access the files. */
7947 rc = CoSetProxyBlanket(pVBoxSDS,
7948 RPC_C_AUTHN_DEFAULT,
7949 RPC_C_AUTHZ_DEFAULT,
7950 COLE_DEFAULT_PRINCIPAL,
7951 RPC_C_AUTHN_LEVEL_DEFAULT,
7952 RPC_C_IMP_LEVEL_IMPERSONATE,
7953 NULL,
7954 EOAC_DEFAULT);
7955 if (FAILED(rc))
7956 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7957
7958 size_t const cEnvVars = aEnvironmentChanges.size();
7959 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7960 for (size_t i = 0; i < cEnvVars; i++)
7961 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7962
7963 ULONG uPid = 0;
7964 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7965 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7966 idCallerSession, &uPid);
7967 if (FAILED(rc))
7968 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7969 pid = (RTPROCESS)uPid;
7970 }
7971 else
7972#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7973 {
7974 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7975 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7976 if (RT_FAILURE(vrc))
7977 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7978 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7979 }
7980
7981 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7982 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7983
7984 if (!fSeparate)
7985 {
7986 /*
7987 * Note that we don't release the lock here before calling the client,
7988 * because it doesn't need to call us back if called with a NULL argument.
7989 * Releasing the lock here is dangerous because we didn't prepare the
7990 * launch data yet, but the client we've just started may happen to be
7991 * too fast and call LockMachine() that will fail (because of PID, etc.),
7992 * so that the Machine will never get out of the Spawning session state.
7993 */
7994
7995 /* inform the session that it will be a remote one */
7996 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7997#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7998 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7999#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8000 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8001#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8002 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8003
8004 if (FAILED(rc))
8005 {
8006 /* restore the session state */
8007 mData->mSession.mState = SessionState_Unlocked;
8008 alock.release();
8009 mParent->i_addProcessToReap(pid);
8010 /* The failure may occur w/o any error info (from RPC), so provide one */
8011 return setError(VBOX_E_VM_ERROR,
8012 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8013 }
8014
8015 /* attach launch data to the machine */
8016 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8017 mData->mSession.mRemoteControls.push_back(aControl);
8018 mData->mSession.mProgress = aProgress;
8019 mData->mSession.mPID = pid;
8020 mData->mSession.mState = SessionState_Spawning;
8021 Assert(strCanonicalName.isNotEmpty());
8022 mData->mSession.mName = strCanonicalName;
8023 }
8024 else
8025 {
8026 /* For separate UI process we declare the launch as completed instantly, as the
8027 * actual headless VM start may or may not come. No point in remembering anything
8028 * yet, as what matters for us is when the headless VM gets started. */
8029 aProgress->i_notifyComplete(S_OK);
8030 }
8031
8032 alock.release();
8033 mParent->i_addProcessToReap(pid);
8034
8035 LogFlowThisFuncLeave();
8036 return S_OK;
8037}
8038
8039/**
8040 * Returns @c true if the given session machine instance has an open direct
8041 * session (and optionally also for direct sessions which are closing) and
8042 * returns the session control machine instance if so.
8043 *
8044 * Note that when the method returns @c false, the arguments remain unchanged.
8045 *
8046 * @param aMachine Session machine object.
8047 * @param aControl Direct session control object (optional).
8048 * @param aRequireVM If true then only allow VM sessions.
8049 * @param aAllowClosing If true then additionally a session which is currently
8050 * being closed will also be allowed.
8051 *
8052 * @note locks this object for reading.
8053 */
8054bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8055 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8056 bool aRequireVM /*= false*/,
8057 bool aAllowClosing /*= false*/)
8058{
8059 AutoLimitedCaller autoCaller(this);
8060 AssertComRCReturn(autoCaller.rc(), false);
8061
8062 /* just return false for inaccessible machines */
8063 if (getObjectState().getState() != ObjectState::Ready)
8064 return false;
8065
8066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8067
8068 if ( ( mData->mSession.mState == SessionState_Locked
8069 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8070 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8071 )
8072 {
8073 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8074
8075 aMachine = mData->mSession.mMachine;
8076
8077 if (aControl != NULL)
8078 *aControl = mData->mSession.mDirectControl;
8079
8080 return true;
8081 }
8082
8083 return false;
8084}
8085
8086/**
8087 * Returns @c true if the given machine has an spawning direct session.
8088 *
8089 * @note locks this object for reading.
8090 */
8091bool Machine::i_isSessionSpawning()
8092{
8093 AutoLimitedCaller autoCaller(this);
8094 AssertComRCReturn(autoCaller.rc(), false);
8095
8096 /* just return false for inaccessible machines */
8097 if (getObjectState().getState() != ObjectState::Ready)
8098 return false;
8099
8100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8101
8102 if (mData->mSession.mState == SessionState_Spawning)
8103 return true;
8104
8105 return false;
8106}
8107
8108/**
8109 * Called from the client watcher thread to check for unexpected client process
8110 * death during Session_Spawning state (e.g. before it successfully opened a
8111 * direct session).
8112 *
8113 * On Win32 and on OS/2, this method is called only when we've got the
8114 * direct client's process termination notification, so it always returns @c
8115 * true.
8116 *
8117 * On other platforms, this method returns @c true if the client process is
8118 * terminated and @c false if it's still alive.
8119 *
8120 * @note Locks this object for writing.
8121 */
8122bool Machine::i_checkForSpawnFailure()
8123{
8124 AutoCaller autoCaller(this);
8125 if (!autoCaller.isOk())
8126 {
8127 /* nothing to do */
8128 LogFlowThisFunc(("Already uninitialized!\n"));
8129 return true;
8130 }
8131
8132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8133
8134 if (mData->mSession.mState != SessionState_Spawning)
8135 {
8136 /* nothing to do */
8137 LogFlowThisFunc(("Not spawning any more!\n"));
8138 return true;
8139 }
8140
8141 HRESULT rc = S_OK;
8142
8143 /* PID not yet initialized, skip check. */
8144 if (mData->mSession.mPID == NIL_RTPROCESS)
8145 return false;
8146
8147 RTPROCSTATUS status;
8148 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8149
8150 if (vrc != VERR_PROCESS_RUNNING)
8151 {
8152 Utf8Str strExtraInfo;
8153
8154#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8155 /* If the startup logfile exists and is of non-zero length, tell the
8156 user to look there for more details to encourage them to attach it
8157 when reporting startup issues. */
8158 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8159 uint64_t cbStartupLogFile = 0;
8160 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8161 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8162 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8163#endif
8164
8165 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8166 rc = setError(E_FAIL,
8167 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8168 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8169 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8170 rc = setError(E_FAIL,
8171 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8172 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8173 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8174 rc = setError(E_FAIL,
8175 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8176 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8177 else
8178 rc = setErrorBoth(E_FAIL, vrc,
8179 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8180 i_getName().c_str(), vrc, strExtraInfo.c_str());
8181 }
8182
8183 if (FAILED(rc))
8184 {
8185 /* Close the remote session, remove the remote control from the list
8186 * and reset session state to Closed (@note keep the code in sync with
8187 * the relevant part in LockMachine()). */
8188
8189 Assert(mData->mSession.mRemoteControls.size() == 1);
8190 if (mData->mSession.mRemoteControls.size() == 1)
8191 {
8192 ErrorInfoKeeper eik;
8193 mData->mSession.mRemoteControls.front()->Uninitialize();
8194 }
8195
8196 mData->mSession.mRemoteControls.clear();
8197 mData->mSession.mState = SessionState_Unlocked;
8198
8199 /* finalize the progress after setting the state */
8200 if (!mData->mSession.mProgress.isNull())
8201 {
8202 mData->mSession.mProgress->notifyComplete(rc);
8203 mData->mSession.mProgress.setNull();
8204 }
8205
8206 mData->mSession.mPID = NIL_RTPROCESS;
8207
8208 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8209 return true;
8210 }
8211
8212 return false;
8213}
8214
8215/**
8216 * Checks whether the machine can be registered. If so, commits and saves
8217 * all settings.
8218 *
8219 * @note Must be called from mParent's write lock. Locks this object and
8220 * children for writing.
8221 */
8222HRESULT Machine::i_prepareRegister()
8223{
8224 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8225
8226 AutoLimitedCaller autoCaller(this);
8227 AssertComRCReturnRC(autoCaller.rc());
8228
8229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8230
8231 /* wait for state dependents to drop to zero */
8232 i_ensureNoStateDependencies(alock);
8233
8234 if (!mData->mAccessible)
8235 return setError(VBOX_E_INVALID_OBJECT_STATE,
8236 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8237 mUserData->s.strName.c_str(),
8238 mData->mUuid.toString().c_str());
8239
8240 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8241
8242 if (mData->mRegistered)
8243 return setError(VBOX_E_INVALID_OBJECT_STATE,
8244 tr("The machine '%s' with UUID {%s} is already registered"),
8245 mUserData->s.strName.c_str(),
8246 mData->mUuid.toString().c_str());
8247
8248 HRESULT rc = S_OK;
8249
8250 // Ensure the settings are saved. If we are going to be registered and
8251 // no config file exists yet, create it by calling i_saveSettings() too.
8252 if ( (mData->flModifications)
8253 || (!mData->pMachineConfigFile->fileExists())
8254 )
8255 {
8256 rc = i_saveSettings(NULL, alock);
8257 // no need to check whether VirtualBox.xml needs saving too since
8258 // we can't have a machine XML file rename pending
8259 if (FAILED(rc)) return rc;
8260 }
8261
8262 /* more config checking goes here */
8263
8264 if (SUCCEEDED(rc))
8265 {
8266 /* we may have had implicit modifications we want to fix on success */
8267 i_commit();
8268
8269 mData->mRegistered = true;
8270 }
8271 else
8272 {
8273 /* we may have had implicit modifications we want to cancel on failure*/
8274 i_rollback(false /* aNotify */);
8275 }
8276
8277 return rc;
8278}
8279
8280/**
8281 * Increases the number of objects dependent on the machine state or on the
8282 * registered state. Guarantees that these two states will not change at least
8283 * until #i_releaseStateDependency() is called.
8284 *
8285 * Depending on the @a aDepType value, additional state checks may be made.
8286 * These checks will set extended error info on failure. See
8287 * #i_checkStateDependency() for more info.
8288 *
8289 * If this method returns a failure, the dependency is not added and the caller
8290 * is not allowed to rely on any particular machine state or registration state
8291 * value and may return the failed result code to the upper level.
8292 *
8293 * @param aDepType Dependency type to add.
8294 * @param aState Current machine state (NULL if not interested).
8295 * @param aRegistered Current registered state (NULL if not interested).
8296 *
8297 * @note Locks this object for writing.
8298 */
8299HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8300 MachineState_T *aState /* = NULL */,
8301 BOOL *aRegistered /* = NULL */)
8302{
8303 AutoCaller autoCaller(this);
8304 AssertComRCReturnRC(autoCaller.rc());
8305
8306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8307
8308 HRESULT rc = i_checkStateDependency(aDepType);
8309 if (FAILED(rc)) return rc;
8310
8311 {
8312 if (mData->mMachineStateChangePending != 0)
8313 {
8314 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8315 * drop to zero so don't add more. It may make sense to wait a bit
8316 * and retry before reporting an error (since the pending state
8317 * transition should be really quick) but let's just assert for
8318 * now to see if it ever happens on practice. */
8319
8320 AssertFailed();
8321
8322 return setError(E_ACCESSDENIED,
8323 tr("Machine state change is in progress. Please retry the operation later."));
8324 }
8325
8326 ++mData->mMachineStateDeps;
8327 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8328 }
8329
8330 if (aState)
8331 *aState = mData->mMachineState;
8332 if (aRegistered)
8333 *aRegistered = mData->mRegistered;
8334
8335 return S_OK;
8336}
8337
8338/**
8339 * Decreases the number of objects dependent on the machine state.
8340 * Must always complete the #i_addStateDependency() call after the state
8341 * dependency is no more necessary.
8342 */
8343void Machine::i_releaseStateDependency()
8344{
8345 AutoCaller autoCaller(this);
8346 AssertComRCReturnVoid(autoCaller.rc());
8347
8348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8349
8350 /* releaseStateDependency() w/o addStateDependency()? */
8351 AssertReturnVoid(mData->mMachineStateDeps != 0);
8352 -- mData->mMachineStateDeps;
8353
8354 if (mData->mMachineStateDeps == 0)
8355 {
8356 /* inform i_ensureNoStateDependencies() that there are no more deps */
8357 if (mData->mMachineStateChangePending != 0)
8358 {
8359 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8360 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8361 }
8362 }
8363}
8364
8365Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8366{
8367 /* start with nothing found */
8368 Utf8Str strResult("");
8369
8370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8371
8372 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8373 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8374 // found:
8375 strResult = it->second; // source is a Utf8Str
8376
8377 return strResult;
8378}
8379
8380// protected methods
8381/////////////////////////////////////////////////////////////////////////////
8382
8383/**
8384 * Performs machine state checks based on the @a aDepType value. If a check
8385 * fails, this method will set extended error info, otherwise it will return
8386 * S_OK. It is supposed, that on failure, the caller will immediately return
8387 * the return value of this method to the upper level.
8388 *
8389 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8390 *
8391 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8392 * current state of this machine object allows to change settings of the
8393 * machine (i.e. the machine is not registered, or registered but not running
8394 * and not saved). It is useful to call this method from Machine setters
8395 * before performing any change.
8396 *
8397 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8398 * as for MutableStateDep except that if the machine is saved, S_OK is also
8399 * returned. This is useful in setters which allow changing machine
8400 * properties when it is in the saved state.
8401 *
8402 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8403 * if the current state of this machine object allows to change runtime
8404 * changeable settings of the machine (i.e. the machine is not registered, or
8405 * registered but either running or not running and not saved). It is useful
8406 * to call this method from Machine setters before performing any changes to
8407 * runtime changeable settings.
8408 *
8409 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8410 * the same as for MutableOrRunningStateDep except that if the machine is
8411 * saved, S_OK is also returned. This is useful in setters which allow
8412 * changing runtime and saved state changeable machine properties.
8413 *
8414 * @param aDepType Dependency type to check.
8415 *
8416 * @note Non Machine based classes should use #i_addStateDependency() and
8417 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8418 * template.
8419 *
8420 * @note This method must be called from under this object's read or write
8421 * lock.
8422 */
8423HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8424{
8425 switch (aDepType)
8426 {
8427 case AnyStateDep:
8428 {
8429 break;
8430 }
8431 case MutableStateDep:
8432 {
8433 if ( mData->mRegistered
8434 && ( !i_isSessionMachine()
8435 || ( mData->mMachineState != MachineState_Aborted
8436 && mData->mMachineState != MachineState_Teleported
8437 && mData->mMachineState != MachineState_PoweredOff
8438 )
8439 )
8440 )
8441 return setError(VBOX_E_INVALID_VM_STATE,
8442 tr("The machine is not mutable (state is %s)"),
8443 Global::stringifyMachineState(mData->mMachineState));
8444 break;
8445 }
8446 case MutableOrSavedStateDep:
8447 {
8448 if ( mData->mRegistered
8449 && ( !i_isSessionMachine()
8450 || ( mData->mMachineState != MachineState_Aborted
8451 && mData->mMachineState != MachineState_Teleported
8452 && mData->mMachineState != MachineState_Saved
8453 && mData->mMachineState != MachineState_AbortedSaved
8454 && mData->mMachineState != MachineState_PoweredOff
8455 )
8456 )
8457 )
8458 return setError(VBOX_E_INVALID_VM_STATE,
8459 tr("The machine is not mutable or saved (state is %s)"),
8460 Global::stringifyMachineState(mData->mMachineState));
8461 break;
8462 }
8463 case MutableOrRunningStateDep:
8464 {
8465 if ( mData->mRegistered
8466 && ( !i_isSessionMachine()
8467 || ( mData->mMachineState != MachineState_Aborted
8468 && mData->mMachineState != MachineState_Teleported
8469 && mData->mMachineState != MachineState_PoweredOff
8470 && !Global::IsOnline(mData->mMachineState)
8471 )
8472 )
8473 )
8474 return setError(VBOX_E_INVALID_VM_STATE,
8475 tr("The machine is not mutable or running (state is %s)"),
8476 Global::stringifyMachineState(mData->mMachineState));
8477 break;
8478 }
8479 case MutableOrSavedOrRunningStateDep:
8480 {
8481 if ( mData->mRegistered
8482 && ( !i_isSessionMachine()
8483 || ( mData->mMachineState != MachineState_Aborted
8484 && mData->mMachineState != MachineState_Teleported
8485 && mData->mMachineState != MachineState_Saved
8486 && mData->mMachineState != MachineState_AbortedSaved
8487 && mData->mMachineState != MachineState_PoweredOff
8488 && !Global::IsOnline(mData->mMachineState)
8489 )
8490 )
8491 )
8492 return setError(VBOX_E_INVALID_VM_STATE,
8493 tr("The machine is not mutable, saved or running (state is %s)"),
8494 Global::stringifyMachineState(mData->mMachineState));
8495 break;
8496 }
8497 }
8498
8499 return S_OK;
8500}
8501
8502/**
8503 * Helper to initialize all associated child objects and allocate data
8504 * structures.
8505 *
8506 * This method must be called as a part of the object's initialization procedure
8507 * (usually done in the #init() method).
8508 *
8509 * @note Must be called only from #init() or from #i_registeredInit().
8510 */
8511HRESULT Machine::initDataAndChildObjects()
8512{
8513 AutoCaller autoCaller(this);
8514 AssertComRCReturnRC(autoCaller.rc());
8515 AssertReturn( getObjectState().getState() == ObjectState::InInit
8516 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8517
8518 AssertReturn(!mData->mAccessible, E_FAIL);
8519
8520 /* allocate data structures */
8521 mSSData.allocate();
8522 mUserData.allocate();
8523 mHWData.allocate();
8524 mMediumAttachments.allocate();
8525 mStorageControllers.allocate();
8526 mUSBControllers.allocate();
8527
8528 /* initialize mOSTypeId */
8529 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8530
8531/** @todo r=bird: init() methods never fails, right? Why don't we make them
8532 * return void then! */
8533
8534 /* create associated BIOS settings object */
8535 unconst(mBIOSSettings).createObject();
8536 mBIOSSettings->init(this);
8537
8538 /* create associated trusted platform module object */
8539 unconst(mTrustedPlatformModule).createObject();
8540 mTrustedPlatformModule->init(this);
8541
8542 /* create associated NVRAM store object */
8543 unconst(mNvramStore).createObject();
8544 mNvramStore->init(this);
8545
8546 /* create associated record settings object */
8547 unconst(mRecordingSettings).createObject();
8548 mRecordingSettings->init(this);
8549
8550 /* create the graphics adapter object (always present) */
8551 unconst(mGraphicsAdapter).createObject();
8552 mGraphicsAdapter->init(this);
8553
8554 /* create an associated VRDE object (default is disabled) */
8555 unconst(mVRDEServer).createObject();
8556 mVRDEServer->init(this);
8557
8558 /* create associated serial port objects */
8559 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8560 {
8561 unconst(mSerialPorts[slot]).createObject();
8562 mSerialPorts[slot]->init(this, slot);
8563 }
8564
8565 /* create associated parallel port objects */
8566 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8567 {
8568 unconst(mParallelPorts[slot]).createObject();
8569 mParallelPorts[slot]->init(this, slot);
8570 }
8571
8572 /* create the audio adapter object (always present, default is disabled) */
8573 unconst(mAudioAdapter).createObject();
8574 mAudioAdapter->init(this);
8575
8576 /* create the USB device filters object (always present) */
8577 unconst(mUSBDeviceFilters).createObject();
8578 mUSBDeviceFilters->init(this);
8579
8580 /* create associated network adapter objects */
8581 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8582 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8583 {
8584 unconst(mNetworkAdapters[slot]).createObject();
8585 mNetworkAdapters[slot]->init(this, slot);
8586 }
8587
8588 /* create the bandwidth control */
8589 unconst(mBandwidthControl).createObject();
8590 mBandwidthControl->init(this);
8591
8592 return S_OK;
8593}
8594
8595/**
8596 * Helper to uninitialize all associated child objects and to free all data
8597 * structures.
8598 *
8599 * This method must be called as a part of the object's uninitialization
8600 * procedure (usually done in the #uninit() method).
8601 *
8602 * @note Must be called only from #uninit() or from #i_registeredInit().
8603 */
8604void Machine::uninitDataAndChildObjects()
8605{
8606 AutoCaller autoCaller(this);
8607 AssertComRCReturnVoid(autoCaller.rc());
8608 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8609 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8610 || getObjectState().getState() == ObjectState::InUninit
8611 || getObjectState().getState() == ObjectState::Limited);
8612
8613 /* tell all our other child objects we've been uninitialized */
8614 if (mBandwidthControl)
8615 {
8616 mBandwidthControl->uninit();
8617 unconst(mBandwidthControl).setNull();
8618 }
8619
8620 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8621 {
8622 if (mNetworkAdapters[slot])
8623 {
8624 mNetworkAdapters[slot]->uninit();
8625 unconst(mNetworkAdapters[slot]).setNull();
8626 }
8627 }
8628
8629 if (mUSBDeviceFilters)
8630 {
8631 mUSBDeviceFilters->uninit();
8632 unconst(mUSBDeviceFilters).setNull();
8633 }
8634
8635 if (mAudioAdapter)
8636 {
8637 mAudioAdapter->uninit();
8638 unconst(mAudioAdapter).setNull();
8639 }
8640
8641 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8642 {
8643 if (mParallelPorts[slot])
8644 {
8645 mParallelPorts[slot]->uninit();
8646 unconst(mParallelPorts[slot]).setNull();
8647 }
8648 }
8649
8650 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8651 {
8652 if (mSerialPorts[slot])
8653 {
8654 mSerialPorts[slot]->uninit();
8655 unconst(mSerialPorts[slot]).setNull();
8656 }
8657 }
8658
8659 if (mVRDEServer)
8660 {
8661 mVRDEServer->uninit();
8662 unconst(mVRDEServer).setNull();
8663 }
8664
8665 if (mGraphicsAdapter)
8666 {
8667 mGraphicsAdapter->uninit();
8668 unconst(mGraphicsAdapter).setNull();
8669 }
8670
8671 if (mBIOSSettings)
8672 {
8673 mBIOSSettings->uninit();
8674 unconst(mBIOSSettings).setNull();
8675 }
8676
8677 if (mTrustedPlatformModule)
8678 {
8679 mTrustedPlatformModule->uninit();
8680 unconst(mTrustedPlatformModule).setNull();
8681 }
8682
8683 if (mNvramStore)
8684 {
8685 mNvramStore->uninit();
8686 unconst(mNvramStore).setNull();
8687 }
8688
8689 if (mRecordingSettings)
8690 {
8691 mRecordingSettings->uninit();
8692 unconst(mRecordingSettings).setNull();
8693 }
8694
8695 /* Deassociate media (only when a real Machine or a SnapshotMachine
8696 * instance is uninitialized; SessionMachine instances refer to real
8697 * Machine media). This is necessary for a clean re-initialization of
8698 * the VM after successfully re-checking the accessibility state. Note
8699 * that in case of normal Machine or SnapshotMachine uninitialization (as
8700 * a result of unregistering or deleting the snapshot), outdated media
8701 * attachments will already be uninitialized and deleted, so this
8702 * code will not affect them. */
8703 if ( !mMediumAttachments.isNull()
8704 && !i_isSessionMachine()
8705 )
8706 {
8707 for (MediumAttachmentList::const_iterator
8708 it = mMediumAttachments->begin();
8709 it != mMediumAttachments->end();
8710 ++it)
8711 {
8712 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8713 if (pMedium.isNull())
8714 continue;
8715 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8716 AssertComRC(rc);
8717 }
8718 }
8719
8720 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8721 {
8722 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8723 if (mData->mFirstSnapshot)
8724 {
8725 // Snapshots tree is protected by machine write lock.
8726 // Otherwise we assert in Snapshot::uninit()
8727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8728 mData->mFirstSnapshot->uninit();
8729 mData->mFirstSnapshot.setNull();
8730 }
8731
8732 mData->mCurrentSnapshot.setNull();
8733 }
8734
8735 /* free data structures (the essential mData structure is not freed here
8736 * since it may be still in use) */
8737 mMediumAttachments.free();
8738 mStorageControllers.free();
8739 mUSBControllers.free();
8740 mHWData.free();
8741 mUserData.free();
8742 mSSData.free();
8743}
8744
8745/**
8746 * Returns a pointer to the Machine object for this machine that acts like a
8747 * parent for complex machine data objects such as shared folders, etc.
8748 *
8749 * For primary Machine objects and for SnapshotMachine objects, returns this
8750 * object's pointer itself. For SessionMachine objects, returns the peer
8751 * (primary) machine pointer.
8752 */
8753Machine *Machine::i_getMachine()
8754{
8755 if (i_isSessionMachine())
8756 return (Machine*)mPeer;
8757 return this;
8758}
8759
8760/**
8761 * Makes sure that there are no machine state dependents. If necessary, waits
8762 * for the number of dependents to drop to zero.
8763 *
8764 * Make sure this method is called from under this object's write lock to
8765 * guarantee that no new dependents may be added when this method returns
8766 * control to the caller.
8767 *
8768 * @note Receives a lock to this object for writing. The lock will be released
8769 * while waiting (if necessary).
8770 *
8771 * @warning To be used only in methods that change the machine state!
8772 */
8773void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8774{
8775 AssertReturnVoid(isWriteLockOnCurrentThread());
8776
8777 /* Wait for all state dependents if necessary */
8778 if (mData->mMachineStateDeps != 0)
8779 {
8780 /* lazy semaphore creation */
8781 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8782 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8783
8784 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8785 mData->mMachineStateDeps));
8786
8787 ++mData->mMachineStateChangePending;
8788
8789 /* reset the semaphore before waiting, the last dependent will signal
8790 * it */
8791 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8792
8793 alock.release();
8794
8795 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8796
8797 alock.acquire();
8798
8799 -- mData->mMachineStateChangePending;
8800 }
8801}
8802
8803/**
8804 * Changes the machine state and informs callbacks.
8805 *
8806 * This method is not intended to fail so it either returns S_OK or asserts (and
8807 * returns a failure).
8808 *
8809 * @note Locks this object for writing.
8810 */
8811HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8812{
8813 LogFlowThisFuncEnter();
8814 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8815 Assert(aMachineState != MachineState_Null);
8816
8817 AutoCaller autoCaller(this);
8818 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8819
8820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8821
8822 /* wait for state dependents to drop to zero */
8823 i_ensureNoStateDependencies(alock);
8824
8825 MachineState_T const enmOldState = mData->mMachineState;
8826 if (enmOldState != aMachineState)
8827 {
8828 mData->mMachineState = aMachineState;
8829 RTTimeNow(&mData->mLastStateChange);
8830
8831#ifdef VBOX_WITH_DTRACE_R3_MAIN
8832 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8833#endif
8834 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8835 }
8836
8837 LogFlowThisFuncLeave();
8838 return S_OK;
8839}
8840
8841/**
8842 * Searches for a shared folder with the given logical name
8843 * in the collection of shared folders.
8844 *
8845 * @param aName logical name of the shared folder
8846 * @param aSharedFolder where to return the found object
8847 * @param aSetError whether to set the error info if the folder is
8848 * not found
8849 * @return
8850 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8851 *
8852 * @note
8853 * must be called from under the object's lock!
8854 */
8855HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8856 ComObjPtr<SharedFolder> &aSharedFolder,
8857 bool aSetError /* = false */)
8858{
8859 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8860 for (HWData::SharedFolderList::const_iterator
8861 it = mHWData->mSharedFolders.begin();
8862 it != mHWData->mSharedFolders.end();
8863 ++it)
8864 {
8865 SharedFolder *pSF = *it;
8866 AutoCaller autoCaller(pSF);
8867 if (pSF->i_getName() == aName)
8868 {
8869 aSharedFolder = pSF;
8870 rc = S_OK;
8871 break;
8872 }
8873 }
8874
8875 if (aSetError && FAILED(rc))
8876 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8877
8878 return rc;
8879}
8880
8881/**
8882 * Initializes all machine instance data from the given settings structures
8883 * from XML. The exception is the machine UUID which needs special handling
8884 * depending on the caller's use case, so the caller needs to set that herself.
8885 *
8886 * This gets called in several contexts during machine initialization:
8887 *
8888 * -- When machine XML exists on disk already and needs to be loaded into memory,
8889 * for example, from #i_registeredInit() to load all registered machines on
8890 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8891 * attached to the machine should be part of some media registry already.
8892 *
8893 * -- During OVF import, when a machine config has been constructed from an
8894 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8895 * ensure that the media listed as attachments in the config (which have
8896 * been imported from the OVF) receive the correct registry ID.
8897 *
8898 * -- During VM cloning.
8899 *
8900 * @param config Machine settings from XML.
8901 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8902 * for each attached medium in the config.
8903 * @return
8904 */
8905HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8906 const Guid *puuidRegistry)
8907{
8908 // copy name, description, OS type, teleporter, UTC etc.
8909 mUserData->s = config.machineUserData;
8910
8911 // look up the object by Id to check it is valid
8912 ComObjPtr<GuestOSType> pGuestOSType;
8913 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8914 if (!pGuestOSType.isNull())
8915 mUserData->s.strOsType = pGuestOSType->i_id();
8916
8917 // stateFile (optional)
8918 if (config.strStateFile.isEmpty())
8919 mSSData->strStateFilePath.setNull();
8920 else
8921 {
8922 Utf8Str stateFilePathFull(config.strStateFile);
8923 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8924 if (RT_FAILURE(vrc))
8925 return setErrorBoth(E_FAIL, vrc,
8926 tr("Invalid saved state file path '%s' (%Rrc)"),
8927 config.strStateFile.c_str(),
8928 vrc);
8929 mSSData->strStateFilePath = stateFilePathFull;
8930 }
8931
8932 // snapshot folder needs special processing so set it again
8933 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8934 if (FAILED(rc)) return rc;
8935
8936 /* Copy the extra data items (config may or may not be the same as
8937 * mData->pMachineConfigFile) if necessary. When loading the XML files
8938 * from disk they are the same, but not for OVF import. */
8939 if (mData->pMachineConfigFile != &config)
8940 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8941
8942 /* currentStateModified (optional, default is true) */
8943 mData->mCurrentStateModified = config.fCurrentStateModified;
8944
8945 mData->mLastStateChange = config.timeLastStateChange;
8946
8947 /*
8948 * note: all mUserData members must be assigned prior this point because
8949 * we need to commit changes in order to let mUserData be shared by all
8950 * snapshot machine instances.
8951 */
8952 mUserData.commitCopy();
8953
8954 // machine registry, if present (must be loaded before snapshots)
8955 if (config.canHaveOwnMediaRegistry())
8956 {
8957 // determine machine folder
8958 Utf8Str strMachineFolder = i_getSettingsFileFull();
8959 strMachineFolder.stripFilename();
8960 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8961 config.mediaRegistry,
8962 strMachineFolder);
8963 if (FAILED(rc)) return rc;
8964 }
8965
8966 /* Snapshot node (optional) */
8967 size_t cRootSnapshots;
8968 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8969 {
8970 // there must be only one root snapshot
8971 Assert(cRootSnapshots == 1);
8972 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8973
8974 rc = i_loadSnapshot(snap,
8975 config.uuidCurrentSnapshot);
8976 if (FAILED(rc)) return rc;
8977 }
8978
8979 // hardware data
8980 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8981 if (FAILED(rc)) return rc;
8982
8983 /*
8984 * NOTE: the assignment below must be the last thing to do,
8985 * otherwise it will be not possible to change the settings
8986 * somewhere in the code above because all setters will be
8987 * blocked by i_checkStateDependency(MutableStateDep).
8988 */
8989
8990 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8991 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8992 {
8993 /* no need to use i_setMachineState() during init() */
8994 mData->mMachineState = MachineState_AbortedSaved;
8995 }
8996 else if (config.fAborted)
8997 {
8998 mSSData->strStateFilePath.setNull();
8999
9000 /* no need to use i_setMachineState() during init() */
9001 mData->mMachineState = MachineState_Aborted;
9002 }
9003 else if (!mSSData->strStateFilePath.isEmpty())
9004 {
9005 /* no need to use i_setMachineState() during init() */
9006 mData->mMachineState = MachineState_Saved;
9007 }
9008
9009 // after loading settings, we are no longer different from the XML on disk
9010 mData->flModifications = 0;
9011
9012 return S_OK;
9013}
9014
9015/**
9016 * Loads all snapshots starting from the given settings.
9017 *
9018 * @param data snapshot settings.
9019 * @param aCurSnapshotId Current snapshot ID from the settings file.
9020 */
9021HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9022 const Guid &aCurSnapshotId)
9023{
9024 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9025 AssertReturn(!i_isSessionMachine(), E_FAIL);
9026
9027 HRESULT rc = S_OK;
9028
9029 std::list<const settings::Snapshot *> llSettingsTodo;
9030 llSettingsTodo.push_back(&data);
9031 std::list<Snapshot *> llParentsTodo;
9032 llParentsTodo.push_back(NULL);
9033
9034 while (llSettingsTodo.size() > 0)
9035 {
9036 const settings::Snapshot *current = llSettingsTodo.front();
9037 llSettingsTodo.pop_front();
9038 Snapshot *pParent = llParentsTodo.front();
9039 llParentsTodo.pop_front();
9040
9041 Utf8Str strStateFile;
9042 if (!current->strStateFile.isEmpty())
9043 {
9044 /* optional */
9045 strStateFile = current->strStateFile;
9046 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9047 if (RT_FAILURE(vrc))
9048 {
9049 setErrorBoth(E_FAIL, vrc,
9050 tr("Invalid saved state file path '%s' (%Rrc)"),
9051 strStateFile.c_str(), vrc);
9052 }
9053 }
9054
9055 /* create a snapshot machine object */
9056 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9057 pSnapshotMachine.createObject();
9058 rc = pSnapshotMachine->initFromSettings(this,
9059 current->hardware,
9060 &current->debugging,
9061 &current->autostart,
9062 current->uuid.ref(),
9063 strStateFile);
9064 if (FAILED(rc)) break;
9065
9066 /* create a snapshot object */
9067 ComObjPtr<Snapshot> pSnapshot;
9068 pSnapshot.createObject();
9069 /* initialize the snapshot */
9070 rc = pSnapshot->init(mParent, // VirtualBox object
9071 current->uuid,
9072 current->strName,
9073 current->strDescription,
9074 current->timestamp,
9075 pSnapshotMachine,
9076 pParent);
9077 if (FAILED(rc)) break;
9078
9079 /* memorize the first snapshot if necessary */
9080 if (!mData->mFirstSnapshot)
9081 {
9082 Assert(pParent == NULL);
9083 mData->mFirstSnapshot = pSnapshot;
9084 }
9085
9086 /* memorize the current snapshot when appropriate */
9087 if ( !mData->mCurrentSnapshot
9088 && pSnapshot->i_getId() == aCurSnapshotId
9089 )
9090 mData->mCurrentSnapshot = pSnapshot;
9091
9092 /* create all children */
9093 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9094 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9095 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9096 {
9097 llSettingsTodo.push_back(&*it);
9098 llParentsTodo.push_back(pSnapshot);
9099 }
9100 }
9101
9102 return rc;
9103}
9104
9105/**
9106 * Loads settings into mHWData.
9107 *
9108 * @param puuidRegistry Registry ID.
9109 * @param puuidSnapshot Snapshot ID
9110 * @param data Reference to the hardware settings.
9111 * @param pDbg Pointer to the debugging settings.
9112 * @param pAutostart Pointer to the autostart settings.
9113 */
9114HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9115 const Guid *puuidSnapshot,
9116 const settings::Hardware &data,
9117 const settings::Debugging *pDbg,
9118 const settings::Autostart *pAutostart)
9119{
9120 AssertReturn(!i_isSessionMachine(), E_FAIL);
9121
9122 HRESULT rc = S_OK;
9123
9124 try
9125 {
9126 ComObjPtr<GuestOSType> pGuestOSType;
9127 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9128
9129 /* The hardware version attribute (optional). */
9130 mHWData->mHWVersion = data.strVersion;
9131 mHWData->mHardwareUUID = data.uuid;
9132
9133 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9134 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9135 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9136 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9137 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9138 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9139 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9140 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9141 mHWData->mPAEEnabled = data.fPAE;
9142 mHWData->mLongMode = data.enmLongMode;
9143 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9144 mHWData->mAPIC = data.fAPIC;
9145 mHWData->mX2APIC = data.fX2APIC;
9146 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9147 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9148 mHWData->mSpecCtrl = data.fSpecCtrl;
9149 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9150 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9151 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9152 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9153 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9154 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9155 mHWData->mCPUCount = data.cCPUs;
9156 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9157 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9158 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9159 mHWData->mCpuProfile = data.strCpuProfile;
9160
9161 // cpu
9162 if (mHWData->mCPUHotPlugEnabled)
9163 {
9164 for (settings::CpuList::const_iterator
9165 it = data.llCpus.begin();
9166 it != data.llCpus.end();
9167 ++it)
9168 {
9169 const settings::Cpu &cpu = *it;
9170
9171 mHWData->mCPUAttached[cpu.ulId] = true;
9172 }
9173 }
9174
9175 // cpuid leafs
9176 for (settings::CpuIdLeafsList::const_iterator
9177 it = data.llCpuIdLeafs.begin();
9178 it != data.llCpuIdLeafs.end();
9179 ++it)
9180 {
9181 const settings::CpuIdLeaf &rLeaf= *it;
9182 if ( rLeaf.idx < UINT32_C(0x20)
9183 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9184 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9185 mHWData->mCpuIdLeafList.push_back(rLeaf);
9186 /* else: just ignore */
9187 }
9188
9189 mHWData->mMemorySize = data.ulMemorySizeMB;
9190 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9191
9192 // boot order
9193 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9194 {
9195 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9196 if (it == data.mapBootOrder.end())
9197 mHWData->mBootOrder[i] = DeviceType_Null;
9198 else
9199 mHWData->mBootOrder[i] = it->second;
9200 }
9201
9202 mHWData->mFirmwareType = data.firmwareType;
9203 mHWData->mPointingHIDType = data.pointingHIDType;
9204 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9205 mHWData->mChipsetType = data.chipsetType;
9206 mHWData->mIommuType = data.iommuType;
9207 mHWData->mParavirtProvider = data.paravirtProvider;
9208 mHWData->mParavirtDebug = data.strParavirtDebug;
9209 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9210 mHWData->mHPETEnabled = data.fHPETEnabled;
9211
9212 /* GraphicsAdapter */
9213 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9214 if (FAILED(rc)) return rc;
9215
9216 /* VRDEServer */
9217 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9218 if (FAILED(rc)) return rc;
9219
9220 /* BIOS */
9221 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9222 if (FAILED(rc)) return rc;
9223
9224 /* Trusted Platform Module */
9225 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9226 if (FAILED(rc)) return rc;
9227
9228 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9229 if (FAILED(rc)) return rc;
9230
9231 /* Recording settings */
9232 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
9233 if (FAILED(rc)) return rc;
9234
9235 // Bandwidth control (must come before network adapters)
9236 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9237 if (FAILED(rc)) return rc;
9238
9239 /* USB controllers */
9240 for (settings::USBControllerList::const_iterator
9241 it = data.usbSettings.llUSBControllers.begin();
9242 it != data.usbSettings.llUSBControllers.end();
9243 ++it)
9244 {
9245 const settings::USBController &settingsCtrl = *it;
9246 ComObjPtr<USBController> newCtrl;
9247
9248 newCtrl.createObject();
9249 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9250 mUSBControllers->push_back(newCtrl);
9251 }
9252
9253 /* USB device filters */
9254 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9255 if (FAILED(rc)) return rc;
9256
9257 // network adapters (establish array size first and apply defaults, to
9258 // ensure reading the same settings as we saved, since the list skips
9259 // adapters having defaults)
9260 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9261 size_t oldCount = mNetworkAdapters.size();
9262 if (newCount > oldCount)
9263 {
9264 mNetworkAdapters.resize(newCount);
9265 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9266 {
9267 unconst(mNetworkAdapters[slot]).createObject();
9268 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9269 }
9270 }
9271 else if (newCount < oldCount)
9272 mNetworkAdapters.resize(newCount);
9273 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9274 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9275 for (settings::NetworkAdaptersList::const_iterator
9276 it = data.llNetworkAdapters.begin();
9277 it != data.llNetworkAdapters.end();
9278 ++it)
9279 {
9280 const settings::NetworkAdapter &nic = *it;
9281
9282 /* slot uniqueness is guaranteed by XML Schema */
9283 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9284 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9285 if (FAILED(rc)) return rc;
9286 }
9287
9288 // serial ports (establish defaults first, to ensure reading the same
9289 // settings as we saved, since the list skips ports having defaults)
9290 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9291 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9292 for (settings::SerialPortsList::const_iterator
9293 it = data.llSerialPorts.begin();
9294 it != data.llSerialPorts.end();
9295 ++it)
9296 {
9297 const settings::SerialPort &s = *it;
9298
9299 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9300 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9301 if (FAILED(rc)) return rc;
9302 }
9303
9304 // parallel ports (establish defaults first, to ensure reading the same
9305 // settings as we saved, since the list skips ports having defaults)
9306 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9307 mParallelPorts[i]->i_applyDefaults();
9308 for (settings::ParallelPortsList::const_iterator
9309 it = data.llParallelPorts.begin();
9310 it != data.llParallelPorts.end();
9311 ++it)
9312 {
9313 const settings::ParallelPort &p = *it;
9314
9315 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9316 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9317 if (FAILED(rc)) return rc;
9318 }
9319
9320 /* AudioAdapter */
9321 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9322 if (FAILED(rc)) return rc;
9323
9324 /* storage controllers */
9325 rc = i_loadStorageControllers(data.storage,
9326 puuidRegistry,
9327 puuidSnapshot);
9328 if (FAILED(rc)) return rc;
9329
9330 /* Shared folders */
9331 for (settings::SharedFoldersList::const_iterator
9332 it = data.llSharedFolders.begin();
9333 it != data.llSharedFolders.end();
9334 ++it)
9335 {
9336 const settings::SharedFolder &sf = *it;
9337
9338 ComObjPtr<SharedFolder> sharedFolder;
9339 /* Check for double entries. Not allowed! */
9340 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9341 if (SUCCEEDED(rc))
9342 return setError(VBOX_E_OBJECT_IN_USE,
9343 tr("Shared folder named '%s' already exists"),
9344 sf.strName.c_str());
9345
9346 /* Create the new shared folder. Don't break on error. This will be
9347 * reported when the machine starts. */
9348 sharedFolder.createObject();
9349 rc = sharedFolder->init(i_getMachine(),
9350 sf.strName,
9351 sf.strHostPath,
9352 RT_BOOL(sf.fWritable),
9353 RT_BOOL(sf.fAutoMount),
9354 sf.strAutoMountPoint,
9355 false /* fFailOnError */);
9356 if (FAILED(rc)) return rc;
9357 mHWData->mSharedFolders.push_back(sharedFolder);
9358 }
9359
9360 // Clipboard
9361 mHWData->mClipboardMode = data.clipboardMode;
9362 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9363
9364 // drag'n'drop
9365 mHWData->mDnDMode = data.dndMode;
9366
9367 // guest settings
9368 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9369
9370 // IO settings
9371 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9372 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9373
9374 // Host PCI devices
9375 for (settings::HostPCIDeviceAttachmentList::const_iterator
9376 it = data.pciAttachments.begin();
9377 it != data.pciAttachments.end();
9378 ++it)
9379 {
9380 const settings::HostPCIDeviceAttachment &hpda = *it;
9381 ComObjPtr<PCIDeviceAttachment> pda;
9382
9383 pda.createObject();
9384 pda->i_loadSettings(this, hpda);
9385 mHWData->mPCIDeviceAssignments.push_back(pda);
9386 }
9387
9388 /*
9389 * (The following isn't really real hardware, but it lives in HWData
9390 * for reasons of convenience.)
9391 */
9392
9393#ifdef VBOX_WITH_GUEST_PROPS
9394 /* Guest properties (optional) */
9395
9396 /* Only load transient guest properties for configs which have saved
9397 * state, because there shouldn't be any for powered off VMs. The same
9398 * logic applies for snapshots, as offline snapshots shouldn't have
9399 * any such properties. They confuse the code in various places.
9400 * Note: can't rely on the machine state, as it isn't set yet. */
9401 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9402 /* apologies for the hacky unconst() usage, but this needs hacking
9403 * actually inconsistent settings into consistency, otherwise there
9404 * will be some corner cases where the inconsistency survives
9405 * surprisingly long without getting fixed, especially for snapshots
9406 * as there are no config changes. */
9407 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9408 for (settings::GuestPropertiesList::iterator
9409 it = llGuestProperties.begin();
9410 it != llGuestProperties.end();
9411 /*nothing*/)
9412 {
9413 const settings::GuestProperty &prop = *it;
9414 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9415 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9416 if ( fSkipTransientGuestProperties
9417 && ( fFlags & GUEST_PROP_F_TRANSIENT
9418 || fFlags & GUEST_PROP_F_TRANSRESET))
9419 {
9420 it = llGuestProperties.erase(it);
9421 continue;
9422 }
9423 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9424 mHWData->mGuestProperties[prop.strName] = property;
9425 ++it;
9426 }
9427#endif /* VBOX_WITH_GUEST_PROPS defined */
9428
9429 rc = i_loadDebugging(pDbg);
9430 if (FAILED(rc))
9431 return rc;
9432
9433 mHWData->mAutostart = *pAutostart;
9434
9435 /* default frontend */
9436 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9437 }
9438 catch (std::bad_alloc &)
9439 {
9440 return E_OUTOFMEMORY;
9441 }
9442
9443 AssertComRC(rc);
9444 return rc;
9445}
9446
9447/**
9448 * Called from i_loadHardware() to load the debugging settings of the
9449 * machine.
9450 *
9451 * @param pDbg Pointer to the settings.
9452 */
9453HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9454{
9455 mHWData->mDebugging = *pDbg;
9456 /* no more processing currently required, this will probably change. */
9457 return S_OK;
9458}
9459
9460/**
9461 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9462 *
9463 * @param data storage settings.
9464 * @param puuidRegistry media registry ID to set media to or NULL;
9465 * see Machine::i_loadMachineDataFromSettings()
9466 * @param puuidSnapshot snapshot ID
9467 * @return
9468 */
9469HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9470 const Guid *puuidRegistry,
9471 const Guid *puuidSnapshot)
9472{
9473 AssertReturn(!i_isSessionMachine(), E_FAIL);
9474
9475 HRESULT rc = S_OK;
9476
9477 for (settings::StorageControllersList::const_iterator
9478 it = data.llStorageControllers.begin();
9479 it != data.llStorageControllers.end();
9480 ++it)
9481 {
9482 const settings::StorageController &ctlData = *it;
9483
9484 ComObjPtr<StorageController> pCtl;
9485 /* Try to find one with the name first. */
9486 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9487 if (SUCCEEDED(rc))
9488 return setError(VBOX_E_OBJECT_IN_USE,
9489 tr("Storage controller named '%s' already exists"),
9490 ctlData.strName.c_str());
9491
9492 pCtl.createObject();
9493 rc = pCtl->init(this,
9494 ctlData.strName,
9495 ctlData.storageBus,
9496 ctlData.ulInstance,
9497 ctlData.fBootable);
9498 if (FAILED(rc)) return rc;
9499
9500 mStorageControllers->push_back(pCtl);
9501
9502 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9503 if (FAILED(rc)) return rc;
9504
9505 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9506 if (FAILED(rc)) return rc;
9507
9508 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9509 if (FAILED(rc)) return rc;
9510
9511 /* Load the attached devices now. */
9512 rc = i_loadStorageDevices(pCtl,
9513 ctlData,
9514 puuidRegistry,
9515 puuidSnapshot);
9516 if (FAILED(rc)) return rc;
9517 }
9518
9519 return S_OK;
9520}
9521
9522/**
9523 * Called from i_loadStorageControllers for a controller's devices.
9524 *
9525 * @param aStorageController
9526 * @param data
9527 * @param puuidRegistry media registry ID to set media to or NULL; see
9528 * Machine::i_loadMachineDataFromSettings()
9529 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9530 * @return
9531 */
9532HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9533 const settings::StorageController &data,
9534 const Guid *puuidRegistry,
9535 const Guid *puuidSnapshot)
9536{
9537 HRESULT rc = S_OK;
9538
9539 /* paranoia: detect duplicate attachments */
9540 for (settings::AttachedDevicesList::const_iterator
9541 it = data.llAttachedDevices.begin();
9542 it != data.llAttachedDevices.end();
9543 ++it)
9544 {
9545 const settings::AttachedDevice &ad = *it;
9546
9547 for (settings::AttachedDevicesList::const_iterator it2 = it;
9548 it2 != data.llAttachedDevices.end();
9549 ++it2)
9550 {
9551 if (it == it2)
9552 continue;
9553
9554 const settings::AttachedDevice &ad2 = *it2;
9555
9556 if ( ad.lPort == ad2.lPort
9557 && ad.lDevice == ad2.lDevice)
9558 {
9559 return setError(E_FAIL,
9560 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9561 aStorageController->i_getName().c_str(),
9562 ad.lPort,
9563 ad.lDevice,
9564 mUserData->s.strName.c_str());
9565 }
9566 }
9567 }
9568
9569 for (settings::AttachedDevicesList::const_iterator
9570 it = data.llAttachedDevices.begin();
9571 it != data.llAttachedDevices.end();
9572 ++it)
9573 {
9574 const settings::AttachedDevice &dev = *it;
9575 ComObjPtr<Medium> medium;
9576
9577 switch (dev.deviceType)
9578 {
9579 case DeviceType_Floppy:
9580 case DeviceType_DVD:
9581 if (dev.strHostDriveSrc.isNotEmpty())
9582 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9583 false /* fRefresh */, medium);
9584 else
9585 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9586 dev.uuid,
9587 false /* fRefresh */,
9588 false /* aSetError */,
9589 medium);
9590 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9591 // This is not an error. The host drive or UUID might have vanished, so just go
9592 // ahead without this removeable medium attachment
9593 rc = S_OK;
9594 break;
9595
9596 case DeviceType_HardDisk:
9597 {
9598 /* find a hard disk by UUID */
9599 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9600 if (FAILED(rc))
9601 {
9602 if (i_isSnapshotMachine())
9603 {
9604 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9605 // so the user knows that the bad disk is in a snapshot somewhere
9606 com::ErrorInfo info;
9607 return setError(E_FAIL,
9608 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9609 puuidSnapshot->raw(),
9610 info.getText().raw());
9611 }
9612 else
9613 return rc;
9614 }
9615
9616 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9617
9618 if (medium->i_getType() == MediumType_Immutable)
9619 {
9620 if (i_isSnapshotMachine())
9621 return setError(E_FAIL,
9622 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9623 "of the virtual machine '%s' ('%s')"),
9624 medium->i_getLocationFull().c_str(),
9625 dev.uuid.raw(),
9626 puuidSnapshot->raw(),
9627 mUserData->s.strName.c_str(),
9628 mData->m_strConfigFileFull.c_str());
9629
9630 return setError(E_FAIL,
9631 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9632 medium->i_getLocationFull().c_str(),
9633 dev.uuid.raw(),
9634 mUserData->s.strName.c_str(),
9635 mData->m_strConfigFileFull.c_str());
9636 }
9637
9638 if (medium->i_getType() == MediumType_MultiAttach)
9639 {
9640 if (i_isSnapshotMachine())
9641 return setError(E_FAIL,
9642 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9643 "of the virtual machine '%s' ('%s')"),
9644 medium->i_getLocationFull().c_str(),
9645 dev.uuid.raw(),
9646 puuidSnapshot->raw(),
9647 mUserData->s.strName.c_str(),
9648 mData->m_strConfigFileFull.c_str());
9649
9650 return setError(E_FAIL,
9651 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9652 medium->i_getLocationFull().c_str(),
9653 dev.uuid.raw(),
9654 mUserData->s.strName.c_str(),
9655 mData->m_strConfigFileFull.c_str());
9656 }
9657
9658 if ( !i_isSnapshotMachine()
9659 && medium->i_getChildren().size() != 0
9660 )
9661 return setError(E_FAIL,
9662 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9663 "because it has %d differencing child hard disks"),
9664 medium->i_getLocationFull().c_str(),
9665 dev.uuid.raw(),
9666 mUserData->s.strName.c_str(),
9667 mData->m_strConfigFileFull.c_str(),
9668 medium->i_getChildren().size());
9669
9670 if (i_findAttachment(*mMediumAttachments.data(),
9671 medium))
9672 return setError(E_FAIL,
9673 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9674 medium->i_getLocationFull().c_str(),
9675 dev.uuid.raw(),
9676 mUserData->s.strName.c_str(),
9677 mData->m_strConfigFileFull.c_str());
9678
9679 break;
9680 }
9681
9682 default:
9683 return setError(E_FAIL,
9684 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9685 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9686 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9687 }
9688
9689 if (FAILED(rc))
9690 break;
9691
9692 /* Bandwidth groups are loaded at this point. */
9693 ComObjPtr<BandwidthGroup> pBwGroup;
9694
9695 if (!dev.strBwGroup.isEmpty())
9696 {
9697 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9698 if (FAILED(rc))
9699 return setError(E_FAIL,
9700 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9701 medium->i_getLocationFull().c_str(),
9702 dev.strBwGroup.c_str(),
9703 mUserData->s.strName.c_str(),
9704 mData->m_strConfigFileFull.c_str());
9705 pBwGroup->i_reference();
9706 }
9707
9708 const Utf8Str controllerName = aStorageController->i_getName();
9709 ComObjPtr<MediumAttachment> pAttachment;
9710 pAttachment.createObject();
9711 rc = pAttachment->init(this,
9712 medium,
9713 controllerName,
9714 dev.lPort,
9715 dev.lDevice,
9716 dev.deviceType,
9717 false,
9718 dev.fPassThrough,
9719 dev.fTempEject,
9720 dev.fNonRotational,
9721 dev.fDiscard,
9722 dev.fHotPluggable,
9723 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9724 if (FAILED(rc)) break;
9725
9726 /* associate the medium with this machine and snapshot */
9727 if (!medium.isNull())
9728 {
9729 AutoCaller medCaller(medium);
9730 if (FAILED(medCaller.rc())) return medCaller.rc();
9731 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9732
9733 if (i_isSnapshotMachine())
9734 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9735 else
9736 rc = medium->i_addBackReference(mData->mUuid);
9737 /* If the medium->addBackReference fails it sets an appropriate
9738 * error message, so no need to do any guesswork here. */
9739
9740 if (puuidRegistry)
9741 // caller wants registry ID to be set on all attached media (OVF import case)
9742 medium->i_addRegistry(*puuidRegistry);
9743 }
9744
9745 if (FAILED(rc))
9746 break;
9747
9748 /* back up mMediumAttachments to let registeredInit() properly rollback
9749 * on failure (= limited accessibility) */
9750 i_setModified(IsModified_Storage);
9751 mMediumAttachments.backup();
9752 mMediumAttachments->push_back(pAttachment);
9753 }
9754
9755 return rc;
9756}
9757
9758/**
9759 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9760 *
9761 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9762 * @param aSnapshot where to return the found snapshot
9763 * @param aSetError true to set extended error info on failure
9764 */
9765HRESULT Machine::i_findSnapshotById(const Guid &aId,
9766 ComObjPtr<Snapshot> &aSnapshot,
9767 bool aSetError /* = false */)
9768{
9769 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9770
9771 if (!mData->mFirstSnapshot)
9772 {
9773 if (aSetError)
9774 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9775 return E_FAIL;
9776 }
9777
9778 if (aId.isZero())
9779 aSnapshot = mData->mFirstSnapshot;
9780 else
9781 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9782
9783 if (!aSnapshot)
9784 {
9785 if (aSetError)
9786 return setError(E_FAIL,
9787 tr("Could not find a snapshot with UUID {%s}"),
9788 aId.toString().c_str());
9789 return E_FAIL;
9790 }
9791
9792 return S_OK;
9793}
9794
9795/**
9796 * Returns the snapshot with the given name or fails of no such snapshot.
9797 *
9798 * @param strName snapshot name to find
9799 * @param aSnapshot where to return the found snapshot
9800 * @param aSetError true to set extended error info on failure
9801 */
9802HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9803 ComObjPtr<Snapshot> &aSnapshot,
9804 bool aSetError /* = false */)
9805{
9806 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9807
9808 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9809
9810 if (!mData->mFirstSnapshot)
9811 {
9812 if (aSetError)
9813 return setError(VBOX_E_OBJECT_NOT_FOUND,
9814 tr("This machine does not have any snapshots"));
9815 return VBOX_E_OBJECT_NOT_FOUND;
9816 }
9817
9818 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9819
9820 if (!aSnapshot)
9821 {
9822 if (aSetError)
9823 return setError(VBOX_E_OBJECT_NOT_FOUND,
9824 tr("Could not find a snapshot named '%s'"), strName.c_str());
9825 return VBOX_E_OBJECT_NOT_FOUND;
9826 }
9827
9828 return S_OK;
9829}
9830
9831/**
9832 * Returns a storage controller object with the given name.
9833 *
9834 * @param aName storage controller name to find
9835 * @param aStorageController where to return the found storage controller
9836 * @param aSetError true to set extended error info on failure
9837 */
9838HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9839 ComObjPtr<StorageController> &aStorageController,
9840 bool aSetError /* = false */)
9841{
9842 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9843
9844 for (StorageControllerList::const_iterator
9845 it = mStorageControllers->begin();
9846 it != mStorageControllers->end();
9847 ++it)
9848 {
9849 if ((*it)->i_getName() == aName)
9850 {
9851 aStorageController = (*it);
9852 return S_OK;
9853 }
9854 }
9855
9856 if (aSetError)
9857 return setError(VBOX_E_OBJECT_NOT_FOUND,
9858 tr("Could not find a storage controller named '%s'"),
9859 aName.c_str());
9860 return VBOX_E_OBJECT_NOT_FOUND;
9861}
9862
9863/**
9864 * Returns a USB controller object with the given name.
9865 *
9866 * @param aName USB controller name to find
9867 * @param aUSBController where to return the found USB controller
9868 * @param aSetError true to set extended error info on failure
9869 */
9870HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9871 ComObjPtr<USBController> &aUSBController,
9872 bool aSetError /* = false */)
9873{
9874 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9875
9876 for (USBControllerList::const_iterator
9877 it = mUSBControllers->begin();
9878 it != mUSBControllers->end();
9879 ++it)
9880 {
9881 if ((*it)->i_getName() == aName)
9882 {
9883 aUSBController = (*it);
9884 return S_OK;
9885 }
9886 }
9887
9888 if (aSetError)
9889 return setError(VBOX_E_OBJECT_NOT_FOUND,
9890 tr("Could not find a storage controller named '%s'"),
9891 aName.c_str());
9892 return VBOX_E_OBJECT_NOT_FOUND;
9893}
9894
9895/**
9896 * Returns the number of USB controller instance of the given type.
9897 *
9898 * @param enmType USB controller type.
9899 */
9900ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9901{
9902 ULONG cCtrls = 0;
9903
9904 for (USBControllerList::const_iterator
9905 it = mUSBControllers->begin();
9906 it != mUSBControllers->end();
9907 ++it)
9908 {
9909 if ((*it)->i_getControllerType() == enmType)
9910 cCtrls++;
9911 }
9912
9913 return cCtrls;
9914}
9915
9916HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9917 MediumAttachmentList &atts)
9918{
9919 AutoCaller autoCaller(this);
9920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9921
9922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9923
9924 for (MediumAttachmentList::const_iterator
9925 it = mMediumAttachments->begin();
9926 it != mMediumAttachments->end();
9927 ++it)
9928 {
9929 const ComObjPtr<MediumAttachment> &pAtt = *it;
9930 // should never happen, but deal with NULL pointers in the list.
9931 AssertContinue(!pAtt.isNull());
9932
9933 // getControllerName() needs caller+read lock
9934 AutoCaller autoAttCaller(pAtt);
9935 if (FAILED(autoAttCaller.rc()))
9936 {
9937 atts.clear();
9938 return autoAttCaller.rc();
9939 }
9940 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9941
9942 if (pAtt->i_getControllerName() == aName)
9943 atts.push_back(pAtt);
9944 }
9945
9946 return S_OK;
9947}
9948
9949
9950/**
9951 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9952 * file if the machine name was changed and about creating a new settings file
9953 * if this is a new machine.
9954 *
9955 * @note Must be never called directly but only from #saveSettings().
9956 */
9957HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9958 bool *pfSettingsFileIsNew)
9959{
9960 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9961
9962 HRESULT rc = S_OK;
9963
9964 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9965 /// @todo need to handle primary group change, too
9966
9967 /* attempt to rename the settings file if machine name is changed */
9968 if ( mUserData->s.fNameSync
9969 && mUserData.isBackedUp()
9970 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9971 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9972 )
9973 {
9974 bool dirRenamed = false;
9975 bool fileRenamed = false;
9976
9977 Utf8Str configFile, newConfigFile;
9978 Utf8Str configFilePrev, newConfigFilePrev;
9979 Utf8Str NVRAMFile, newNVRAMFile;
9980 Utf8Str configDir, newConfigDir;
9981
9982 do
9983 {
9984 int vrc = VINF_SUCCESS;
9985
9986 Utf8Str name = mUserData.backedUpData()->s.strName;
9987 Utf8Str newName = mUserData->s.strName;
9988 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9989 if (group == "/")
9990 group.setNull();
9991 Utf8Str newGroup = mUserData->s.llGroups.front();
9992 if (newGroup == "/")
9993 newGroup.setNull();
9994
9995 configFile = mData->m_strConfigFileFull;
9996
9997 /* first, rename the directory if it matches the group and machine name */
9998 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9999 /** @todo hack, make somehow use of ComposeMachineFilename */
10000 if (mUserData->s.fDirectoryIncludesUUID)
10001 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10002 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10003 /** @todo hack, make somehow use of ComposeMachineFilename */
10004 if (mUserData->s.fDirectoryIncludesUUID)
10005 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10006 configDir = configFile;
10007 configDir.stripFilename();
10008 newConfigDir = configDir;
10009 if ( configDir.length() >= groupPlusName.length()
10010 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10011 groupPlusName.c_str()))
10012 {
10013 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10014 Utf8Str newConfigBaseDir(newConfigDir);
10015 newConfigDir.append(newGroupPlusName);
10016 /* consistency: use \ if appropriate on the platform */
10017 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10018 /* new dir and old dir cannot be equal here because of 'if'
10019 * above and because name != newName */
10020 Assert(configDir != newConfigDir);
10021 if (!fSettingsFileIsNew)
10022 {
10023 /* perform real rename only if the machine is not new */
10024 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10025 if ( vrc == VERR_FILE_NOT_FOUND
10026 || vrc == VERR_PATH_NOT_FOUND)
10027 {
10028 /* create the parent directory, then retry renaming */
10029 Utf8Str parent(newConfigDir);
10030 parent.stripFilename();
10031 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10032 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10033 }
10034 if (RT_FAILURE(vrc))
10035 {
10036 rc = setErrorBoth(E_FAIL, vrc,
10037 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10038 configDir.c_str(),
10039 newConfigDir.c_str(),
10040 vrc);
10041 break;
10042 }
10043 /* delete subdirectories which are no longer needed */
10044 Utf8Str dir(configDir);
10045 dir.stripFilename();
10046 while (dir != newConfigBaseDir && dir != ".")
10047 {
10048 vrc = RTDirRemove(dir.c_str());
10049 if (RT_FAILURE(vrc))
10050 break;
10051 dir.stripFilename();
10052 }
10053 dirRenamed = true;
10054 }
10055 }
10056
10057 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10058
10059 /* then try to rename the settings file itself */
10060 if (newConfigFile != configFile)
10061 {
10062 /* get the path to old settings file in renamed directory */
10063 Assert(mData->m_strConfigFileFull == configFile);
10064 configFile.printf("%s%c%s",
10065 newConfigDir.c_str(),
10066 RTPATH_DELIMITER,
10067 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10068 if (!fSettingsFileIsNew)
10069 {
10070 /* perform real rename only if the machine is not new */
10071 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10072 if (RT_FAILURE(vrc))
10073 {
10074 rc = setErrorBoth(E_FAIL, vrc,
10075 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10076 configFile.c_str(),
10077 newConfigFile.c_str(),
10078 vrc);
10079 break;
10080 }
10081 fileRenamed = true;
10082 configFilePrev = configFile;
10083 configFilePrev += "-prev";
10084 newConfigFilePrev = newConfigFile;
10085 newConfigFilePrev += "-prev";
10086 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10087 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10088 if (NVRAMFile.isNotEmpty())
10089 {
10090 // in the NVRAM file path, replace the old directory with the new directory
10091 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10092 {
10093 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10094 NVRAMFile = newConfigDir + strNVRAMFile;
10095 }
10096 newNVRAMFile = newConfigFile;
10097 newNVRAMFile.stripSuffix();
10098 newNVRAMFile += ".nvram";
10099 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10100 }
10101 }
10102 }
10103
10104 // update m_strConfigFileFull amd mConfigFile
10105 mData->m_strConfigFileFull = newConfigFile;
10106 // compute the relative path too
10107 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10108
10109 // store the old and new so that VirtualBox::i_saveSettings() can update
10110 // the media registry
10111 if ( mData->mRegistered
10112 && (configDir != newConfigDir || configFile != newConfigFile))
10113 {
10114 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10115
10116 if (pfNeedsGlobalSaveSettings)
10117 *pfNeedsGlobalSaveSettings = true;
10118 }
10119
10120 // in the saved state file path, replace the old directory with the new directory
10121 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10122 {
10123 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10124 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10125 }
10126 if (newNVRAMFile.isNotEmpty())
10127 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10128
10129 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10130 if (mData->mFirstSnapshot)
10131 {
10132 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10133 newConfigDir.c_str());
10134 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10135 newConfigDir.c_str());
10136 }
10137 }
10138 while (0);
10139
10140 if (FAILED(rc))
10141 {
10142 /* silently try to rename everything back */
10143 if (fileRenamed)
10144 {
10145 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10146 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10147 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10148 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10149 }
10150 if (dirRenamed)
10151 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10152 }
10153
10154 if (FAILED(rc)) return rc;
10155 }
10156
10157 if (fSettingsFileIsNew)
10158 {
10159 /* create a virgin config file */
10160 int vrc = VINF_SUCCESS;
10161
10162 /* ensure the settings directory exists */
10163 Utf8Str path(mData->m_strConfigFileFull);
10164 path.stripFilename();
10165 if (!RTDirExists(path.c_str()))
10166 {
10167 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10168 if (RT_FAILURE(vrc))
10169 {
10170 return setErrorBoth(E_FAIL, vrc,
10171 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10172 path.c_str(),
10173 vrc);
10174 }
10175 }
10176
10177 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10178 path = mData->m_strConfigFileFull;
10179 RTFILE f = NIL_RTFILE;
10180 vrc = RTFileOpen(&f, path.c_str(),
10181 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10182 if (RT_FAILURE(vrc))
10183 return setErrorBoth(E_FAIL, vrc,
10184 tr("Could not create the settings file '%s' (%Rrc)"),
10185 path.c_str(),
10186 vrc);
10187 RTFileClose(f);
10188 }
10189 if (pfSettingsFileIsNew)
10190 *pfSettingsFileIsNew = fSettingsFileIsNew;
10191
10192 return rc;
10193}
10194
10195/**
10196 * Saves and commits machine data, user data and hardware data.
10197 *
10198 * Note that on failure, the data remains uncommitted.
10199 *
10200 * @a aFlags may combine the following flags:
10201 *
10202 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10203 * Used when saving settings after an operation that makes them 100%
10204 * correspond to the settings from the current snapshot.
10205 * - SaveS_Force: settings will be saved without doing a deep compare of the
10206 * settings structures. This is used when this is called because snapshots
10207 * have changed to avoid the overhead of the deep compare.
10208 *
10209 * @note Must be called from under this object's write lock. Locks children for
10210 * writing.
10211 *
10212 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10213 * initialized to false and that will be set to true by this function if
10214 * the caller must invoke VirtualBox::i_saveSettings() because the global
10215 * settings have changed. This will happen if a machine rename has been
10216 * saved and the global machine and media registries will therefore need
10217 * updating.
10218 * @param alock Reference to the lock for this machine object.
10219 * @param aFlags Flags.
10220 */
10221HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10222 AutoWriteLock &alock,
10223 int aFlags /*= 0*/)
10224{
10225 LogFlowThisFuncEnter();
10226
10227 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10228
10229 /* make sure child objects are unable to modify the settings while we are
10230 * saving them */
10231 i_ensureNoStateDependencies(alock);
10232
10233 AssertReturn(!i_isSnapshotMachine(),
10234 E_FAIL);
10235
10236 if (!mData->mAccessible)
10237 return setError(VBOX_E_INVALID_VM_STATE,
10238 tr("The machine is not accessible, so cannot save settings"));
10239
10240 HRESULT rc = S_OK;
10241 PCVBOXCRYPTOIF pCryptoIf = NULL;
10242 const char *pszPassword = NULL;
10243 SecretKey *pKey = NULL;
10244
10245#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10246 if (mData->mstrKeyId.isNotEmpty())
10247 {
10248 /* VM is going to be encrypted. */
10249 alock.release(); /** @todo Revise the locking. */
10250 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10251 alock.acquire();
10252 if (FAILED(rc)) return rc; /* Error is set. */
10253
10254 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10255 if (RT_SUCCESS(vrc))
10256 pszPassword = (const char *)pKey->getKeyBuffer();
10257 else
10258 {
10259 mParent->i_releaseCryptoIf(pCryptoIf);
10260 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10261 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10262 mData->mstrKeyId.c_str(), vrc);
10263 }
10264 }
10265#else
10266 RT_NOREF(pKey);
10267#endif
10268
10269 bool fNeedsWrite = false;
10270 bool fSettingsFileIsNew = false;
10271
10272 /* First, prepare to save settings. It will care about renaming the
10273 * settings directory and file if the machine name was changed and about
10274 * creating a new settings file if this is a new machine. */
10275 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10276 &fSettingsFileIsNew);
10277 if (FAILED(rc))
10278 {
10279#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10280 if (pCryptoIf)
10281 {
10282 alock.release(); /** @todo Revise the locking. */
10283 mParent->i_releaseCryptoIf(pCryptoIf);
10284 alock.acquire();
10285 }
10286 if (pKey)
10287 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10288#endif
10289 return rc;
10290 }
10291
10292 // keep a pointer to the current settings structures
10293 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10294 settings::MachineConfigFile *pNewConfig = NULL;
10295
10296 try
10297 {
10298 // make a fresh one to have everyone write stuff into
10299 pNewConfig = new settings::MachineConfigFile(NULL);
10300 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10301#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10302 pNewConfig->strKeyId = mData->mstrKeyId;
10303 pNewConfig->strKeyStore = mData->mstrKeyStore;
10304#endif
10305
10306 // now go and copy all the settings data from COM to the settings structures
10307 // (this calls i_saveSettings() on all the COM objects in the machine)
10308 i_copyMachineDataToSettings(*pNewConfig);
10309
10310 if (aFlags & SaveS_ResetCurStateModified)
10311 {
10312 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10313 mData->mCurrentStateModified = FALSE;
10314 fNeedsWrite = true; // always, no need to compare
10315 }
10316 else if (aFlags & SaveS_Force)
10317 {
10318 fNeedsWrite = true; // always, no need to compare
10319 }
10320 else
10321 {
10322 if (!mData->mCurrentStateModified)
10323 {
10324 // do a deep compare of the settings that we just saved with the settings
10325 // previously stored in the config file; this invokes MachineConfigFile::operator==
10326 // which does a deep compare of all the settings, which is expensive but less expensive
10327 // than writing out XML in vain
10328 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10329
10330 // could still be modified if any settings changed
10331 mData->mCurrentStateModified = fAnySettingsChanged;
10332
10333 fNeedsWrite = fAnySettingsChanged;
10334 }
10335 else
10336 fNeedsWrite = true;
10337 }
10338
10339 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10340
10341 if (fNeedsWrite)
10342 {
10343 // now spit it all out!
10344 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10345 if (aFlags & SaveS_RemoveBackup)
10346 RTFileDelete((mData->m_strConfigFileFull + "-prev").c_str());
10347 }
10348
10349 mData->pMachineConfigFile = pNewConfig;
10350 delete pOldConfig;
10351 i_commit();
10352
10353 // after saving settings, we are no longer different from the XML on disk
10354 mData->flModifications = 0;
10355 }
10356 catch (HRESULT err)
10357 {
10358 // we assume that error info is set by the thrower
10359 rc = err;
10360
10361 // delete any newly created settings file
10362 if (fSettingsFileIsNew)
10363 RTFileDelete(mData->m_strConfigFileFull.c_str());
10364
10365 // restore old config
10366 delete pNewConfig;
10367 mData->pMachineConfigFile = pOldConfig;
10368 }
10369 catch (...)
10370 {
10371 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10372 }
10373
10374#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10375 if (pCryptoIf)
10376 {
10377 alock.release(); /** @todo Revise the locking. */
10378 mParent->i_releaseCryptoIf(pCryptoIf);
10379 alock.acquire();
10380 }
10381 if (pKey)
10382 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10383#endif
10384
10385 if (fNeedsWrite)
10386 {
10387 /* Fire the data change event, even on failure (since we've already
10388 * committed all data). This is done only for SessionMachines because
10389 * mutable Machine instances are always not registered (i.e. private
10390 * to the client process that creates them) and thus don't need to
10391 * inform callbacks. */
10392 if (i_isSessionMachine())
10393 mParent->i_onMachineDataChanged(mData->mUuid);
10394 }
10395
10396 LogFlowThisFunc(("rc=%08X\n", rc));
10397 LogFlowThisFuncLeave();
10398 return rc;
10399}
10400
10401/**
10402 * Implementation for saving the machine settings into the given
10403 * settings::MachineConfigFile instance. This copies machine extradata
10404 * from the previous machine config file in the instance data, if any.
10405 *
10406 * This gets called from two locations:
10407 *
10408 * -- Machine::i_saveSettings(), during the regular XML writing;
10409 *
10410 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10411 * exported to OVF and we write the VirtualBox proprietary XML
10412 * into a <vbox:Machine> tag.
10413 *
10414 * This routine fills all the fields in there, including snapshots, *except*
10415 * for the following:
10416 *
10417 * -- fCurrentStateModified. There is some special logic associated with that.
10418 *
10419 * The caller can then call MachineConfigFile::write() or do something else
10420 * with it.
10421 *
10422 * Caller must hold the machine lock!
10423 *
10424 * This throws XML errors and HRESULT, so the caller must have a catch block!
10425 */
10426void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10427{
10428 // deep copy extradata, being extra careful with self assignment (the STL
10429 // map assignment on Mac OS X clang based Xcode isn't checking)
10430 if (&config != mData->pMachineConfigFile)
10431 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10432
10433 config.uuid = mData->mUuid;
10434
10435 // copy name, description, OS type, teleport, UTC etc.
10436 config.machineUserData = mUserData->s;
10437
10438 if ( mData->mMachineState == MachineState_Saved
10439 || mData->mMachineState == MachineState_AbortedSaved
10440 || mData->mMachineState == MachineState_Restoring
10441 // when doing certain snapshot operations we may or may not have
10442 // a saved state in the current state, so keep everything as is
10443 || ( ( mData->mMachineState == MachineState_Snapshotting
10444 || mData->mMachineState == MachineState_DeletingSnapshot
10445 || mData->mMachineState == MachineState_RestoringSnapshot)
10446 && (!mSSData->strStateFilePath.isEmpty())
10447 )
10448 )
10449 {
10450 Assert(!mSSData->strStateFilePath.isEmpty());
10451 /* try to make the file name relative to the settings file dir */
10452 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10453 }
10454 else
10455 {
10456 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10457 config.strStateFile.setNull();
10458 }
10459
10460 if (mData->mCurrentSnapshot)
10461 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10462 else
10463 config.uuidCurrentSnapshot.clear();
10464
10465 config.timeLastStateChange = mData->mLastStateChange;
10466 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10467 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10468
10469 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10470 if (FAILED(rc)) throw rc;
10471
10472 // save machine's media registry if this is VirtualBox 4.0 or later
10473 if (config.canHaveOwnMediaRegistry())
10474 {
10475 // determine machine folder
10476 Utf8Str strMachineFolder = i_getSettingsFileFull();
10477 strMachineFolder.stripFilename();
10478 mParent->i_saveMediaRegistry(config.mediaRegistry,
10479 i_getId(), // only media with registry ID == machine UUID
10480 strMachineFolder);
10481 // this throws HRESULT
10482 }
10483
10484 // save snapshots
10485 rc = i_saveAllSnapshots(config);
10486 if (FAILED(rc)) throw rc;
10487}
10488
10489/**
10490 * Saves all snapshots of the machine into the given machine config file. Called
10491 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10492 * @param config
10493 * @return
10494 */
10495HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10496{
10497 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10498
10499 HRESULT rc = S_OK;
10500
10501 try
10502 {
10503 config.llFirstSnapshot.clear();
10504
10505 if (mData->mFirstSnapshot)
10506 {
10507 // the settings use a list for "the first snapshot"
10508 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10509
10510 // get reference to the snapshot on the list and work on that
10511 // element straight in the list to avoid excessive copying later
10512 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10513 if (FAILED(rc)) throw rc;
10514 }
10515
10516// if (mType == IsSessionMachine)
10517// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10518
10519 }
10520 catch (HRESULT err)
10521 {
10522 /* we assume that error info is set by the thrower */
10523 rc = err;
10524 }
10525 catch (...)
10526 {
10527 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10528 }
10529
10530 return rc;
10531}
10532
10533/**
10534 * Saves the VM hardware configuration. It is assumed that the
10535 * given node is empty.
10536 *
10537 * @param data Reference to the settings object for the hardware config.
10538 * @param pDbg Pointer to the settings object for the debugging config
10539 * which happens to live in mHWData.
10540 * @param pAutostart Pointer to the settings object for the autostart config
10541 * which happens to live in mHWData.
10542 */
10543HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10544 settings::Autostart *pAutostart)
10545{
10546 HRESULT rc = S_OK;
10547
10548 try
10549 {
10550 /* The hardware version attribute (optional).
10551 Automatically upgrade from 1 to current default hardware version
10552 when there is no saved state. (ugly!) */
10553 if ( mHWData->mHWVersion == "1"
10554 && mSSData->strStateFilePath.isEmpty()
10555 )
10556 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10557
10558 data.strVersion = mHWData->mHWVersion;
10559 data.uuid = mHWData->mHardwareUUID;
10560
10561 // CPU
10562 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10563 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10564 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10565 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10566 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10567 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10568 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10569 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10570 data.fPAE = !!mHWData->mPAEEnabled;
10571 data.enmLongMode = mHWData->mLongMode;
10572 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10573 data.fAPIC = !!mHWData->mAPIC;
10574 data.fX2APIC = !!mHWData->mX2APIC;
10575 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10576 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10577 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10578 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10579 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10580 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10581 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10582 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10583 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10584 data.cCPUs = mHWData->mCPUCount;
10585 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10586 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10587 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10588 data.strCpuProfile = mHWData->mCpuProfile;
10589
10590 data.llCpus.clear();
10591 if (data.fCpuHotPlug)
10592 {
10593 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10594 {
10595 if (mHWData->mCPUAttached[idx])
10596 {
10597 settings::Cpu cpu;
10598 cpu.ulId = idx;
10599 data.llCpus.push_back(cpu);
10600 }
10601 }
10602 }
10603
10604 /* Standard and Extended CPUID leafs. */
10605 data.llCpuIdLeafs.clear();
10606 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10607
10608 // memory
10609 data.ulMemorySizeMB = mHWData->mMemorySize;
10610 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10611
10612 // firmware
10613 data.firmwareType = mHWData->mFirmwareType;
10614
10615 // HID
10616 data.pointingHIDType = mHWData->mPointingHIDType;
10617 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10618
10619 // chipset
10620 data.chipsetType = mHWData->mChipsetType;
10621
10622 // iommu
10623 data.iommuType = mHWData->mIommuType;
10624
10625 // paravirt
10626 data.paravirtProvider = mHWData->mParavirtProvider;
10627 data.strParavirtDebug = mHWData->mParavirtDebug;
10628
10629 // emulated USB card reader
10630 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10631
10632 // HPET
10633 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10634
10635 // boot order
10636 data.mapBootOrder.clear();
10637 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10638 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10639
10640 /* VRDEServer settings (optional) */
10641 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10642 if (FAILED(rc)) throw rc;
10643
10644 /* BIOS settings (required) */
10645 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10646 if (FAILED(rc)) throw rc;
10647
10648 /* Trusted Platform Module settings (required) */
10649 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10650 if (FAILED(rc)) throw rc;
10651
10652 /* NVRAM settings (required) */
10653 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10654 if (FAILED(rc)) throw rc;
10655
10656 /* Recording settings (required) */
10657 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10658 if (FAILED(rc)) throw rc;
10659
10660 /* GraphicsAdapter settings (required) */
10661 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10662 if (FAILED(rc)) throw rc;
10663
10664 /* USB Controller (required) */
10665 data.usbSettings.llUSBControllers.clear();
10666 for (USBControllerList::const_iterator
10667 it = mUSBControllers->begin();
10668 it != mUSBControllers->end();
10669 ++it)
10670 {
10671 ComObjPtr<USBController> ctrl = *it;
10672 settings::USBController settingsCtrl;
10673
10674 settingsCtrl.strName = ctrl->i_getName();
10675 settingsCtrl.enmType = ctrl->i_getControllerType();
10676
10677 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10678 }
10679
10680 /* USB device filters (required) */
10681 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10682 if (FAILED(rc)) throw rc;
10683
10684 /* Network adapters (required) */
10685 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10686 data.llNetworkAdapters.clear();
10687 /* Write out only the nominal number of network adapters for this
10688 * chipset type. Since Machine::commit() hasn't been called there
10689 * may be extra NIC settings in the vector. */
10690 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10691 {
10692 settings::NetworkAdapter nic;
10693 nic.ulSlot = (uint32_t)slot;
10694 /* paranoia check... must not be NULL, but must not crash either. */
10695 if (mNetworkAdapters[slot])
10696 {
10697 if (mNetworkAdapters[slot]->i_hasDefaults())
10698 continue;
10699
10700 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10701 if (FAILED(rc)) throw rc;
10702
10703 data.llNetworkAdapters.push_back(nic);
10704 }
10705 }
10706
10707 /* Serial ports */
10708 data.llSerialPorts.clear();
10709 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10710 {
10711 if (mSerialPorts[slot]->i_hasDefaults())
10712 continue;
10713
10714 settings::SerialPort s;
10715 s.ulSlot = slot;
10716 rc = mSerialPorts[slot]->i_saveSettings(s);
10717 if (FAILED(rc)) return rc;
10718
10719 data.llSerialPorts.push_back(s);
10720 }
10721
10722 /* Parallel ports */
10723 data.llParallelPorts.clear();
10724 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10725 {
10726 if (mParallelPorts[slot]->i_hasDefaults())
10727 continue;
10728
10729 settings::ParallelPort p;
10730 p.ulSlot = slot;
10731 rc = mParallelPorts[slot]->i_saveSettings(p);
10732 if (FAILED(rc)) return rc;
10733
10734 data.llParallelPorts.push_back(p);
10735 }
10736
10737 /* Audio adapter */
10738 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10739 if (FAILED(rc)) return rc;
10740
10741 rc = i_saveStorageControllers(data.storage);
10742 if (FAILED(rc)) return rc;
10743
10744 /* Shared folders */
10745 data.llSharedFolders.clear();
10746 for (HWData::SharedFolderList::const_iterator
10747 it = mHWData->mSharedFolders.begin();
10748 it != mHWData->mSharedFolders.end();
10749 ++it)
10750 {
10751 SharedFolder *pSF = *it;
10752 AutoCaller sfCaller(pSF);
10753 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10754 settings::SharedFolder sf;
10755 sf.strName = pSF->i_getName();
10756 sf.strHostPath = pSF->i_getHostPath();
10757 sf.fWritable = !!pSF->i_isWritable();
10758 sf.fAutoMount = !!pSF->i_isAutoMounted();
10759 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10760
10761 data.llSharedFolders.push_back(sf);
10762 }
10763
10764 // clipboard
10765 data.clipboardMode = mHWData->mClipboardMode;
10766 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10767
10768 // drag'n'drop
10769 data.dndMode = mHWData->mDnDMode;
10770
10771 /* Guest */
10772 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10773
10774 // IO settings
10775 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10776 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10777
10778 /* BandwidthControl (required) */
10779 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10780 if (FAILED(rc)) throw rc;
10781
10782 /* Host PCI devices */
10783 data.pciAttachments.clear();
10784 for (HWData::PCIDeviceAssignmentList::const_iterator
10785 it = mHWData->mPCIDeviceAssignments.begin();
10786 it != mHWData->mPCIDeviceAssignments.end();
10787 ++it)
10788 {
10789 ComObjPtr<PCIDeviceAttachment> pda = *it;
10790 settings::HostPCIDeviceAttachment hpda;
10791
10792 rc = pda->i_saveSettings(hpda);
10793 if (FAILED(rc)) throw rc;
10794
10795 data.pciAttachments.push_back(hpda);
10796 }
10797
10798 // guest properties
10799 data.llGuestProperties.clear();
10800#ifdef VBOX_WITH_GUEST_PROPS
10801 for (HWData::GuestPropertyMap::const_iterator
10802 it = mHWData->mGuestProperties.begin();
10803 it != mHWData->mGuestProperties.end();
10804 ++it)
10805 {
10806 HWData::GuestProperty property = it->second;
10807
10808 /* Remove transient guest properties at shutdown unless we
10809 * are saving state. Note that restoring snapshot intentionally
10810 * keeps them, they will be removed if appropriate once the final
10811 * machine state is set (as crashes etc. need to work). */
10812 if ( ( mData->mMachineState == MachineState_PoweredOff
10813 || mData->mMachineState == MachineState_Aborted
10814 || mData->mMachineState == MachineState_Teleported)
10815 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10816 continue;
10817 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10818 prop.strName = it->first;
10819 prop.strValue = property.strValue;
10820 prop.timestamp = (uint64_t)property.mTimestamp;
10821 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10822 GuestPropWriteFlags(property.mFlags, szFlags);
10823 prop.strFlags = szFlags;
10824
10825 data.llGuestProperties.push_back(prop);
10826 }
10827
10828 /* I presume this doesn't require a backup(). */
10829 mData->mGuestPropertiesModified = FALSE;
10830#endif /* VBOX_WITH_GUEST_PROPS defined */
10831
10832 *pDbg = mHWData->mDebugging;
10833 *pAutostart = mHWData->mAutostart;
10834
10835 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10836 }
10837 catch (std::bad_alloc &)
10838 {
10839 return E_OUTOFMEMORY;
10840 }
10841
10842 AssertComRC(rc);
10843 return rc;
10844}
10845
10846/**
10847 * Saves the storage controller configuration.
10848 *
10849 * @param data storage settings.
10850 */
10851HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10852{
10853 data.llStorageControllers.clear();
10854
10855 for (StorageControllerList::const_iterator
10856 it = mStorageControllers->begin();
10857 it != mStorageControllers->end();
10858 ++it)
10859 {
10860 HRESULT rc;
10861 ComObjPtr<StorageController> pCtl = *it;
10862
10863 settings::StorageController ctl;
10864 ctl.strName = pCtl->i_getName();
10865 ctl.controllerType = pCtl->i_getControllerType();
10866 ctl.storageBus = pCtl->i_getStorageBus();
10867 ctl.ulInstance = pCtl->i_getInstance();
10868 ctl.fBootable = pCtl->i_getBootable();
10869
10870 /* Save the port count. */
10871 ULONG portCount;
10872 rc = pCtl->COMGETTER(PortCount)(&portCount);
10873 ComAssertComRCRet(rc, rc);
10874 ctl.ulPortCount = portCount;
10875
10876 /* Save fUseHostIOCache */
10877 BOOL fUseHostIOCache;
10878 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10879 ComAssertComRCRet(rc, rc);
10880 ctl.fUseHostIOCache = !!fUseHostIOCache;
10881
10882 /* save the devices now. */
10883 rc = i_saveStorageDevices(pCtl, ctl);
10884 ComAssertComRCRet(rc, rc);
10885
10886 data.llStorageControllers.push_back(ctl);
10887 }
10888
10889 return S_OK;
10890}
10891
10892/**
10893 * Saves the hard disk configuration.
10894 */
10895HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10896 settings::StorageController &data)
10897{
10898 MediumAttachmentList atts;
10899
10900 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10901 if (FAILED(rc)) return rc;
10902
10903 data.llAttachedDevices.clear();
10904 for (MediumAttachmentList::const_iterator
10905 it = atts.begin();
10906 it != atts.end();
10907 ++it)
10908 {
10909 settings::AttachedDevice dev;
10910 IMediumAttachment *iA = *it;
10911 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10912 Medium *pMedium = pAttach->i_getMedium();
10913
10914 dev.deviceType = pAttach->i_getType();
10915 dev.lPort = pAttach->i_getPort();
10916 dev.lDevice = pAttach->i_getDevice();
10917 dev.fPassThrough = pAttach->i_getPassthrough();
10918 dev.fHotPluggable = pAttach->i_getHotPluggable();
10919 if (pMedium)
10920 {
10921 if (pMedium->i_isHostDrive())
10922 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10923 else
10924 dev.uuid = pMedium->i_getId();
10925 dev.fTempEject = pAttach->i_getTempEject();
10926 dev.fNonRotational = pAttach->i_getNonRotational();
10927 dev.fDiscard = pAttach->i_getDiscard();
10928 }
10929
10930 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10931
10932 data.llAttachedDevices.push_back(dev);
10933 }
10934
10935 return S_OK;
10936}
10937
10938/**
10939 * Saves machine state settings as defined by aFlags
10940 * (SaveSTS_* values).
10941 *
10942 * @param aFlags Combination of SaveSTS_* flags.
10943 *
10944 * @note Locks objects for writing.
10945 */
10946HRESULT Machine::i_saveStateSettings(int aFlags)
10947{
10948 if (aFlags == 0)
10949 return S_OK;
10950
10951 AutoCaller autoCaller(this);
10952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10953
10954 /* This object's write lock is also necessary to serialize file access
10955 * (prevent concurrent reads and writes) */
10956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10957
10958 HRESULT rc = S_OK;
10959
10960 Assert(mData->pMachineConfigFile);
10961
10962 try
10963 {
10964 if (aFlags & SaveSTS_CurStateModified)
10965 mData->pMachineConfigFile->fCurrentStateModified = true;
10966
10967 if (aFlags & SaveSTS_StateFilePath)
10968 {
10969 if (!mSSData->strStateFilePath.isEmpty())
10970 /* try to make the file name relative to the settings file dir */
10971 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10972 else
10973 mData->pMachineConfigFile->strStateFile.setNull();
10974 }
10975
10976 if (aFlags & SaveSTS_StateTimeStamp)
10977 {
10978 Assert( mData->mMachineState != MachineState_Aborted
10979 || mSSData->strStateFilePath.isEmpty());
10980
10981 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10982
10983 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10984 || mData->mMachineState == MachineState_AbortedSaved);
10985/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10986 }
10987
10988 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10989 }
10990 catch (...)
10991 {
10992 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10993 }
10994
10995 return rc;
10996}
10997
10998/**
10999 * Ensures that the given medium is added to a media registry. If this machine
11000 * was created with 4.0 or later, then the machine registry is used. Otherwise
11001 * the global VirtualBox media registry is used.
11002 *
11003 * Caller must NOT hold machine lock, media tree or any medium locks!
11004 *
11005 * @param pMedium
11006 */
11007void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11008{
11009 /* Paranoia checks: do not hold machine or media tree locks. */
11010 AssertReturnVoid(!isWriteLockOnCurrentThread());
11011 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11012
11013 ComObjPtr<Medium> pBase;
11014 {
11015 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11016 pBase = pMedium->i_getBase();
11017 }
11018
11019 /* Paranoia checks: do not hold medium locks. */
11020 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11021 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11022
11023 // decide which medium registry to use now that the medium is attached:
11024 Guid uuid;
11025 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11026 if (fCanHaveOwnMediaRegistry)
11027 // machine XML is VirtualBox 4.0 or higher:
11028 uuid = i_getId(); // machine UUID
11029 else
11030 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11031
11032 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11033 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11034 if (pMedium->i_addRegistry(uuid))
11035 mParent->i_markRegistryModified(uuid);
11036
11037 /* For more complex hard disk structures it can happen that the base
11038 * medium isn't yet associated with any medium registry. Do that now. */
11039 if (pMedium != pBase)
11040 {
11041 /* Tree lock needed by Medium::addRegistryAll. */
11042 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11043 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11044 {
11045 treeLock.release();
11046 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11047 treeLock.acquire();
11048 }
11049 if (pBase->i_addRegistryAll(uuid))
11050 {
11051 treeLock.release();
11052 mParent->i_markRegistryModified(uuid);
11053 }
11054 }
11055}
11056
11057/**
11058 * Creates differencing hard disks for all normal hard disks attached to this
11059 * machine and a new set of attachments to refer to created disks.
11060 *
11061 * Used when taking a snapshot or when deleting the current state. Gets called
11062 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11063 *
11064 * This method assumes that mMediumAttachments contains the original hard disk
11065 * attachments it needs to create diffs for. On success, these attachments will
11066 * be replaced with the created diffs.
11067 *
11068 * Attachments with non-normal hard disks are left as is.
11069 *
11070 * If @a aOnline is @c false then the original hard disks that require implicit
11071 * diffs will be locked for reading. Otherwise it is assumed that they are
11072 * already locked for writing (when the VM was started). Note that in the latter
11073 * case it is responsibility of the caller to lock the newly created diffs for
11074 * writing if this method succeeds.
11075 *
11076 * @param aProgress Progress object to run (must contain at least as
11077 * many operations left as the number of hard disks
11078 * attached).
11079 * @param aWeight Weight of this operation.
11080 * @param aOnline Whether the VM was online prior to this operation.
11081 *
11082 * @note The progress object is not marked as completed, neither on success nor
11083 * on failure. This is a responsibility of the caller.
11084 *
11085 * @note Locks this object and the media tree for writing.
11086 */
11087HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11088 ULONG aWeight,
11089 bool aOnline)
11090{
11091 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11092
11093 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11094 AssertReturn(!!pProgressControl, E_INVALIDARG);
11095
11096 AutoCaller autoCaller(this);
11097 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11098
11099 AutoMultiWriteLock2 alock(this->lockHandle(),
11100 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11101
11102 /* must be in a protective state because we release the lock below */
11103 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11104 || mData->mMachineState == MachineState_OnlineSnapshotting
11105 || mData->mMachineState == MachineState_LiveSnapshotting
11106 || mData->mMachineState == MachineState_RestoringSnapshot
11107 || mData->mMachineState == MachineState_DeletingSnapshot
11108 , E_FAIL);
11109
11110 HRESULT rc = S_OK;
11111
11112 // use appropriate locked media map (online or offline)
11113 MediumLockListMap lockedMediaOffline;
11114 MediumLockListMap *lockedMediaMap;
11115 if (aOnline)
11116 lockedMediaMap = &mData->mSession.mLockedMedia;
11117 else
11118 lockedMediaMap = &lockedMediaOffline;
11119
11120 try
11121 {
11122 if (!aOnline)
11123 {
11124 /* lock all attached hard disks early to detect "in use"
11125 * situations before creating actual diffs */
11126 for (MediumAttachmentList::const_iterator
11127 it = mMediumAttachments->begin();
11128 it != mMediumAttachments->end();
11129 ++it)
11130 {
11131 MediumAttachment *pAtt = *it;
11132 if (pAtt->i_getType() == DeviceType_HardDisk)
11133 {
11134 Medium *pMedium = pAtt->i_getMedium();
11135 Assert(pMedium);
11136
11137 MediumLockList *pMediumLockList(new MediumLockList());
11138 alock.release();
11139 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11140 NULL /* pToLockWrite */,
11141 false /* fMediumLockWriteAll */,
11142 NULL,
11143 *pMediumLockList);
11144 alock.acquire();
11145 if (FAILED(rc))
11146 {
11147 delete pMediumLockList;
11148 throw rc;
11149 }
11150 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11151 if (FAILED(rc))
11152 {
11153 throw setError(rc,
11154 tr("Collecting locking information for all attached media failed"));
11155 }
11156 }
11157 }
11158
11159 /* Now lock all media. If this fails, nothing is locked. */
11160 alock.release();
11161 rc = lockedMediaMap->Lock();
11162 alock.acquire();
11163 if (FAILED(rc))
11164 {
11165 throw setError(rc,
11166 tr("Locking of attached media failed"));
11167 }
11168 }
11169
11170 /* remember the current list (note that we don't use backup() since
11171 * mMediumAttachments may be already backed up) */
11172 MediumAttachmentList atts = *mMediumAttachments.data();
11173
11174 /* start from scratch */
11175 mMediumAttachments->clear();
11176
11177 /* go through remembered attachments and create diffs for normal hard
11178 * disks and attach them */
11179 for (MediumAttachmentList::const_iterator
11180 it = atts.begin();
11181 it != atts.end();
11182 ++it)
11183 {
11184 MediumAttachment *pAtt = *it;
11185
11186 DeviceType_T devType = pAtt->i_getType();
11187 Medium *pMedium = pAtt->i_getMedium();
11188
11189 if ( devType != DeviceType_HardDisk
11190 || pMedium == NULL
11191 || pMedium->i_getType() != MediumType_Normal)
11192 {
11193 /* copy the attachment as is */
11194
11195 /** @todo the progress object created in SessionMachine::TakeSnaphot
11196 * only expects operations for hard disks. Later other
11197 * device types need to show up in the progress as well. */
11198 if (devType == DeviceType_HardDisk)
11199 {
11200 if (pMedium == NULL)
11201 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11202 aWeight); // weight
11203 else
11204 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11205 pMedium->i_getBase()->i_getName().c_str()).raw(),
11206 aWeight); // weight
11207 }
11208
11209 mMediumAttachments->push_back(pAtt);
11210 continue;
11211 }
11212
11213 /* need a diff */
11214 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11215 pMedium->i_getBase()->i_getName().c_str()).raw(),
11216 aWeight); // weight
11217
11218 Utf8Str strFullSnapshotFolder;
11219 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11220
11221 ComObjPtr<Medium> diff;
11222 diff.createObject();
11223 // store the diff in the same registry as the parent
11224 // (this cannot fail here because we can't create implicit diffs for
11225 // unregistered images)
11226 Guid uuidRegistryParent;
11227 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11228 Assert(fInRegistry); NOREF(fInRegistry);
11229 rc = diff->init(mParent,
11230 pMedium->i_getPreferredDiffFormat(),
11231 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11232 uuidRegistryParent,
11233 DeviceType_HardDisk);
11234 if (FAILED(rc)) throw rc;
11235
11236 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11237 * the push_back? Looks like we're going to release medium with the
11238 * wrong kind of lock (general issue with if we fail anywhere at all)
11239 * and an orphaned VDI in the snapshots folder. */
11240
11241 /* update the appropriate lock list */
11242 MediumLockList *pMediumLockList;
11243 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11244 AssertComRCThrowRC(rc);
11245 if (aOnline)
11246 {
11247 alock.release();
11248 /* The currently attached medium will be read-only, change
11249 * the lock type to read. */
11250 rc = pMediumLockList->Update(pMedium, false);
11251 alock.acquire();
11252 AssertComRCThrowRC(rc);
11253 }
11254
11255 /* release the locks before the potentially lengthy operation */
11256 alock.release();
11257 rc = pMedium->i_createDiffStorage(diff,
11258 pMedium->i_getPreferredDiffVariant(),
11259 pMediumLockList,
11260 NULL /* aProgress */,
11261 true /* aWait */,
11262 false /* aNotify */);
11263 alock.acquire();
11264 if (FAILED(rc)) throw rc;
11265
11266 /* actual lock list update is done in Machine::i_commitMedia */
11267
11268 rc = diff->i_addBackReference(mData->mUuid);
11269 AssertComRCThrowRC(rc);
11270
11271 /* add a new attachment */
11272 ComObjPtr<MediumAttachment> attachment;
11273 attachment.createObject();
11274 rc = attachment->init(this,
11275 diff,
11276 pAtt->i_getControllerName(),
11277 pAtt->i_getPort(),
11278 pAtt->i_getDevice(),
11279 DeviceType_HardDisk,
11280 true /* aImplicit */,
11281 false /* aPassthrough */,
11282 false /* aTempEject */,
11283 pAtt->i_getNonRotational(),
11284 pAtt->i_getDiscard(),
11285 pAtt->i_getHotPluggable(),
11286 pAtt->i_getBandwidthGroup());
11287 if (FAILED(rc)) throw rc;
11288
11289 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11290 AssertComRCThrowRC(rc);
11291 mMediumAttachments->push_back(attachment);
11292 }
11293 }
11294 catch (HRESULT aRC) { rc = aRC; }
11295
11296 /* unlock all hard disks we locked when there is no VM */
11297 if (!aOnline)
11298 {
11299 ErrorInfoKeeper eik;
11300
11301 HRESULT rc1 = lockedMediaMap->Clear();
11302 AssertComRC(rc1);
11303 }
11304
11305 return rc;
11306}
11307
11308/**
11309 * Deletes implicit differencing hard disks created either by
11310 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11311 * mMediumAttachments.
11312 *
11313 * Note that to delete hard disks created by #attachDevice() this method is
11314 * called from #i_rollbackMedia() when the changes are rolled back.
11315 *
11316 * @note Locks this object and the media tree for writing.
11317 */
11318HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11319{
11320 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11321
11322 AutoCaller autoCaller(this);
11323 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11324
11325 AutoMultiWriteLock2 alock(this->lockHandle(),
11326 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11327
11328 /* We absolutely must have backed up state. */
11329 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11330
11331 /* Check if there are any implicitly created diff images. */
11332 bool fImplicitDiffs = false;
11333 for (MediumAttachmentList::const_iterator
11334 it = mMediumAttachments->begin();
11335 it != mMediumAttachments->end();
11336 ++it)
11337 {
11338 const ComObjPtr<MediumAttachment> &pAtt = *it;
11339 if (pAtt->i_isImplicit())
11340 {
11341 fImplicitDiffs = true;
11342 break;
11343 }
11344 }
11345 /* If there is nothing to do, leave early. This saves lots of image locking
11346 * effort. It also avoids a MachineStateChanged event without real reason.
11347 * This is important e.g. when loading a VM config, because there should be
11348 * no events. Otherwise API clients can become thoroughly confused for
11349 * inaccessible VMs (the code for loading VM configs uses this method for
11350 * cleanup if the config makes no sense), as they take such events as an
11351 * indication that the VM is alive, and they would force the VM config to
11352 * be reread, leading to an endless loop. */
11353 if (!fImplicitDiffs)
11354 return S_OK;
11355
11356 HRESULT rc = S_OK;
11357 MachineState_T oldState = mData->mMachineState;
11358
11359 /* will release the lock before the potentially lengthy operation,
11360 * so protect with the special state (unless already protected) */
11361 if ( oldState != MachineState_Snapshotting
11362 && oldState != MachineState_OnlineSnapshotting
11363 && oldState != MachineState_LiveSnapshotting
11364 && oldState != MachineState_RestoringSnapshot
11365 && oldState != MachineState_DeletingSnapshot
11366 && oldState != MachineState_DeletingSnapshotOnline
11367 && oldState != MachineState_DeletingSnapshotPaused
11368 )
11369 i_setMachineState(MachineState_SettingUp);
11370
11371 // use appropriate locked media map (online or offline)
11372 MediumLockListMap lockedMediaOffline;
11373 MediumLockListMap *lockedMediaMap;
11374 if (aOnline)
11375 lockedMediaMap = &mData->mSession.mLockedMedia;
11376 else
11377 lockedMediaMap = &lockedMediaOffline;
11378
11379 try
11380 {
11381 if (!aOnline)
11382 {
11383 /* lock all attached hard disks early to detect "in use"
11384 * situations before deleting actual diffs */
11385 for (MediumAttachmentList::const_iterator
11386 it = mMediumAttachments->begin();
11387 it != mMediumAttachments->end();
11388 ++it)
11389 {
11390 MediumAttachment *pAtt = *it;
11391 if (pAtt->i_getType() == DeviceType_HardDisk)
11392 {
11393 Medium *pMedium = pAtt->i_getMedium();
11394 Assert(pMedium);
11395
11396 MediumLockList *pMediumLockList(new MediumLockList());
11397 alock.release();
11398 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11399 NULL /* pToLockWrite */,
11400 false /* fMediumLockWriteAll */,
11401 NULL,
11402 *pMediumLockList);
11403 alock.acquire();
11404
11405 if (FAILED(rc))
11406 {
11407 delete pMediumLockList;
11408 throw rc;
11409 }
11410
11411 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11412 if (FAILED(rc))
11413 throw rc;
11414 }
11415 }
11416
11417 if (FAILED(rc))
11418 throw rc;
11419 } // end of offline
11420
11421 /* Lock lists are now up to date and include implicitly created media */
11422
11423 /* Go through remembered attachments and delete all implicitly created
11424 * diffs and fix up the attachment information */
11425 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11426 MediumAttachmentList implicitAtts;
11427 for (MediumAttachmentList::const_iterator
11428 it = mMediumAttachments->begin();
11429 it != mMediumAttachments->end();
11430 ++it)
11431 {
11432 ComObjPtr<MediumAttachment> pAtt = *it;
11433 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11434 if (pMedium.isNull())
11435 continue;
11436
11437 // Implicit attachments go on the list for deletion and back references are removed.
11438 if (pAtt->i_isImplicit())
11439 {
11440 /* Deassociate and mark for deletion */
11441 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11442 rc = pMedium->i_removeBackReference(mData->mUuid);
11443 if (FAILED(rc))
11444 throw rc;
11445 implicitAtts.push_back(pAtt);
11446 continue;
11447 }
11448
11449 /* Was this medium attached before? */
11450 if (!i_findAttachment(oldAtts, pMedium))
11451 {
11452 /* no: de-associate */
11453 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11454 rc = pMedium->i_removeBackReference(mData->mUuid);
11455 if (FAILED(rc))
11456 throw rc;
11457 continue;
11458 }
11459 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11460 }
11461
11462 /* If there are implicit attachments to delete, throw away the lock
11463 * map contents (which will unlock all media) since the medium
11464 * attachments will be rolled back. Below we need to completely
11465 * recreate the lock map anyway since it is infinitely complex to
11466 * do this incrementally (would need reconstructing each attachment
11467 * change, which would be extremely hairy). */
11468 if (implicitAtts.size() != 0)
11469 {
11470 ErrorInfoKeeper eik;
11471
11472 HRESULT rc1 = lockedMediaMap->Clear();
11473 AssertComRC(rc1);
11474 }
11475
11476 /* rollback hard disk changes */
11477 mMediumAttachments.rollback();
11478
11479 MultiResult mrc(S_OK);
11480
11481 // Delete unused implicit diffs.
11482 if (implicitAtts.size() != 0)
11483 {
11484 alock.release();
11485
11486 for (MediumAttachmentList::const_iterator
11487 it = implicitAtts.begin();
11488 it != implicitAtts.end();
11489 ++it)
11490 {
11491 // Remove medium associated with this attachment.
11492 ComObjPtr<MediumAttachment> pAtt = *it;
11493 Assert(pAtt);
11494 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11495 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11496 Assert(pMedium);
11497
11498 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11499 // continue on delete failure, just collect error messages
11500 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11501 pMedium->i_getLocationFull().c_str() ));
11502 mrc = rc;
11503 }
11504 // Clear the list of deleted implicit attachments now, while not
11505 // holding the lock, as it will ultimately trigger Medium::uninit()
11506 // calls which assume that the media tree lock isn't held.
11507 implicitAtts.clear();
11508
11509 alock.acquire();
11510
11511 /* if there is a VM recreate media lock map as mentioned above,
11512 * otherwise it is a waste of time and we leave things unlocked */
11513 if (aOnline)
11514 {
11515 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11516 /* must never be NULL, but better safe than sorry */
11517 if (!pMachine.isNull())
11518 {
11519 alock.release();
11520 rc = mData->mSession.mMachine->i_lockMedia();
11521 alock.acquire();
11522 if (FAILED(rc))
11523 throw rc;
11524 }
11525 }
11526 }
11527 }
11528 catch (HRESULT aRC) {rc = aRC;}
11529
11530 if (mData->mMachineState == MachineState_SettingUp)
11531 i_setMachineState(oldState);
11532
11533 /* unlock all hard disks we locked when there is no VM */
11534 if (!aOnline)
11535 {
11536 ErrorInfoKeeper eik;
11537
11538 HRESULT rc1 = lockedMediaMap->Clear();
11539 AssertComRC(rc1);
11540 }
11541
11542 return rc;
11543}
11544
11545
11546/**
11547 * Looks through the given list of media attachments for one with the given parameters
11548 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11549 * can be searched as well if needed.
11550 *
11551 * @param ll
11552 * @param aControllerName
11553 * @param aControllerPort
11554 * @param aDevice
11555 * @return
11556 */
11557MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11558 const Utf8Str &aControllerName,
11559 LONG aControllerPort,
11560 LONG aDevice)
11561{
11562 for (MediumAttachmentList::const_iterator
11563 it = ll.begin();
11564 it != ll.end();
11565 ++it)
11566 {
11567 MediumAttachment *pAttach = *it;
11568 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11569 return pAttach;
11570 }
11571
11572 return NULL;
11573}
11574
11575/**
11576 * Looks through the given list of media attachments for one with the given parameters
11577 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11578 * can be searched as well if needed.
11579 *
11580 * @param ll
11581 * @param pMedium
11582 * @return
11583 */
11584MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11585 ComObjPtr<Medium> pMedium)
11586{
11587 for (MediumAttachmentList::const_iterator
11588 it = ll.begin();
11589 it != ll.end();
11590 ++it)
11591 {
11592 MediumAttachment *pAttach = *it;
11593 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11594 if (pMediumThis == pMedium)
11595 return pAttach;
11596 }
11597
11598 return NULL;
11599}
11600
11601/**
11602 * Looks through the given list of media attachments for one with the given parameters
11603 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11604 * can be searched as well if needed.
11605 *
11606 * @param ll
11607 * @param id
11608 * @return
11609 */
11610MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11611 Guid &id)
11612{
11613 for (MediumAttachmentList::const_iterator
11614 it = ll.begin();
11615 it != ll.end();
11616 ++it)
11617 {
11618 MediumAttachment *pAttach = *it;
11619 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11620 if (pMediumThis->i_getId() == id)
11621 return pAttach;
11622 }
11623
11624 return NULL;
11625}
11626
11627/**
11628 * Main implementation for Machine::DetachDevice. This also gets called
11629 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11630 *
11631 * @param pAttach Medium attachment to detach.
11632 * @param writeLock Machine write lock which the caller must have locked once.
11633 * This may be released temporarily in here.
11634 * @param pSnapshot If NULL, then the detachment is for the current machine.
11635 * Otherwise this is for a SnapshotMachine, and this must be
11636 * its snapshot.
11637 * @return
11638 */
11639HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11640 AutoWriteLock &writeLock,
11641 Snapshot *pSnapshot)
11642{
11643 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11644 DeviceType_T mediumType = pAttach->i_getType();
11645
11646 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11647
11648 if (pAttach->i_isImplicit())
11649 {
11650 /* attempt to implicitly delete the implicitly created diff */
11651
11652 /// @todo move the implicit flag from MediumAttachment to Medium
11653 /// and forbid any hard disk operation when it is implicit. Or maybe
11654 /// a special media state for it to make it even more simple.
11655
11656 Assert(mMediumAttachments.isBackedUp());
11657
11658 /* will release the lock before the potentially lengthy operation, so
11659 * protect with the special state */
11660 MachineState_T oldState = mData->mMachineState;
11661 i_setMachineState(MachineState_SettingUp);
11662
11663 writeLock.release();
11664
11665 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11666 true /*aWait*/,
11667 false /*aNotify*/);
11668
11669 writeLock.acquire();
11670
11671 i_setMachineState(oldState);
11672
11673 if (FAILED(rc)) return rc;
11674 }
11675
11676 i_setModified(IsModified_Storage);
11677 mMediumAttachments.backup();
11678 mMediumAttachments->remove(pAttach);
11679
11680 if (!oldmedium.isNull())
11681 {
11682 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11683 if (pSnapshot)
11684 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11685 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11686 else if (mediumType != DeviceType_HardDisk)
11687 oldmedium->i_removeBackReference(mData->mUuid);
11688 }
11689
11690 return S_OK;
11691}
11692
11693/**
11694 * Goes thru all media of the given list and
11695 *
11696 * 1) calls i_detachDevice() on each of them for this machine and
11697 * 2) adds all Medium objects found in the process to the given list,
11698 * depending on cleanupMode.
11699 *
11700 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11701 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11702 * media to the list.
11703 *
11704 * This gets called from Machine::Unregister, both for the actual Machine and
11705 * the SnapshotMachine objects that might be found in the snapshots.
11706 *
11707 * Requires caller and locking. The machine lock must be passed in because it
11708 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11709 *
11710 * @param writeLock Machine lock from top-level caller; this gets passed to
11711 * i_detachDevice.
11712 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11713 * object if called for a SnapshotMachine.
11714 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11715 * added to llMedia; if Full, then all media get added;
11716 * otherwise no media get added.
11717 * @param llMedia Caller's list to receive Medium objects which got detached so
11718 * caller can close() them, depending on cleanupMode.
11719 * @return
11720 */
11721HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11722 Snapshot *pSnapshot,
11723 CleanupMode_T cleanupMode,
11724 MediaList &llMedia)
11725{
11726 Assert(isWriteLockOnCurrentThread());
11727
11728 HRESULT rc;
11729
11730 // make a temporary list because i_detachDevice invalidates iterators into
11731 // mMediumAttachments
11732 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11733
11734 for (MediumAttachmentList::iterator
11735 it = llAttachments2.begin();
11736 it != llAttachments2.end();
11737 ++it)
11738 {
11739 ComObjPtr<MediumAttachment> &pAttach = *it;
11740 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11741
11742 if (!pMedium.isNull())
11743 {
11744 AutoCaller mac(pMedium);
11745 if (FAILED(mac.rc())) return mac.rc();
11746 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11747 DeviceType_T devType = pMedium->i_getDeviceType();
11748 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11749 && devType == DeviceType_HardDisk)
11750 || (cleanupMode == CleanupMode_Full)
11751 )
11752 {
11753 llMedia.push_back(pMedium);
11754 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11755 /* Not allowed to keep this lock as below we need the parent
11756 * medium lock, and the lock order is parent to child. */
11757 lock.release();
11758 /*
11759 * Search for medias which are not attached to any machine, but
11760 * in the chain to an attached disk. Mediums are only consided
11761 * if they are:
11762 * - have only one child
11763 * - no references to any machines
11764 * - are of normal medium type
11765 */
11766 while (!pParent.isNull())
11767 {
11768 AutoCaller mac1(pParent);
11769 if (FAILED(mac1.rc())) return mac1.rc();
11770 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11771 if (pParent->i_getChildren().size() == 1)
11772 {
11773 if ( pParent->i_getMachineBackRefCount() == 0
11774 && pParent->i_getType() == MediumType_Normal
11775 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11776 llMedia.push_back(pParent);
11777 }
11778 else
11779 break;
11780 pParent = pParent->i_getParent();
11781 }
11782 }
11783 }
11784
11785 // real machine: then we need to use the proper method
11786 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11787
11788 if (FAILED(rc))
11789 return rc;
11790 }
11791
11792 return S_OK;
11793}
11794
11795/**
11796 * Perform deferred hard disk detachments.
11797 *
11798 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11799 * changed (not backed up).
11800 *
11801 * If @a aOnline is @c true then this method will also unlock the old hard
11802 * disks for which the new implicit diffs were created and will lock these new
11803 * diffs for writing.
11804 *
11805 * @param aOnline Whether the VM was online prior to this operation.
11806 *
11807 * @note Locks this object for writing!
11808 */
11809void Machine::i_commitMedia(bool aOnline /*= false*/)
11810{
11811 AutoCaller autoCaller(this);
11812 AssertComRCReturnVoid(autoCaller.rc());
11813
11814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11815
11816 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11817
11818 HRESULT rc = S_OK;
11819
11820 /* no attach/detach operations -- nothing to do */
11821 if (!mMediumAttachments.isBackedUp())
11822 return;
11823
11824 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11825 bool fMediaNeedsLocking = false;
11826
11827 /* enumerate new attachments */
11828 for (MediumAttachmentList::const_iterator
11829 it = mMediumAttachments->begin();
11830 it != mMediumAttachments->end();
11831 ++it)
11832 {
11833 MediumAttachment *pAttach = *it;
11834
11835 pAttach->i_commit();
11836
11837 Medium *pMedium = pAttach->i_getMedium();
11838 bool fImplicit = pAttach->i_isImplicit();
11839
11840 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11841 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11842 fImplicit));
11843
11844 /** @todo convert all this Machine-based voodoo to MediumAttachment
11845 * based commit logic. */
11846 if (fImplicit)
11847 {
11848 /* convert implicit attachment to normal */
11849 pAttach->i_setImplicit(false);
11850
11851 if ( aOnline
11852 && pMedium
11853 && pAttach->i_getType() == DeviceType_HardDisk
11854 )
11855 {
11856 /* update the appropriate lock list */
11857 MediumLockList *pMediumLockList;
11858 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11859 AssertComRC(rc);
11860 if (pMediumLockList)
11861 {
11862 /* unlock if there's a need to change the locking */
11863 if (!fMediaNeedsLocking)
11864 {
11865 rc = mData->mSession.mLockedMedia.Unlock();
11866 AssertComRC(rc);
11867 fMediaNeedsLocking = true;
11868 }
11869 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11870 AssertComRC(rc);
11871 rc = pMediumLockList->Append(pMedium, true);
11872 AssertComRC(rc);
11873 }
11874 }
11875
11876 continue;
11877 }
11878
11879 if (pMedium)
11880 {
11881 /* was this medium attached before? */
11882 for (MediumAttachmentList::iterator
11883 oldIt = oldAtts.begin();
11884 oldIt != oldAtts.end();
11885 ++oldIt)
11886 {
11887 MediumAttachment *pOldAttach = *oldIt;
11888 if (pOldAttach->i_getMedium() == pMedium)
11889 {
11890 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11891
11892 /* yes: remove from old to avoid de-association */
11893 oldAtts.erase(oldIt);
11894 break;
11895 }
11896 }
11897 }
11898 }
11899
11900 /* enumerate remaining old attachments and de-associate from the
11901 * current machine state */
11902 for (MediumAttachmentList::const_iterator
11903 it = oldAtts.begin();
11904 it != oldAtts.end();
11905 ++it)
11906 {
11907 MediumAttachment *pAttach = *it;
11908 Medium *pMedium = pAttach->i_getMedium();
11909
11910 /* Detach only hard disks, since DVD/floppy media is detached
11911 * instantly in MountMedium. */
11912 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11913 {
11914 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11915
11916 /* now de-associate from the current machine state */
11917 rc = pMedium->i_removeBackReference(mData->mUuid);
11918 AssertComRC(rc);
11919
11920 if (aOnline)
11921 {
11922 /* unlock since medium is not used anymore */
11923 MediumLockList *pMediumLockList;
11924 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11925 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11926 {
11927 /* this happens for online snapshots, there the attachment
11928 * is changing, but only to a diff image created under
11929 * the old one, so there is no separate lock list */
11930 Assert(!pMediumLockList);
11931 }
11932 else
11933 {
11934 AssertComRC(rc);
11935 if (pMediumLockList)
11936 {
11937 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11938 AssertComRC(rc);
11939 }
11940 }
11941 }
11942 }
11943 }
11944
11945 /* take media locks again so that the locking state is consistent */
11946 if (fMediaNeedsLocking)
11947 {
11948 Assert(aOnline);
11949 rc = mData->mSession.mLockedMedia.Lock();
11950 AssertComRC(rc);
11951 }
11952
11953 /* commit the hard disk changes */
11954 mMediumAttachments.commit();
11955
11956 if (i_isSessionMachine())
11957 {
11958 /*
11959 * Update the parent machine to point to the new owner.
11960 * This is necessary because the stored parent will point to the
11961 * session machine otherwise and cause crashes or errors later
11962 * when the session machine gets invalid.
11963 */
11964 /** @todo Change the MediumAttachment class to behave like any other
11965 * class in this regard by creating peer MediumAttachment
11966 * objects for session machines and share the data with the peer
11967 * machine.
11968 */
11969 for (MediumAttachmentList::const_iterator
11970 it = mMediumAttachments->begin();
11971 it != mMediumAttachments->end();
11972 ++it)
11973 (*it)->i_updateParentMachine(mPeer);
11974
11975 /* attach new data to the primary machine and reshare it */
11976 mPeer->mMediumAttachments.attach(mMediumAttachments);
11977 }
11978
11979 return;
11980}
11981
11982/**
11983 * Perform deferred deletion of implicitly created diffs.
11984 *
11985 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11986 * changed (not backed up).
11987 *
11988 * @note Locks this object for writing!
11989 */
11990void Machine::i_rollbackMedia()
11991{
11992 AutoCaller autoCaller(this);
11993 AssertComRCReturnVoid(autoCaller.rc());
11994
11995 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11996 LogFlowThisFunc(("Entering rollbackMedia\n"));
11997
11998 HRESULT rc = S_OK;
11999
12000 /* no attach/detach operations -- nothing to do */
12001 if (!mMediumAttachments.isBackedUp())
12002 return;
12003
12004 /* enumerate new attachments */
12005 for (MediumAttachmentList::const_iterator
12006 it = mMediumAttachments->begin();
12007 it != mMediumAttachments->end();
12008 ++it)
12009 {
12010 MediumAttachment *pAttach = *it;
12011 /* Fix up the backrefs for DVD/floppy media. */
12012 if (pAttach->i_getType() != DeviceType_HardDisk)
12013 {
12014 Medium *pMedium = pAttach->i_getMedium();
12015 if (pMedium)
12016 {
12017 rc = pMedium->i_removeBackReference(mData->mUuid);
12018 AssertComRC(rc);
12019 }
12020 }
12021
12022 (*it)->i_rollback();
12023
12024 pAttach = *it;
12025 /* Fix up the backrefs for DVD/floppy media. */
12026 if (pAttach->i_getType() != DeviceType_HardDisk)
12027 {
12028 Medium *pMedium = pAttach->i_getMedium();
12029 if (pMedium)
12030 {
12031 rc = pMedium->i_addBackReference(mData->mUuid);
12032 AssertComRC(rc);
12033 }
12034 }
12035 }
12036
12037 /** @todo convert all this Machine-based voodoo to MediumAttachment
12038 * based rollback logic. */
12039 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12040
12041 return;
12042}
12043
12044/**
12045 * Returns true if the settings file is located in the directory named exactly
12046 * as the machine; this means, among other things, that the machine directory
12047 * should be auto-renamed.
12048 *
12049 * @param aSettingsDir if not NULL, the full machine settings file directory
12050 * name will be assigned there.
12051 *
12052 * @note Doesn't lock anything.
12053 * @note Not thread safe (must be called from this object's lock).
12054 */
12055bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12056{
12057 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12058 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12059 if (aSettingsDir)
12060 *aSettingsDir = strMachineDirName;
12061 strMachineDirName.stripPath(); // vmname
12062 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12063 strConfigFileOnly.stripPath() // vmname.vbox
12064 .stripSuffix(); // vmname
12065 /** @todo hack, make somehow use of ComposeMachineFilename */
12066 if (mUserData->s.fDirectoryIncludesUUID)
12067 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12068
12069 AssertReturn(!strMachineDirName.isEmpty(), false);
12070 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12071
12072 return strMachineDirName == strConfigFileOnly;
12073}
12074
12075/**
12076 * Discards all changes to machine settings.
12077 *
12078 * @param aNotify Whether to notify the direct session about changes or not.
12079 *
12080 * @note Locks objects for writing!
12081 */
12082void Machine::i_rollback(bool aNotify)
12083{
12084 AutoCaller autoCaller(this);
12085 AssertComRCReturn(autoCaller.rc(), (void)0);
12086
12087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12088
12089 if (!mStorageControllers.isNull())
12090 {
12091 if (mStorageControllers.isBackedUp())
12092 {
12093 /* unitialize all new devices (absent in the backed up list). */
12094 StorageControllerList *backedList = mStorageControllers.backedUpData();
12095 for (StorageControllerList::const_iterator
12096 it = mStorageControllers->begin();
12097 it != mStorageControllers->end();
12098 ++it)
12099 {
12100 if ( std::find(backedList->begin(), backedList->end(), *it)
12101 == backedList->end()
12102 )
12103 {
12104 (*it)->uninit();
12105 }
12106 }
12107
12108 /* restore the list */
12109 mStorageControllers.rollback();
12110 }
12111
12112 /* rollback any changes to devices after restoring the list */
12113 if (mData->flModifications & IsModified_Storage)
12114 {
12115 for (StorageControllerList::const_iterator
12116 it = mStorageControllers->begin();
12117 it != mStorageControllers->end();
12118 ++it)
12119 {
12120 (*it)->i_rollback();
12121 }
12122 }
12123 }
12124
12125 if (!mUSBControllers.isNull())
12126 {
12127 if (mUSBControllers.isBackedUp())
12128 {
12129 /* unitialize all new devices (absent in the backed up list). */
12130 USBControllerList *backedList = mUSBControllers.backedUpData();
12131 for (USBControllerList::const_iterator
12132 it = mUSBControllers->begin();
12133 it != mUSBControllers->end();
12134 ++it)
12135 {
12136 if ( std::find(backedList->begin(), backedList->end(), *it)
12137 == backedList->end()
12138 )
12139 {
12140 (*it)->uninit();
12141 }
12142 }
12143
12144 /* restore the list */
12145 mUSBControllers.rollback();
12146 }
12147
12148 /* rollback any changes to devices after restoring the list */
12149 if (mData->flModifications & IsModified_USB)
12150 {
12151 for (USBControllerList::const_iterator
12152 it = mUSBControllers->begin();
12153 it != mUSBControllers->end();
12154 ++it)
12155 {
12156 (*it)->i_rollback();
12157 }
12158 }
12159 }
12160
12161 mUserData.rollback();
12162
12163 mHWData.rollback();
12164
12165 if (mData->flModifications & IsModified_Storage)
12166 i_rollbackMedia();
12167
12168 if (mBIOSSettings)
12169 mBIOSSettings->i_rollback();
12170
12171 if (mTrustedPlatformModule)
12172 mTrustedPlatformModule->i_rollback();
12173
12174 if (mNvramStore)
12175 mNvramStore->i_rollback();
12176
12177 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12178 mRecordingSettings->i_rollback();
12179
12180 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12181 mGraphicsAdapter->i_rollback();
12182
12183 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12184 mVRDEServer->i_rollback();
12185
12186 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
12187 mAudioAdapter->i_rollback();
12188
12189 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12190 mUSBDeviceFilters->i_rollback();
12191
12192 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12193 mBandwidthControl->i_rollback();
12194
12195 if (!mHWData.isNull())
12196 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12197 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12198 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12199 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12200
12201 if (mData->flModifications & IsModified_NetworkAdapters)
12202 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12203 if ( mNetworkAdapters[slot]
12204 && mNetworkAdapters[slot]->i_isModified())
12205 {
12206 mNetworkAdapters[slot]->i_rollback();
12207 networkAdapters[slot] = mNetworkAdapters[slot];
12208 }
12209
12210 if (mData->flModifications & IsModified_SerialPorts)
12211 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12212 if ( mSerialPorts[slot]
12213 && mSerialPorts[slot]->i_isModified())
12214 {
12215 mSerialPorts[slot]->i_rollback();
12216 serialPorts[slot] = mSerialPorts[slot];
12217 }
12218
12219 if (mData->flModifications & IsModified_ParallelPorts)
12220 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12221 if ( mParallelPorts[slot]
12222 && mParallelPorts[slot]->i_isModified())
12223 {
12224 mParallelPorts[slot]->i_rollback();
12225 parallelPorts[slot] = mParallelPorts[slot];
12226 }
12227
12228 if (aNotify)
12229 {
12230 /* inform the direct session about changes */
12231
12232 ComObjPtr<Machine> that = this;
12233 uint32_t flModifications = mData->flModifications;
12234 alock.release();
12235
12236 if (flModifications & IsModified_SharedFolders)
12237 that->i_onSharedFolderChange();
12238
12239 if (flModifications & IsModified_VRDEServer)
12240 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12241 if (flModifications & IsModified_USB)
12242 that->i_onUSBControllerChange();
12243
12244 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12245 if (networkAdapters[slot])
12246 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12247 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12248 if (serialPorts[slot])
12249 that->i_onSerialPortChange(serialPorts[slot]);
12250 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12251 if (parallelPorts[slot])
12252 that->i_onParallelPortChange(parallelPorts[slot]);
12253
12254 if (flModifications & IsModified_Storage)
12255 {
12256 for (StorageControllerList::const_iterator
12257 it = mStorageControllers->begin();
12258 it != mStorageControllers->end();
12259 ++it)
12260 {
12261 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12262 }
12263 }
12264
12265
12266#if 0
12267 if (flModifications & IsModified_BandwidthControl)
12268 that->onBandwidthControlChange();
12269#endif
12270 }
12271}
12272
12273/**
12274 * Commits all the changes to machine settings.
12275 *
12276 * Note that this operation is supposed to never fail.
12277 *
12278 * @note Locks this object and children for writing.
12279 */
12280void Machine::i_commit()
12281{
12282 AutoCaller autoCaller(this);
12283 AssertComRCReturnVoid(autoCaller.rc());
12284
12285 AutoCaller peerCaller(mPeer);
12286 AssertComRCReturnVoid(peerCaller.rc());
12287
12288 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12289
12290 /*
12291 * use safe commit to ensure Snapshot machines (that share mUserData)
12292 * will still refer to a valid memory location
12293 */
12294 mUserData.commitCopy();
12295
12296 mHWData.commit();
12297
12298 if (mMediumAttachments.isBackedUp())
12299 i_commitMedia(Global::IsOnline(mData->mMachineState));
12300
12301 mBIOSSettings->i_commit();
12302 mTrustedPlatformModule->i_commit();
12303 mNvramStore->i_commit();
12304 mRecordingSettings->i_commit();
12305 mGraphicsAdapter->i_commit();
12306 mVRDEServer->i_commit();
12307 mAudioAdapter->i_commit();
12308 mUSBDeviceFilters->i_commit();
12309 mBandwidthControl->i_commit();
12310
12311 /* Since mNetworkAdapters is a list which might have been changed (resized)
12312 * without using the Backupable<> template we need to handle the copying
12313 * of the list entries manually, including the creation of peers for the
12314 * new objects. */
12315 bool commitNetworkAdapters = false;
12316 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12317 if (mPeer)
12318 {
12319 /* commit everything, even the ones which will go away */
12320 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12321 mNetworkAdapters[slot]->i_commit();
12322 /* copy over the new entries, creating a peer and uninit the original */
12323 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12324 for (size_t slot = 0; slot < newSize; slot++)
12325 {
12326 /* look if this adapter has a peer device */
12327 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12328 if (!peer)
12329 {
12330 /* no peer means the adapter is a newly created one;
12331 * create a peer owning data this data share it with */
12332 peer.createObject();
12333 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12334 }
12335 mPeer->mNetworkAdapters[slot] = peer;
12336 }
12337 /* uninit any no longer needed network adapters */
12338 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12339 mNetworkAdapters[slot]->uninit();
12340 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12341 {
12342 if (mPeer->mNetworkAdapters[slot])
12343 mPeer->mNetworkAdapters[slot]->uninit();
12344 }
12345 /* Keep the original network adapter count until this point, so that
12346 * discarding a chipset type change will not lose settings. */
12347 mNetworkAdapters.resize(newSize);
12348 mPeer->mNetworkAdapters.resize(newSize);
12349 }
12350 else
12351 {
12352 /* we have no peer (our parent is the newly created machine);
12353 * just commit changes to the network adapters */
12354 commitNetworkAdapters = true;
12355 }
12356 if (commitNetworkAdapters)
12357 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12358 mNetworkAdapters[slot]->i_commit();
12359
12360 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12361 mSerialPorts[slot]->i_commit();
12362 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12363 mParallelPorts[slot]->i_commit();
12364
12365 bool commitStorageControllers = false;
12366
12367 if (mStorageControllers.isBackedUp())
12368 {
12369 mStorageControllers.commit();
12370
12371 if (mPeer)
12372 {
12373 /* Commit all changes to new controllers (this will reshare data with
12374 * peers for those who have peers) */
12375 StorageControllerList *newList = new StorageControllerList();
12376 for (StorageControllerList::const_iterator
12377 it = mStorageControllers->begin();
12378 it != mStorageControllers->end();
12379 ++it)
12380 {
12381 (*it)->i_commit();
12382
12383 /* look if this controller has a peer device */
12384 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12385 if (!peer)
12386 {
12387 /* no peer means the device is a newly created one;
12388 * create a peer owning data this device share it with */
12389 peer.createObject();
12390 peer->init(mPeer, *it, true /* aReshare */);
12391 }
12392 else
12393 {
12394 /* remove peer from the old list */
12395 mPeer->mStorageControllers->remove(peer);
12396 }
12397 /* and add it to the new list */
12398 newList->push_back(peer);
12399 }
12400
12401 /* uninit old peer's controllers that are left */
12402 for (StorageControllerList::const_iterator
12403 it = mPeer->mStorageControllers->begin();
12404 it != mPeer->mStorageControllers->end();
12405 ++it)
12406 {
12407 (*it)->uninit();
12408 }
12409
12410 /* attach new list of controllers to our peer */
12411 mPeer->mStorageControllers.attach(newList);
12412 }
12413 else
12414 {
12415 /* we have no peer (our parent is the newly created machine);
12416 * just commit changes to devices */
12417 commitStorageControllers = true;
12418 }
12419 }
12420 else
12421 {
12422 /* the list of controllers itself is not changed,
12423 * just commit changes to controllers themselves */
12424 commitStorageControllers = true;
12425 }
12426
12427 if (commitStorageControllers)
12428 {
12429 for (StorageControllerList::const_iterator
12430 it = mStorageControllers->begin();
12431 it != mStorageControllers->end();
12432 ++it)
12433 {
12434 (*it)->i_commit();
12435 }
12436 }
12437
12438 bool commitUSBControllers = false;
12439
12440 if (mUSBControllers.isBackedUp())
12441 {
12442 mUSBControllers.commit();
12443
12444 if (mPeer)
12445 {
12446 /* Commit all changes to new controllers (this will reshare data with
12447 * peers for those who have peers) */
12448 USBControllerList *newList = new USBControllerList();
12449 for (USBControllerList::const_iterator
12450 it = mUSBControllers->begin();
12451 it != mUSBControllers->end();
12452 ++it)
12453 {
12454 (*it)->i_commit();
12455
12456 /* look if this controller has a peer device */
12457 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12458 if (!peer)
12459 {
12460 /* no peer means the device is a newly created one;
12461 * create a peer owning data this device share it with */
12462 peer.createObject();
12463 peer->init(mPeer, *it, true /* aReshare */);
12464 }
12465 else
12466 {
12467 /* remove peer from the old list */
12468 mPeer->mUSBControllers->remove(peer);
12469 }
12470 /* and add it to the new list */
12471 newList->push_back(peer);
12472 }
12473
12474 /* uninit old peer's controllers that are left */
12475 for (USBControllerList::const_iterator
12476 it = mPeer->mUSBControllers->begin();
12477 it != mPeer->mUSBControllers->end();
12478 ++it)
12479 {
12480 (*it)->uninit();
12481 }
12482
12483 /* attach new list of controllers to our peer */
12484 mPeer->mUSBControllers.attach(newList);
12485 }
12486 else
12487 {
12488 /* we have no peer (our parent is the newly created machine);
12489 * just commit changes to devices */
12490 commitUSBControllers = true;
12491 }
12492 }
12493 else
12494 {
12495 /* the list of controllers itself is not changed,
12496 * just commit changes to controllers themselves */
12497 commitUSBControllers = true;
12498 }
12499
12500 if (commitUSBControllers)
12501 {
12502 for (USBControllerList::const_iterator
12503 it = mUSBControllers->begin();
12504 it != mUSBControllers->end();
12505 ++it)
12506 {
12507 (*it)->i_commit();
12508 }
12509 }
12510
12511 if (i_isSessionMachine())
12512 {
12513 /* attach new data to the primary machine and reshare it */
12514 mPeer->mUserData.attach(mUserData);
12515 mPeer->mHWData.attach(mHWData);
12516 /* mmMediumAttachments is reshared by fixupMedia */
12517 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12518 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12519 }
12520}
12521
12522/**
12523 * Copies all the hardware data from the given machine.
12524 *
12525 * Currently, only called when the VM is being restored from a snapshot. In
12526 * particular, this implies that the VM is not running during this method's
12527 * call.
12528 *
12529 * @note This method must be called from under this object's lock.
12530 *
12531 * @note This method doesn't call #i_commit(), so all data remains backed up and
12532 * unsaved.
12533 */
12534void Machine::i_copyFrom(Machine *aThat)
12535{
12536 AssertReturnVoid(!i_isSnapshotMachine());
12537 AssertReturnVoid(aThat->i_isSnapshotMachine());
12538
12539 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12540
12541 mHWData.assignCopy(aThat->mHWData);
12542
12543 // create copies of all shared folders (mHWData after attaching a copy
12544 // contains just references to original objects)
12545 for (HWData::SharedFolderList::iterator
12546 it = mHWData->mSharedFolders.begin();
12547 it != mHWData->mSharedFolders.end();
12548 ++it)
12549 {
12550 ComObjPtr<SharedFolder> folder;
12551 folder.createObject();
12552 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12553 AssertComRC(rc);
12554 *it = folder;
12555 }
12556
12557 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12558 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12559 mNvramStore->i_copyFrom(aThat->mNvramStore);
12560 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12561 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12562 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12563 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12564 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12565 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12566
12567 /* create private copies of all controllers */
12568 mStorageControllers.backup();
12569 mStorageControllers->clear();
12570 for (StorageControllerList::const_iterator
12571 it = aThat->mStorageControllers->begin();
12572 it != aThat->mStorageControllers->end();
12573 ++it)
12574 {
12575 ComObjPtr<StorageController> ctrl;
12576 ctrl.createObject();
12577 ctrl->initCopy(this, *it);
12578 mStorageControllers->push_back(ctrl);
12579 }
12580
12581 /* create private copies of all USB controllers */
12582 mUSBControllers.backup();
12583 mUSBControllers->clear();
12584 for (USBControllerList::const_iterator
12585 it = aThat->mUSBControllers->begin();
12586 it != aThat->mUSBControllers->end();
12587 ++it)
12588 {
12589 ComObjPtr<USBController> ctrl;
12590 ctrl.createObject();
12591 ctrl->initCopy(this, *it);
12592 mUSBControllers->push_back(ctrl);
12593 }
12594
12595 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12596 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12597 {
12598 if (mNetworkAdapters[slot].isNotNull())
12599 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12600 else
12601 {
12602 unconst(mNetworkAdapters[slot]).createObject();
12603 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12604 }
12605 }
12606 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12607 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12608 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12609 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12610}
12611
12612/**
12613 * Returns whether the given storage controller is hotplug capable.
12614 *
12615 * @returns true if the controller supports hotplugging
12616 * false otherwise.
12617 * @param enmCtrlType The controller type to check for.
12618 */
12619bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12620{
12621 ComPtr<ISystemProperties> systemProperties;
12622 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12623 if (FAILED(rc))
12624 return false;
12625
12626 BOOL aHotplugCapable = FALSE;
12627 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12628
12629 return RT_BOOL(aHotplugCapable);
12630}
12631
12632#ifdef VBOX_WITH_RESOURCE_USAGE_API
12633
12634void Machine::i_getDiskList(MediaList &list)
12635{
12636 for (MediumAttachmentList::const_iterator
12637 it = mMediumAttachments->begin();
12638 it != mMediumAttachments->end();
12639 ++it)
12640 {
12641 MediumAttachment *pAttach = *it;
12642 /* just in case */
12643 AssertContinue(pAttach);
12644
12645 AutoCaller localAutoCallerA(pAttach);
12646 if (FAILED(localAutoCallerA.rc())) continue;
12647
12648 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12649
12650 if (pAttach->i_getType() == DeviceType_HardDisk)
12651 list.push_back(pAttach->i_getMedium());
12652 }
12653}
12654
12655void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12656{
12657 AssertReturnVoid(isWriteLockOnCurrentThread());
12658 AssertPtrReturnVoid(aCollector);
12659
12660 pm::CollectorHAL *hal = aCollector->getHAL();
12661 /* Create sub metrics */
12662 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12663 "Percentage of processor time spent in user mode by the VM process.");
12664 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12665 "Percentage of processor time spent in kernel mode by the VM process.");
12666 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12667 "Size of resident portion of VM process in memory.");
12668 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12669 "Actual size of all VM disks combined.");
12670 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12671 "Network receive rate.");
12672 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12673 "Network transmit rate.");
12674 /* Create and register base metrics */
12675 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12676 cpuLoadUser, cpuLoadKernel);
12677 aCollector->registerBaseMetric(cpuLoad);
12678 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12679 ramUsageUsed);
12680 aCollector->registerBaseMetric(ramUsage);
12681 MediaList disks;
12682 i_getDiskList(disks);
12683 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12684 diskUsageUsed);
12685 aCollector->registerBaseMetric(diskUsage);
12686
12687 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12688 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12689 new pm::AggregateAvg()));
12690 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12691 new pm::AggregateMin()));
12692 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12693 new pm::AggregateMax()));
12694 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12695 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12696 new pm::AggregateAvg()));
12697 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12698 new pm::AggregateMin()));
12699 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12700 new pm::AggregateMax()));
12701
12702 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12703 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12704 new pm::AggregateAvg()));
12705 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12706 new pm::AggregateMin()));
12707 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12708 new pm::AggregateMax()));
12709
12710 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12711 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12712 new pm::AggregateAvg()));
12713 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12714 new pm::AggregateMin()));
12715 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12716 new pm::AggregateMax()));
12717
12718
12719 /* Guest metrics collector */
12720 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12721 aCollector->registerGuest(mCollectorGuest);
12722 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12723
12724 /* Create sub metrics */
12725 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12726 "Percentage of processor time spent in user mode as seen by the guest.");
12727 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12728 "Percentage of processor time spent in kernel mode as seen by the guest.");
12729 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12730 "Percentage of processor time spent idling as seen by the guest.");
12731
12732 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12733 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12734 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12735 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12736 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12737 pm::SubMetric *guestMemCache = new pm::SubMetric(
12738 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12739
12740 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12741 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12742
12743 /* Create and register base metrics */
12744 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12745 machineNetRx, machineNetTx);
12746 aCollector->registerBaseMetric(machineNetRate);
12747
12748 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12749 guestLoadUser, guestLoadKernel, guestLoadIdle);
12750 aCollector->registerBaseMetric(guestCpuLoad);
12751
12752 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12753 guestMemTotal, guestMemFree,
12754 guestMemBalloon, guestMemShared,
12755 guestMemCache, guestPagedTotal);
12756 aCollector->registerBaseMetric(guestCpuMem);
12757
12758 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12759 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12760 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12761 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12762
12763 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12764 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12765 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12766 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12767
12768 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12769 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12770 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12771 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12772
12773 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12774 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12775 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12776 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12777
12778 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12779 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12780 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12781 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12782
12783 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12784 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12785 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12786 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12787
12788 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12789 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12790 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12791 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12792
12793 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12794 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12795 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12796 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12797
12798 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12799 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12800 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12801 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12802
12803 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12804 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12805 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12806 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12807
12808 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12809 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12810 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12811 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12812}
12813
12814void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12815{
12816 AssertReturnVoid(isWriteLockOnCurrentThread());
12817
12818 if (aCollector)
12819 {
12820 aCollector->unregisterMetricsFor(aMachine);
12821 aCollector->unregisterBaseMetricsFor(aMachine);
12822 }
12823}
12824
12825#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12826
12827
12828////////////////////////////////////////////////////////////////////////////////
12829
12830DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12831
12832HRESULT SessionMachine::FinalConstruct()
12833{
12834 LogFlowThisFunc(("\n"));
12835
12836 mClientToken = NULL;
12837
12838 return BaseFinalConstruct();
12839}
12840
12841void SessionMachine::FinalRelease()
12842{
12843 LogFlowThisFunc(("\n"));
12844
12845 Assert(!mClientToken);
12846 /* paranoia, should not hang around any more */
12847 if (mClientToken)
12848 {
12849 delete mClientToken;
12850 mClientToken = NULL;
12851 }
12852
12853 uninit(Uninit::Unexpected);
12854
12855 BaseFinalRelease();
12856}
12857
12858/**
12859 * @note Must be called only by Machine::LockMachine() from its own write lock.
12860 */
12861HRESULT SessionMachine::init(Machine *aMachine)
12862{
12863 LogFlowThisFuncEnter();
12864 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12865
12866 AssertReturn(aMachine, E_INVALIDARG);
12867
12868 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12869
12870 /* Enclose the state transition NotReady->InInit->Ready */
12871 AutoInitSpan autoInitSpan(this);
12872 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12873
12874 HRESULT rc = S_OK;
12875
12876 RT_ZERO(mAuthLibCtx);
12877
12878 /* create the machine client token */
12879 try
12880 {
12881 mClientToken = new ClientToken(aMachine, this);
12882 if (!mClientToken->isReady())
12883 {
12884 delete mClientToken;
12885 mClientToken = NULL;
12886 rc = E_FAIL;
12887 }
12888 }
12889 catch (std::bad_alloc &)
12890 {
12891 rc = E_OUTOFMEMORY;
12892 }
12893 if (FAILED(rc))
12894 return rc;
12895
12896 /* memorize the peer Machine */
12897 unconst(mPeer) = aMachine;
12898 /* share the parent pointer */
12899 unconst(mParent) = aMachine->mParent;
12900
12901 /* take the pointers to data to share */
12902 mData.share(aMachine->mData);
12903 mSSData.share(aMachine->mSSData);
12904
12905 mUserData.share(aMachine->mUserData);
12906 mHWData.share(aMachine->mHWData);
12907 mMediumAttachments.share(aMachine->mMediumAttachments);
12908
12909 mStorageControllers.allocate();
12910 for (StorageControllerList::const_iterator
12911 it = aMachine->mStorageControllers->begin();
12912 it != aMachine->mStorageControllers->end();
12913 ++it)
12914 {
12915 ComObjPtr<StorageController> ctl;
12916 ctl.createObject();
12917 ctl->init(this, *it);
12918 mStorageControllers->push_back(ctl);
12919 }
12920
12921 mUSBControllers.allocate();
12922 for (USBControllerList::const_iterator
12923 it = aMachine->mUSBControllers->begin();
12924 it != aMachine->mUSBControllers->end();
12925 ++it)
12926 {
12927 ComObjPtr<USBController> ctl;
12928 ctl.createObject();
12929 ctl->init(this, *it);
12930 mUSBControllers->push_back(ctl);
12931 }
12932
12933 unconst(mBIOSSettings).createObject();
12934 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12935
12936 unconst(mTrustedPlatformModule).createObject();
12937 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12938
12939 unconst(mNvramStore).createObject();
12940 mNvramStore->init(this, aMachine->mNvramStore);
12941
12942 unconst(mRecordingSettings).createObject();
12943 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12944 /* create another GraphicsAdapter object that will be mutable */
12945 unconst(mGraphicsAdapter).createObject();
12946 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12947 /* create another VRDEServer object that will be mutable */
12948 unconst(mVRDEServer).createObject();
12949 mVRDEServer->init(this, aMachine->mVRDEServer);
12950 /* create another audio adapter object that will be mutable */
12951 unconst(mAudioAdapter).createObject();
12952 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12953 /* create a list of serial ports that will be mutable */
12954 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12955 {
12956 unconst(mSerialPorts[slot]).createObject();
12957 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12958 }
12959 /* create a list of parallel ports that will be mutable */
12960 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12961 {
12962 unconst(mParallelPorts[slot]).createObject();
12963 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12964 }
12965
12966 /* create another USB device filters object that will be mutable */
12967 unconst(mUSBDeviceFilters).createObject();
12968 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12969
12970 /* create a list of network adapters that will be mutable */
12971 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12972 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12973 {
12974 unconst(mNetworkAdapters[slot]).createObject();
12975 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12976 }
12977
12978 /* create another bandwidth control object that will be mutable */
12979 unconst(mBandwidthControl).createObject();
12980 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12981
12982 /* default is to delete saved state on Saved -> PoweredOff transition */
12983 mRemoveSavedState = true;
12984
12985 /* Confirm a successful initialization when it's the case */
12986 autoInitSpan.setSucceeded();
12987
12988 miNATNetworksStarted = 0;
12989
12990 LogFlowThisFuncLeave();
12991 return rc;
12992}
12993
12994/**
12995 * Uninitializes this session object. If the reason is other than
12996 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12997 * or the client watcher code.
12998 *
12999 * @param aReason uninitialization reason
13000 *
13001 * @note Locks mParent + this object for writing.
13002 */
13003void SessionMachine::uninit(Uninit::Reason aReason)
13004{
13005 LogFlowThisFuncEnter();
13006 LogFlowThisFunc(("reason=%d\n", aReason));
13007
13008 /*
13009 * Strongly reference ourselves to prevent this object deletion after
13010 * mData->mSession.mMachine.setNull() below (which can release the last
13011 * reference and call the destructor). Important: this must be done before
13012 * accessing any members (and before AutoUninitSpan that does it as well).
13013 * This self reference will be released as the very last step on return.
13014 */
13015 ComObjPtr<SessionMachine> selfRef;
13016 if (aReason != Uninit::Unexpected)
13017 selfRef = this;
13018
13019 /* Enclose the state transition Ready->InUninit->NotReady */
13020 AutoUninitSpan autoUninitSpan(this);
13021 if (autoUninitSpan.uninitDone())
13022 {
13023 LogFlowThisFunc(("Already uninitialized\n"));
13024 LogFlowThisFuncLeave();
13025 return;
13026 }
13027
13028 if (autoUninitSpan.initFailed())
13029 {
13030 /* We've been called by init() because it's failed. It's not really
13031 * necessary (nor it's safe) to perform the regular uninit sequence
13032 * below, the following is enough.
13033 */
13034 LogFlowThisFunc(("Initialization failed.\n"));
13035 /* destroy the machine client token */
13036 if (mClientToken)
13037 {
13038 delete mClientToken;
13039 mClientToken = NULL;
13040 }
13041 uninitDataAndChildObjects();
13042 mData.free();
13043 unconst(mParent) = NULL;
13044 unconst(mPeer) = NULL;
13045 LogFlowThisFuncLeave();
13046 return;
13047 }
13048
13049 MachineState_T lastState;
13050 {
13051 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13052 lastState = mData->mMachineState;
13053 }
13054 NOREF(lastState);
13055
13056#ifdef VBOX_WITH_USB
13057 // release all captured USB devices, but do this before requesting the locks below
13058 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13059 {
13060 /* Console::captureUSBDevices() is called in the VM process only after
13061 * setting the machine state to Starting or Restoring.
13062 * Console::detachAllUSBDevices() will be called upon successful
13063 * termination. So, we need to release USB devices only if there was
13064 * an abnormal termination of a running VM.
13065 *
13066 * This is identical to SessionMachine::DetachAllUSBDevices except
13067 * for the aAbnormal argument. */
13068 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13069 AssertComRC(rc);
13070 NOREF(rc);
13071
13072 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13073 if (service)
13074 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13075 }
13076#endif /* VBOX_WITH_USB */
13077
13078 // we need to lock this object in uninit() because the lock is shared
13079 // with mPeer (as well as data we modify below). mParent lock is needed
13080 // by several calls to it.
13081 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13082
13083#ifdef VBOX_WITH_RESOURCE_USAGE_API
13084 /*
13085 * It is safe to call Machine::i_unregisterMetrics() here because
13086 * PerformanceCollector::samplerCallback no longer accesses guest methods
13087 * holding the lock.
13088 */
13089 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13090 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13091 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13092 if (mCollectorGuest)
13093 {
13094 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13095 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13096 mCollectorGuest = NULL;
13097 }
13098#endif
13099
13100 if (aReason == Uninit::Abnormal)
13101 {
13102 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13103
13104 /*
13105 * Move the VM to the 'Aborted' machine state unless we are restoring a
13106 * VM that was in the 'Saved' machine state. In that case, if the VM
13107 * fails before reaching either the 'Restoring' machine state or the
13108 * 'Running' machine state then we set the machine state to
13109 * 'AbortedSaved' in order to preserve the saved state file so that the
13110 * VM can be restored in the future.
13111 */
13112 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13113 i_setMachineState(MachineState_AbortedSaved);
13114 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13115 i_setMachineState(MachineState_Aborted);
13116 }
13117
13118 // any machine settings modified?
13119 if (mData->flModifications)
13120 {
13121 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13122 i_rollback(false /* aNotify */);
13123 }
13124
13125 mData->mSession.mPID = NIL_RTPROCESS;
13126
13127 if (aReason == Uninit::Unexpected)
13128 {
13129 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13130 * client watcher thread to update the set of machines that have open
13131 * sessions. */
13132 mParent->i_updateClientWatcher();
13133 }
13134
13135 /* uninitialize all remote controls */
13136 if (mData->mSession.mRemoteControls.size())
13137 {
13138 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13139 mData->mSession.mRemoteControls.size()));
13140
13141 /* Always restart a the beginning, since the iterator is invalidated
13142 * by using erase(). */
13143 for (Data::Session::RemoteControlList::iterator
13144 it = mData->mSession.mRemoteControls.begin();
13145 it != mData->mSession.mRemoteControls.end();
13146 it = mData->mSession.mRemoteControls.begin())
13147 {
13148 ComPtr<IInternalSessionControl> pControl = *it;
13149 mData->mSession.mRemoteControls.erase(it);
13150 multilock.release();
13151 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13152 HRESULT rc = pControl->Uninitialize();
13153 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13154 if (FAILED(rc))
13155 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13156 multilock.acquire();
13157 }
13158 mData->mSession.mRemoteControls.clear();
13159 }
13160
13161 /* Remove all references to the NAT network service. The service will stop
13162 * if all references (also from other VMs) are removed. */
13163 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13164 {
13165 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13166 {
13167 BOOL enabled;
13168 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13169 if ( FAILED(hrc)
13170 || !enabled)
13171 continue;
13172
13173 NetworkAttachmentType_T type;
13174 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13175 if ( SUCCEEDED(hrc)
13176 && type == NetworkAttachmentType_NATNetwork)
13177 {
13178 Bstr name;
13179 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13180 if (SUCCEEDED(hrc))
13181 {
13182 multilock.release();
13183 Utf8Str strName(name);
13184 LogRel(("VM '%s' stops using NAT network '%s'\n",
13185 mUserData->s.strName.c_str(), strName.c_str()));
13186 mParent->i_natNetworkRefDec(strName);
13187 multilock.acquire();
13188 }
13189 }
13190 }
13191 }
13192
13193 /*
13194 * An expected uninitialization can come only from #i_checkForDeath().
13195 * Otherwise it means that something's gone really wrong (for example,
13196 * the Session implementation has released the VirtualBox reference
13197 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13198 * etc). However, it's also possible, that the client releases the IPC
13199 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13200 * but the VirtualBox release event comes first to the server process.
13201 * This case is practically possible, so we should not assert on an
13202 * unexpected uninit, just log a warning.
13203 */
13204
13205 if (aReason == Uninit::Unexpected)
13206 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13207
13208 if (aReason != Uninit::Normal)
13209 {
13210 mData->mSession.mDirectControl.setNull();
13211 }
13212 else
13213 {
13214 /* this must be null here (see #OnSessionEnd()) */
13215 Assert(mData->mSession.mDirectControl.isNull());
13216 Assert(mData->mSession.mState == SessionState_Unlocking);
13217 Assert(!mData->mSession.mProgress.isNull());
13218 }
13219 if (mData->mSession.mProgress)
13220 {
13221 if (aReason == Uninit::Normal)
13222 mData->mSession.mProgress->i_notifyComplete(S_OK);
13223 else
13224 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13225 COM_IIDOF(ISession),
13226 getComponentName(),
13227 tr("The VM session was aborted"));
13228 mData->mSession.mProgress.setNull();
13229 }
13230
13231 if (mConsoleTaskData.mProgress)
13232 {
13233 Assert(aReason == Uninit::Abnormal);
13234 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13235 COM_IIDOF(ISession),
13236 getComponentName(),
13237 tr("The VM session was aborted"));
13238 mConsoleTaskData.mProgress.setNull();
13239 }
13240
13241 /* remove the association between the peer machine and this session machine */
13242 Assert( (SessionMachine*)mData->mSession.mMachine == this
13243 || aReason == Uninit::Unexpected);
13244
13245 /* reset the rest of session data */
13246 mData->mSession.mLockType = LockType_Null;
13247 mData->mSession.mMachine.setNull();
13248 mData->mSession.mState = SessionState_Unlocked;
13249 mData->mSession.mName.setNull();
13250
13251 /* destroy the machine client token before leaving the exclusive lock */
13252 if (mClientToken)
13253 {
13254 delete mClientToken;
13255 mClientToken = NULL;
13256 }
13257
13258 /* fire an event */
13259 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13260
13261 uninitDataAndChildObjects();
13262
13263 /* free the essential data structure last */
13264 mData.free();
13265
13266 /* release the exclusive lock before setting the below two to NULL */
13267 multilock.release();
13268
13269 unconst(mParent) = NULL;
13270 unconst(mPeer) = NULL;
13271
13272 AuthLibUnload(&mAuthLibCtx);
13273
13274 LogFlowThisFuncLeave();
13275}
13276
13277// util::Lockable interface
13278////////////////////////////////////////////////////////////////////////////////
13279
13280/**
13281 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13282 * with the primary Machine instance (mPeer).
13283 */
13284RWLockHandle *SessionMachine::lockHandle() const
13285{
13286 AssertReturn(mPeer != NULL, NULL);
13287 return mPeer->lockHandle();
13288}
13289
13290// IInternalMachineControl methods
13291////////////////////////////////////////////////////////////////////////////////
13292
13293/**
13294 * Passes collected guest statistics to performance collector object
13295 */
13296HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13297 ULONG aCpuKernel, ULONG aCpuIdle,
13298 ULONG aMemTotal, ULONG aMemFree,
13299 ULONG aMemBalloon, ULONG aMemShared,
13300 ULONG aMemCache, ULONG aPageTotal,
13301 ULONG aAllocVMM, ULONG aFreeVMM,
13302 ULONG aBalloonedVMM, ULONG aSharedVMM,
13303 ULONG aVmNetRx, ULONG aVmNetTx)
13304{
13305#ifdef VBOX_WITH_RESOURCE_USAGE_API
13306 if (mCollectorGuest)
13307 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13308 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13309 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13310 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13311
13312 return S_OK;
13313#else
13314 NOREF(aValidStats);
13315 NOREF(aCpuUser);
13316 NOREF(aCpuKernel);
13317 NOREF(aCpuIdle);
13318 NOREF(aMemTotal);
13319 NOREF(aMemFree);
13320 NOREF(aMemBalloon);
13321 NOREF(aMemShared);
13322 NOREF(aMemCache);
13323 NOREF(aPageTotal);
13324 NOREF(aAllocVMM);
13325 NOREF(aFreeVMM);
13326 NOREF(aBalloonedVMM);
13327 NOREF(aSharedVMM);
13328 NOREF(aVmNetRx);
13329 NOREF(aVmNetTx);
13330 return E_NOTIMPL;
13331#endif
13332}
13333
13334////////////////////////////////////////////////////////////////////////////////
13335//
13336// SessionMachine task records
13337//
13338////////////////////////////////////////////////////////////////////////////////
13339
13340/**
13341 * Task record for saving the machine state.
13342 */
13343class SessionMachine::SaveStateTask
13344 : public Machine::Task
13345{
13346public:
13347 SaveStateTask(SessionMachine *m,
13348 Progress *p,
13349 const Utf8Str &t,
13350 Reason_T enmReason,
13351 const Utf8Str &strStateFilePath)
13352 : Task(m, p, t),
13353 m_enmReason(enmReason),
13354 m_strStateFilePath(strStateFilePath)
13355 {}
13356
13357private:
13358 void handler()
13359 {
13360 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13361 }
13362
13363 Reason_T m_enmReason;
13364 Utf8Str m_strStateFilePath;
13365
13366 friend class SessionMachine;
13367};
13368
13369/**
13370 * Task thread implementation for SessionMachine::SaveState(), called from
13371 * SessionMachine::taskHandler().
13372 *
13373 * @note Locks this object for writing.
13374 *
13375 * @param task
13376 * @return
13377 */
13378void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13379{
13380 LogFlowThisFuncEnter();
13381
13382 AutoCaller autoCaller(this);
13383 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13384 if (FAILED(autoCaller.rc()))
13385 {
13386 /* we might have been uninitialized because the session was accidentally
13387 * closed by the client, so don't assert */
13388 HRESULT rc = setError(E_FAIL,
13389 tr("The session has been accidentally closed"));
13390 task.m_pProgress->i_notifyComplete(rc);
13391 LogFlowThisFuncLeave();
13392 return;
13393 }
13394
13395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13396
13397 HRESULT rc = S_OK;
13398
13399 try
13400 {
13401 ComPtr<IInternalSessionControl> directControl;
13402 if (mData->mSession.mLockType == LockType_VM)
13403 directControl = mData->mSession.mDirectControl;
13404 if (directControl.isNull())
13405 throw setError(VBOX_E_INVALID_VM_STATE,
13406 tr("Trying to save state without a running VM"));
13407 alock.release();
13408 BOOL fSuspendedBySave;
13409 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13410 Assert(!fSuspendedBySave);
13411 alock.acquire();
13412
13413 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13414 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13415 throw E_FAIL);
13416
13417 if (SUCCEEDED(rc))
13418 {
13419 mSSData->strStateFilePath = task.m_strStateFilePath;
13420
13421 /* save all VM settings */
13422 rc = i_saveSettings(NULL, alock);
13423 // no need to check whether VirtualBox.xml needs saving also since
13424 // we can't have a name change pending at this point
13425 }
13426 else
13427 {
13428 // On failure, set the state to the state we had at the beginning.
13429 i_setMachineState(task.m_machineStateBackup);
13430 i_updateMachineStateOnClient();
13431
13432 // Delete the saved state file (might have been already created).
13433 // No need to check whether this is shared with a snapshot here
13434 // because we certainly created a fresh saved state file here.
13435 RTFileDelete(task.m_strStateFilePath.c_str());
13436 }
13437 }
13438 catch (HRESULT aRC) { rc = aRC; }
13439
13440 task.m_pProgress->i_notifyComplete(rc);
13441
13442 LogFlowThisFuncLeave();
13443}
13444
13445/**
13446 * @note Locks this object for writing.
13447 */
13448HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13449{
13450 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13451}
13452
13453HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13454{
13455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13456
13457 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13458 if (FAILED(rc)) return rc;
13459
13460 if ( mData->mMachineState != MachineState_Running
13461 && mData->mMachineState != MachineState_Paused
13462 )
13463 return setError(VBOX_E_INVALID_VM_STATE,
13464 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13465 Global::stringifyMachineState(mData->mMachineState));
13466
13467 ComObjPtr<Progress> pProgress;
13468 pProgress.createObject();
13469 rc = pProgress->init(i_getVirtualBox(),
13470 static_cast<IMachine *>(this) /* aInitiator */,
13471 tr("Saving the execution state of the virtual machine"),
13472 FALSE /* aCancelable */);
13473 if (FAILED(rc))
13474 return rc;
13475
13476 Utf8Str strStateFilePath;
13477 i_composeSavedStateFilename(strStateFilePath);
13478
13479 /* create and start the task on a separate thread (note that it will not
13480 * start working until we release alock) */
13481 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13482 rc = pTask->createThread();
13483 if (FAILED(rc))
13484 return rc;
13485
13486 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13487 i_setMachineState(MachineState_Saving);
13488 i_updateMachineStateOnClient();
13489
13490 pProgress.queryInterfaceTo(aProgress.asOutParam());
13491
13492 return S_OK;
13493}
13494
13495/**
13496 * @note Locks this object for writing.
13497 */
13498HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13499{
13500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13501
13502 HRESULT rc = i_checkStateDependency(MutableStateDep);
13503 if (FAILED(rc)) return rc;
13504
13505 if ( mData->mMachineState != MachineState_PoweredOff
13506 && mData->mMachineState != MachineState_Teleported
13507 && mData->mMachineState != MachineState_Aborted
13508 )
13509 return setError(VBOX_E_INVALID_VM_STATE,
13510 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13511 Global::stringifyMachineState(mData->mMachineState));
13512
13513 com::Utf8Str stateFilePathFull;
13514 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13515 if (RT_FAILURE(vrc))
13516 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13517 tr("Invalid saved state file path '%s' (%Rrc)"),
13518 aSavedStateFile.c_str(),
13519 vrc);
13520
13521 mSSData->strStateFilePath = stateFilePathFull;
13522
13523 /* The below i_setMachineState() will detect the state transition and will
13524 * update the settings file */
13525
13526 return i_setMachineState(MachineState_Saved);
13527}
13528
13529/**
13530 * @note Locks this object for writing.
13531 */
13532HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13533{
13534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13535
13536 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13537 if (FAILED(rc)) return rc;
13538
13539 if ( mData->mMachineState != MachineState_Saved
13540 && mData->mMachineState != MachineState_AbortedSaved)
13541 return setError(VBOX_E_INVALID_VM_STATE,
13542 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13543 Global::stringifyMachineState(mData->mMachineState));
13544
13545 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13546
13547 /*
13548 * Saved -> PoweredOff transition will be detected in the SessionMachine
13549 * and properly handled.
13550 */
13551 rc = i_setMachineState(MachineState_PoweredOff);
13552 return rc;
13553}
13554
13555
13556/**
13557 * @note Locks the same as #i_setMachineState() does.
13558 */
13559HRESULT SessionMachine::updateState(MachineState_T aState)
13560{
13561 return i_setMachineState(aState);
13562}
13563
13564/**
13565 * @note Locks this object for writing.
13566 */
13567HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13568{
13569 IProgress *pProgress(aProgress);
13570
13571 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13572
13573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13574
13575 if (mData->mSession.mState != SessionState_Locked)
13576 return VBOX_E_INVALID_OBJECT_STATE;
13577
13578 if (!mData->mSession.mProgress.isNull())
13579 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13580
13581 /* If we didn't reference the NAT network service yet, add a reference to
13582 * force a start */
13583 if (miNATNetworksStarted < 1)
13584 {
13585 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13586 {
13587 BOOL enabled;
13588 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13589 if ( FAILED(hrc)
13590 || !enabled)
13591 continue;
13592
13593 NetworkAttachmentType_T type;
13594 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13595 if ( SUCCEEDED(hrc)
13596 && type == NetworkAttachmentType_NATNetwork)
13597 {
13598 Bstr name;
13599 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13600 if (SUCCEEDED(hrc))
13601 {
13602 Utf8Str strName(name);
13603 LogRel(("VM '%s' starts using NAT network '%s'\n",
13604 mUserData->s.strName.c_str(), strName.c_str()));
13605 mPeer->lockHandle()->unlockWrite();
13606 mParent->i_natNetworkRefInc(strName);
13607#ifdef RT_LOCK_STRICT
13608 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13609#else
13610 mPeer->lockHandle()->lockWrite();
13611#endif
13612 }
13613 }
13614 }
13615 miNATNetworksStarted++;
13616 }
13617
13618 LogFlowThisFunc(("returns S_OK.\n"));
13619 return S_OK;
13620}
13621
13622/**
13623 * @note Locks this object for writing.
13624 */
13625HRESULT SessionMachine::endPowerUp(LONG aResult)
13626{
13627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13628
13629 if (mData->mSession.mState != SessionState_Locked)
13630 return VBOX_E_INVALID_OBJECT_STATE;
13631
13632 /* Finalize the LaunchVMProcess progress object. */
13633 if (mData->mSession.mProgress)
13634 {
13635 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13636 mData->mSession.mProgress.setNull();
13637 }
13638
13639 if (SUCCEEDED((HRESULT)aResult))
13640 {
13641#ifdef VBOX_WITH_RESOURCE_USAGE_API
13642 /* The VM has been powered up successfully, so it makes sense
13643 * now to offer the performance metrics for a running machine
13644 * object. Doing it earlier wouldn't be safe. */
13645 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13646 mData->mSession.mPID);
13647#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13648 }
13649
13650 return S_OK;
13651}
13652
13653/**
13654 * @note Locks this object for writing.
13655 */
13656HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13657{
13658 LogFlowThisFuncEnter();
13659
13660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13661
13662 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13663 E_FAIL);
13664
13665 /* create a progress object to track operation completion */
13666 ComObjPtr<Progress> pProgress;
13667 pProgress.createObject();
13668 pProgress->init(i_getVirtualBox(),
13669 static_cast<IMachine *>(this) /* aInitiator */,
13670 tr("Stopping the virtual machine"),
13671 FALSE /* aCancelable */);
13672
13673 /* fill in the console task data */
13674 mConsoleTaskData.mLastState = mData->mMachineState;
13675 mConsoleTaskData.mProgress = pProgress;
13676
13677 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13678 i_setMachineState(MachineState_Stopping);
13679
13680 pProgress.queryInterfaceTo(aProgress.asOutParam());
13681
13682 return S_OK;
13683}
13684
13685/**
13686 * @note Locks this object for writing.
13687 */
13688HRESULT SessionMachine::endPoweringDown(LONG aResult,
13689 const com::Utf8Str &aErrMsg)
13690{
13691 HRESULT const hrcResult = (HRESULT)aResult;
13692 LogFlowThisFuncEnter();
13693
13694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13695
13696 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13697 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13698 && mConsoleTaskData.mLastState != MachineState_Null,
13699 E_FAIL);
13700
13701 /*
13702 * On failure, set the state to the state we had when BeginPoweringDown()
13703 * was called (this is expected by Console::PowerDown() and the associated
13704 * task). On success the VM process already changed the state to
13705 * MachineState_PoweredOff, so no need to do anything.
13706 */
13707 if (FAILED(hrcResult))
13708 i_setMachineState(mConsoleTaskData.mLastState);
13709
13710 /* notify the progress object about operation completion */
13711 Assert(mConsoleTaskData.mProgress);
13712 if (SUCCEEDED(hrcResult))
13713 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13714 else
13715 {
13716 if (aErrMsg.length())
13717 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13718 COM_IIDOF(ISession),
13719 getComponentName(),
13720 aErrMsg.c_str());
13721 else
13722 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13723 }
13724
13725 /* clear out the temporary saved state data */
13726 mConsoleTaskData.mLastState = MachineState_Null;
13727 mConsoleTaskData.mProgress.setNull();
13728
13729 LogFlowThisFuncLeave();
13730 return S_OK;
13731}
13732
13733
13734/**
13735 * Goes through the USB filters of the given machine to see if the given
13736 * device matches any filter or not.
13737 *
13738 * @note Locks the same as USBController::hasMatchingFilter() does.
13739 */
13740HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13741 BOOL *aMatched,
13742 ULONG *aMaskedInterfaces)
13743{
13744 LogFlowThisFunc(("\n"));
13745
13746#ifdef VBOX_WITH_USB
13747 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13748#else
13749 NOREF(aDevice);
13750 NOREF(aMaskedInterfaces);
13751 *aMatched = FALSE;
13752#endif
13753
13754 return S_OK;
13755}
13756
13757/**
13758 * @note Locks the same as Host::captureUSBDevice() does.
13759 */
13760HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13761{
13762 LogFlowThisFunc(("\n"));
13763
13764#ifdef VBOX_WITH_USB
13765 /* if captureDeviceForVM() fails, it must have set extended error info */
13766 clearError();
13767 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13768 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13769 return rc;
13770
13771 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13772 AssertReturn(service, E_FAIL);
13773 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13774#else
13775 RT_NOREF(aId, aCaptureFilename);
13776 return E_NOTIMPL;
13777#endif
13778}
13779
13780/**
13781 * @note Locks the same as Host::detachUSBDevice() does.
13782 */
13783HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13784 BOOL aDone)
13785{
13786 LogFlowThisFunc(("\n"));
13787
13788#ifdef VBOX_WITH_USB
13789 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13790 AssertReturn(service, E_FAIL);
13791 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13792#else
13793 NOREF(aId);
13794 NOREF(aDone);
13795 return E_NOTIMPL;
13796#endif
13797}
13798
13799/**
13800 * Inserts all machine filters to the USB proxy service and then calls
13801 * Host::autoCaptureUSBDevices().
13802 *
13803 * Called by Console from the VM process upon VM startup.
13804 *
13805 * @note Locks what called methods lock.
13806 */
13807HRESULT SessionMachine::autoCaptureUSBDevices()
13808{
13809 LogFlowThisFunc(("\n"));
13810
13811#ifdef VBOX_WITH_USB
13812 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13813 AssertComRC(rc);
13814 NOREF(rc);
13815
13816 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13817 AssertReturn(service, E_FAIL);
13818 return service->autoCaptureDevicesForVM(this);
13819#else
13820 return S_OK;
13821#endif
13822}
13823
13824/**
13825 * Removes all machine filters from the USB proxy service and then calls
13826 * Host::detachAllUSBDevices().
13827 *
13828 * Called by Console from the VM process upon normal VM termination or by
13829 * SessionMachine::uninit() upon abnormal VM termination (from under the
13830 * Machine/SessionMachine lock).
13831 *
13832 * @note Locks what called methods lock.
13833 */
13834HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13835{
13836 LogFlowThisFunc(("\n"));
13837
13838#ifdef VBOX_WITH_USB
13839 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13840 AssertComRC(rc);
13841 NOREF(rc);
13842
13843 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13844 AssertReturn(service, E_FAIL);
13845 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13846#else
13847 NOREF(aDone);
13848 return S_OK;
13849#endif
13850}
13851
13852/**
13853 * @note Locks this object for writing.
13854 */
13855HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13856 ComPtr<IProgress> &aProgress)
13857{
13858 LogFlowThisFuncEnter();
13859
13860 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13861 /*
13862 * We don't assert below because it might happen that a non-direct session
13863 * informs us it is closed right after we've been uninitialized -- it's ok.
13864 */
13865
13866 /* get IInternalSessionControl interface */
13867 ComPtr<IInternalSessionControl> control(aSession);
13868
13869 ComAssertRet(!control.isNull(), E_INVALIDARG);
13870
13871 /* Creating a Progress object requires the VirtualBox lock, and
13872 * thus locking it here is required by the lock order rules. */
13873 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13874
13875 if (control == mData->mSession.mDirectControl)
13876 {
13877 /* The direct session is being normally closed by the client process
13878 * ----------------------------------------------------------------- */
13879
13880 /* go to the closing state (essential for all open*Session() calls and
13881 * for #i_checkForDeath()) */
13882 Assert(mData->mSession.mState == SessionState_Locked);
13883 mData->mSession.mState = SessionState_Unlocking;
13884
13885 /* set direct control to NULL to release the remote instance */
13886 mData->mSession.mDirectControl.setNull();
13887 LogFlowThisFunc(("Direct control is set to NULL\n"));
13888
13889 if (mData->mSession.mProgress)
13890 {
13891 /* finalize the progress, someone might wait if a frontend
13892 * closes the session before powering on the VM. */
13893 mData->mSession.mProgress->notifyComplete(E_FAIL,
13894 COM_IIDOF(ISession),
13895 getComponentName(),
13896 tr("The VM session was closed before any attempt to power it on"));
13897 mData->mSession.mProgress.setNull();
13898 }
13899
13900 /* Create the progress object the client will use to wait until
13901 * #i_checkForDeath() is called to uninitialize this session object after
13902 * it releases the IPC semaphore.
13903 * Note! Because we're "reusing" mProgress here, this must be a proxy
13904 * object just like for LaunchVMProcess. */
13905 Assert(mData->mSession.mProgress.isNull());
13906 ComObjPtr<ProgressProxy> progress;
13907 progress.createObject();
13908 ComPtr<IUnknown> pPeer(mPeer);
13909 progress->init(mParent, pPeer,
13910 Bstr(tr("Closing session")).raw(),
13911 FALSE /* aCancelable */);
13912 progress.queryInterfaceTo(aProgress.asOutParam());
13913 mData->mSession.mProgress = progress;
13914 }
13915 else
13916 {
13917 /* the remote session is being normally closed */
13918 bool found = false;
13919 for (Data::Session::RemoteControlList::iterator
13920 it = mData->mSession.mRemoteControls.begin();
13921 it != mData->mSession.mRemoteControls.end();
13922 ++it)
13923 {
13924 if (control == *it)
13925 {
13926 found = true;
13927 // This MUST be erase(it), not remove(*it) as the latter
13928 // triggers a very nasty use after free due to the place where
13929 // the value "lives".
13930 mData->mSession.mRemoteControls.erase(it);
13931 break;
13932 }
13933 }
13934 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13935 E_INVALIDARG);
13936 }
13937
13938 /* signal the client watcher thread, because the client is going away */
13939 mParent->i_updateClientWatcher();
13940
13941 LogFlowThisFuncLeave();
13942 return S_OK;
13943}
13944
13945HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13946 std::vector<com::Utf8Str> &aValues,
13947 std::vector<LONG64> &aTimestamps,
13948 std::vector<com::Utf8Str> &aFlags)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952#ifdef VBOX_WITH_GUEST_PROPS
13953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13954
13955 size_t cEntries = mHWData->mGuestProperties.size();
13956 aNames.resize(cEntries);
13957 aValues.resize(cEntries);
13958 aTimestamps.resize(cEntries);
13959 aFlags.resize(cEntries);
13960
13961 size_t i = 0;
13962 for (HWData::GuestPropertyMap::const_iterator
13963 it = mHWData->mGuestProperties.begin();
13964 it != mHWData->mGuestProperties.end();
13965 ++it, ++i)
13966 {
13967 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13968 aNames[i] = it->first;
13969 aValues[i] = it->second.strValue;
13970 aTimestamps[i] = it->second.mTimestamp;
13971
13972 /* If it is NULL, keep it NULL. */
13973 if (it->second.mFlags)
13974 {
13975 GuestPropWriteFlags(it->second.mFlags, szFlags);
13976 aFlags[i] = szFlags;
13977 }
13978 else
13979 aFlags[i] = "";
13980
13981 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13982 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13983 }
13984 return S_OK;
13985#else
13986 ReturnComNotImplemented();
13987#endif
13988}
13989
13990HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13991 const com::Utf8Str &aValue,
13992 LONG64 aTimestamp,
13993 const com::Utf8Str &aFlags,
13994 BOOL fWasDeleted)
13995{
13996 LogFlowThisFunc(("\n"));
13997
13998#ifdef VBOX_WITH_GUEST_PROPS
13999 try
14000 {
14001 /*
14002 * Convert input up front.
14003 */
14004 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14005 if (aFlags.length())
14006 {
14007 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14008 AssertRCReturn(vrc, E_INVALIDARG);
14009 }
14010
14011 /*
14012 * Now grab the object lock, validate the state and do the update.
14013 */
14014
14015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14016
14017 if (!Global::IsOnline(mData->mMachineState))
14018 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14019
14020 i_setModified(IsModified_MachineData);
14021 mHWData.backup();
14022
14023 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14024 if (it != mHWData->mGuestProperties.end())
14025 {
14026 if (!fWasDeleted)
14027 {
14028 it->second.strValue = aValue;
14029 it->second.mTimestamp = aTimestamp;
14030 it->second.mFlags = fFlags;
14031 }
14032 else
14033 mHWData->mGuestProperties.erase(it);
14034
14035 mData->mGuestPropertiesModified = TRUE;
14036 }
14037 else if (!fWasDeleted)
14038 {
14039 HWData::GuestProperty prop;
14040 prop.strValue = aValue;
14041 prop.mTimestamp = aTimestamp;
14042 prop.mFlags = fFlags;
14043
14044 mHWData->mGuestProperties[aName] = prop;
14045 mData->mGuestPropertiesModified = TRUE;
14046 }
14047
14048 alock.release();
14049
14050 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14051 }
14052 catch (...)
14053 {
14054 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14055 }
14056 return S_OK;
14057#else
14058 ReturnComNotImplemented();
14059#endif
14060}
14061
14062
14063HRESULT SessionMachine::lockMedia()
14064{
14065 AutoMultiWriteLock2 alock(this->lockHandle(),
14066 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14067
14068 AssertReturn( mData->mMachineState == MachineState_Starting
14069 || mData->mMachineState == MachineState_Restoring
14070 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14071
14072 clearError();
14073 alock.release();
14074 return i_lockMedia();
14075}
14076
14077HRESULT SessionMachine::unlockMedia()
14078{
14079 HRESULT hrc = i_unlockMedia();
14080 return hrc;
14081}
14082
14083HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14084 ComPtr<IMediumAttachment> &aNewAttachment)
14085{
14086 // request the host lock first, since might be calling Host methods for getting host drives;
14087 // next, protect the media tree all the while we're in here, as well as our member variables
14088 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14089 this->lockHandle(),
14090 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14091
14092 IMediumAttachment *iAttach = aAttachment;
14093 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14094
14095 Utf8Str ctrlName;
14096 LONG lPort;
14097 LONG lDevice;
14098 bool fTempEject;
14099 {
14100 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14101
14102 /* Need to query the details first, as the IMediumAttachment reference
14103 * might be to the original settings, which we are going to change. */
14104 ctrlName = pAttach->i_getControllerName();
14105 lPort = pAttach->i_getPort();
14106 lDevice = pAttach->i_getDevice();
14107 fTempEject = pAttach->i_getTempEject();
14108 }
14109
14110 if (!fTempEject)
14111 {
14112 /* Remember previously mounted medium. The medium before taking the
14113 * backup is not necessarily the same thing. */
14114 ComObjPtr<Medium> oldmedium;
14115 oldmedium = pAttach->i_getMedium();
14116
14117 i_setModified(IsModified_Storage);
14118 mMediumAttachments.backup();
14119
14120 // The backup operation makes the pAttach reference point to the
14121 // old settings. Re-get the correct reference.
14122 pAttach = i_findAttachment(*mMediumAttachments.data(),
14123 ctrlName,
14124 lPort,
14125 lDevice);
14126
14127 {
14128 AutoCaller autoAttachCaller(this);
14129 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14130
14131 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14132 if (!oldmedium.isNull())
14133 oldmedium->i_removeBackReference(mData->mUuid);
14134
14135 pAttach->i_updateMedium(NULL);
14136 pAttach->i_updateEjected();
14137 }
14138
14139 i_setModified(IsModified_Storage);
14140 }
14141 else
14142 {
14143 {
14144 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14145 pAttach->i_updateEjected();
14146 }
14147 }
14148
14149 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14150
14151 return S_OK;
14152}
14153
14154HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14155 com::Utf8Str &aResult)
14156{
14157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14158
14159 HRESULT hr = S_OK;
14160
14161 if (!mAuthLibCtx.hAuthLibrary)
14162 {
14163 /* Load the external authentication library. */
14164 Bstr authLibrary;
14165 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14166
14167 Utf8Str filename = authLibrary;
14168
14169 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14170 if (RT_FAILURE(vrc))
14171 hr = setErrorBoth(E_FAIL, vrc,
14172 tr("Could not load the external authentication library '%s' (%Rrc)"),
14173 filename.c_str(), vrc);
14174 }
14175
14176 /* The auth library might need the machine lock. */
14177 alock.release();
14178
14179 if (FAILED(hr))
14180 return hr;
14181
14182 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14183 {
14184 enum VRDEAuthParams
14185 {
14186 parmUuid = 1,
14187 parmGuestJudgement,
14188 parmUser,
14189 parmPassword,
14190 parmDomain,
14191 parmClientId
14192 };
14193
14194 AuthResult result = AuthResultAccessDenied;
14195
14196 Guid uuid(aAuthParams[parmUuid]);
14197 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14198 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14199
14200 result = AuthLibAuthenticate(&mAuthLibCtx,
14201 uuid.raw(), guestJudgement,
14202 aAuthParams[parmUser].c_str(),
14203 aAuthParams[parmPassword].c_str(),
14204 aAuthParams[parmDomain].c_str(),
14205 u32ClientId);
14206
14207 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14208 size_t cbPassword = aAuthParams[parmPassword].length();
14209 if (cbPassword)
14210 {
14211 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14212 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14213 }
14214
14215 if (result == AuthResultAccessGranted)
14216 aResult = "granted";
14217 else
14218 aResult = "denied";
14219
14220 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14221 aAuthParams[parmUser].c_str(), aResult.c_str()));
14222 }
14223 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14224 {
14225 enum VRDEAuthDisconnectParams
14226 {
14227 parmUuid = 1,
14228 parmClientId
14229 };
14230
14231 Guid uuid(aAuthParams[parmUuid]);
14232 uint32_t u32ClientId = 0;
14233 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14234 }
14235 else
14236 {
14237 hr = E_INVALIDARG;
14238 }
14239
14240 return hr;
14241}
14242
14243// public methods only for internal purposes
14244/////////////////////////////////////////////////////////////////////////////
14245
14246#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14247/**
14248 * Called from the client watcher thread to check for expected or unexpected
14249 * death of the client process that has a direct session to this machine.
14250 *
14251 * On Win32 and on OS/2, this method is called only when we've got the
14252 * mutex (i.e. the client has either died or terminated normally) so it always
14253 * returns @c true (the client is terminated, the session machine is
14254 * uninitialized).
14255 *
14256 * On other platforms, the method returns @c true if the client process has
14257 * terminated normally or abnormally and the session machine was uninitialized,
14258 * and @c false if the client process is still alive.
14259 *
14260 * @note Locks this object for writing.
14261 */
14262bool SessionMachine::i_checkForDeath()
14263{
14264 Uninit::Reason reason;
14265 bool terminated = false;
14266
14267 /* Enclose autoCaller with a block because calling uninit() from under it
14268 * will deadlock. */
14269 {
14270 AutoCaller autoCaller(this);
14271 if (!autoCaller.isOk())
14272 {
14273 /* return true if not ready, to cause the client watcher to exclude
14274 * the corresponding session from watching */
14275 LogFlowThisFunc(("Already uninitialized!\n"));
14276 return true;
14277 }
14278
14279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14280
14281 /* Determine the reason of death: if the session state is Closing here,
14282 * everything is fine. Otherwise it means that the client did not call
14283 * OnSessionEnd() before it released the IPC semaphore. This may happen
14284 * either because the client process has abnormally terminated, or
14285 * because it simply forgot to call ISession::Close() before exiting. We
14286 * threat the latter also as an abnormal termination (see
14287 * Session::uninit() for details). */
14288 reason = mData->mSession.mState == SessionState_Unlocking ?
14289 Uninit::Normal :
14290 Uninit::Abnormal;
14291
14292 if (mClientToken)
14293 terminated = mClientToken->release();
14294 } /* AutoCaller block */
14295
14296 if (terminated)
14297 uninit(reason);
14298
14299 return terminated;
14300}
14301
14302void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14303{
14304 LogFlowThisFunc(("\n"));
14305
14306 strTokenId.setNull();
14307
14308 AutoCaller autoCaller(this);
14309 AssertComRCReturnVoid(autoCaller.rc());
14310
14311 Assert(mClientToken);
14312 if (mClientToken)
14313 mClientToken->getId(strTokenId);
14314}
14315#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14316IToken *SessionMachine::i_getToken()
14317{
14318 LogFlowThisFunc(("\n"));
14319
14320 AutoCaller autoCaller(this);
14321 AssertComRCReturn(autoCaller.rc(), NULL);
14322
14323 Assert(mClientToken);
14324 if (mClientToken)
14325 return mClientToken->getToken();
14326 else
14327 return NULL;
14328}
14329#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14330
14331Machine::ClientToken *SessionMachine::i_getClientToken()
14332{
14333 LogFlowThisFunc(("\n"));
14334
14335 AutoCaller autoCaller(this);
14336 AssertComRCReturn(autoCaller.rc(), NULL);
14337
14338 return mClientToken;
14339}
14340
14341
14342/**
14343 * @note Locks this object for reading.
14344 */
14345HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14346{
14347 LogFlowThisFunc(("\n"));
14348
14349 AutoCaller autoCaller(this);
14350 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14351
14352 ComPtr<IInternalSessionControl> directControl;
14353 {
14354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14355 if (mData->mSession.mLockType == LockType_VM)
14356 directControl = mData->mSession.mDirectControl;
14357 }
14358
14359 /* ignore notifications sent after #OnSessionEnd() is called */
14360 if (!directControl)
14361 return S_OK;
14362
14363 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14364}
14365
14366/**
14367 * @note Locks this object for reading.
14368 */
14369HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14370 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14371 const Utf8Str &aGuestIp, LONG aGuestPort)
14372{
14373 LogFlowThisFunc(("\n"));
14374
14375 AutoCaller autoCaller(this);
14376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 if (mData->mSession.mLockType == LockType_VM)
14382 directControl = mData->mSession.mDirectControl;
14383 }
14384
14385 /* ignore notifications sent after #OnSessionEnd() is called */
14386 if (!directControl)
14387 return S_OK;
14388 /*
14389 * instead acting like callback we ask IVirtualBox deliver corresponding event
14390 */
14391
14392 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14393 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14394 return S_OK;
14395}
14396
14397/**
14398 * @note Locks this object for reading.
14399 */
14400HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14401{
14402 LogFlowThisFunc(("\n"));
14403
14404 AutoCaller autoCaller(this);
14405 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14406
14407 ComPtr<IInternalSessionControl> directControl;
14408 {
14409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14410 if (mData->mSession.mLockType == LockType_VM)
14411 directControl = mData->mSession.mDirectControl;
14412 }
14413
14414 /* ignore notifications sent after #OnSessionEnd() is called */
14415 if (!directControl)
14416 return S_OK;
14417
14418 return directControl->OnAudioAdapterChange(audioAdapter);
14419}
14420
14421/**
14422 * @note Locks this object for reading.
14423 */
14424HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14425{
14426 LogFlowThisFunc(("\n"));
14427
14428 AutoCaller autoCaller(this);
14429 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14430
14431 ComPtr<IInternalSessionControl> directControl;
14432 {
14433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14434 if (mData->mSession.mLockType == LockType_VM)
14435 directControl = mData->mSession.mDirectControl;
14436 }
14437
14438 /* ignore notifications sent after #OnSessionEnd() is called */
14439 if (!directControl)
14440 return S_OK;
14441
14442 return directControl->OnSerialPortChange(serialPort);
14443}
14444
14445/**
14446 * @note Locks this object for reading.
14447 */
14448HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14449{
14450 LogFlowThisFunc(("\n"));
14451
14452 AutoCaller autoCaller(this);
14453 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14454
14455 ComPtr<IInternalSessionControl> directControl;
14456 {
14457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14458 if (mData->mSession.mLockType == LockType_VM)
14459 directControl = mData->mSession.mDirectControl;
14460 }
14461
14462 /* ignore notifications sent after #OnSessionEnd() is called */
14463 if (!directControl)
14464 return S_OK;
14465
14466 return directControl->OnParallelPortChange(parallelPort);
14467}
14468
14469/**
14470 * @note Locks this object for reading.
14471 */
14472HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14473{
14474 LogFlowThisFunc(("\n"));
14475
14476 AutoCaller autoCaller(this);
14477 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14478
14479 ComPtr<IInternalSessionControl> directControl;
14480 {
14481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14482 if (mData->mSession.mLockType == LockType_VM)
14483 directControl = mData->mSession.mDirectControl;
14484 }
14485
14486 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14487
14488 /* ignore notifications sent after #OnSessionEnd() is called */
14489 if (!directControl)
14490 return S_OK;
14491
14492 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14493}
14494
14495/**
14496 * @note Locks this object for reading.
14497 */
14498HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14499{
14500 LogFlowThisFunc(("\n"));
14501
14502 AutoCaller autoCaller(this);
14503 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14504
14505 ComPtr<IInternalSessionControl> directControl;
14506 {
14507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14508 if (mData->mSession.mLockType == LockType_VM)
14509 directControl = mData->mSession.mDirectControl;
14510 }
14511
14512 mParent->i_onMediumChanged(aAttachment);
14513
14514 /* ignore notifications sent after #OnSessionEnd() is called */
14515 if (!directControl)
14516 return S_OK;
14517
14518 return directControl->OnMediumChange(aAttachment, aForce);
14519}
14520
14521HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14522{
14523 LogFlowThisFunc(("\n"));
14524
14525 AutoCaller autoCaller(this);
14526 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14527
14528 ComPtr<IInternalSessionControl> directControl;
14529 {
14530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14531 if (mData->mSession.mLockType == LockType_VM)
14532 directControl = mData->mSession.mDirectControl;
14533 }
14534
14535 /* ignore notifications sent after #OnSessionEnd() is called */
14536 if (!directControl)
14537 return S_OK;
14538
14539 return directControl->OnVMProcessPriorityChange(aPriority);
14540}
14541
14542/**
14543 * @note Locks this object for reading.
14544 */
14545HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14546{
14547 LogFlowThisFunc(("\n"));
14548
14549 AutoCaller autoCaller(this);
14550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14551
14552 ComPtr<IInternalSessionControl> directControl;
14553 {
14554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14555 if (mData->mSession.mLockType == LockType_VM)
14556 directControl = mData->mSession.mDirectControl;
14557 }
14558
14559 /* ignore notifications sent after #OnSessionEnd() is called */
14560 if (!directControl)
14561 return S_OK;
14562
14563 return directControl->OnCPUChange(aCPU, aRemove);
14564}
14565
14566HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14567{
14568 LogFlowThisFunc(("\n"));
14569
14570 AutoCaller autoCaller(this);
14571 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14572
14573 ComPtr<IInternalSessionControl> directControl;
14574 {
14575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14576 if (mData->mSession.mLockType == LockType_VM)
14577 directControl = mData->mSession.mDirectControl;
14578 }
14579
14580 /* ignore notifications sent after #OnSessionEnd() is called */
14581 if (!directControl)
14582 return S_OK;
14583
14584 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14585}
14586
14587/**
14588 * @note Locks this object for reading.
14589 */
14590HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14591{
14592 LogFlowThisFunc(("\n"));
14593
14594 AutoCaller autoCaller(this);
14595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14596
14597 ComPtr<IInternalSessionControl> directControl;
14598 {
14599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14600 if (mData->mSession.mLockType == LockType_VM)
14601 directControl = mData->mSession.mDirectControl;
14602 }
14603
14604 /* ignore notifications sent after #OnSessionEnd() is called */
14605 if (!directControl)
14606 return S_OK;
14607
14608 return directControl->OnVRDEServerChange(aRestart);
14609}
14610
14611/**
14612 * @note Locks this object for reading.
14613 */
14614HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14615{
14616 LogFlowThisFunc(("\n"));
14617
14618 AutoCaller autoCaller(this);
14619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14620
14621 ComPtr<IInternalSessionControl> directControl;
14622 {
14623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14624 if (mData->mSession.mLockType == LockType_VM)
14625 directControl = mData->mSession.mDirectControl;
14626 }
14627
14628 /* ignore notifications sent after #OnSessionEnd() is called */
14629 if (!directControl)
14630 return S_OK;
14631
14632 return directControl->OnRecordingChange(aEnable);
14633}
14634
14635/**
14636 * @note Locks this object for reading.
14637 */
14638HRESULT SessionMachine::i_onUSBControllerChange()
14639{
14640 LogFlowThisFunc(("\n"));
14641
14642 AutoCaller autoCaller(this);
14643 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14644
14645 ComPtr<IInternalSessionControl> directControl;
14646 {
14647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14648 if (mData->mSession.mLockType == LockType_VM)
14649 directControl = mData->mSession.mDirectControl;
14650 }
14651
14652 /* ignore notifications sent after #OnSessionEnd() is called */
14653 if (!directControl)
14654 return S_OK;
14655
14656 return directControl->OnUSBControllerChange();
14657}
14658
14659/**
14660 * @note Locks this object for reading.
14661 */
14662HRESULT SessionMachine::i_onSharedFolderChange()
14663{
14664 LogFlowThisFunc(("\n"));
14665
14666 AutoCaller autoCaller(this);
14667 AssertComRCReturnRC(autoCaller.rc());
14668
14669 ComPtr<IInternalSessionControl> directControl;
14670 {
14671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14672 if (mData->mSession.mLockType == LockType_VM)
14673 directControl = mData->mSession.mDirectControl;
14674 }
14675
14676 /* ignore notifications sent after #OnSessionEnd() is called */
14677 if (!directControl)
14678 return S_OK;
14679
14680 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14681}
14682
14683/**
14684 * @note Locks this object for reading.
14685 */
14686HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14687{
14688 LogFlowThisFunc(("\n"));
14689
14690 AutoCaller autoCaller(this);
14691 AssertComRCReturnRC(autoCaller.rc());
14692
14693 ComPtr<IInternalSessionControl> directControl;
14694 {
14695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14696 if (mData->mSession.mLockType == LockType_VM)
14697 directControl = mData->mSession.mDirectControl;
14698 }
14699
14700 /* ignore notifications sent after #OnSessionEnd() is called */
14701 if (!directControl)
14702 return S_OK;
14703
14704 return directControl->OnClipboardModeChange(aClipboardMode);
14705}
14706
14707/**
14708 * @note Locks this object for reading.
14709 */
14710HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14711{
14712 LogFlowThisFunc(("\n"));
14713
14714 AutoCaller autoCaller(this);
14715 AssertComRCReturnRC(autoCaller.rc());
14716
14717 ComPtr<IInternalSessionControl> directControl;
14718 {
14719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14720 if (mData->mSession.mLockType == LockType_VM)
14721 directControl = mData->mSession.mDirectControl;
14722 }
14723
14724 /* ignore notifications sent after #OnSessionEnd() is called */
14725 if (!directControl)
14726 return S_OK;
14727
14728 return directControl->OnClipboardFileTransferModeChange(aEnable);
14729}
14730
14731/**
14732 * @note Locks this object for reading.
14733 */
14734HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14735{
14736 LogFlowThisFunc(("\n"));
14737
14738 AutoCaller autoCaller(this);
14739 AssertComRCReturnRC(autoCaller.rc());
14740
14741 ComPtr<IInternalSessionControl> directControl;
14742 {
14743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14744 if (mData->mSession.mLockType == LockType_VM)
14745 directControl = mData->mSession.mDirectControl;
14746 }
14747
14748 /* ignore notifications sent after #OnSessionEnd() is called */
14749 if (!directControl)
14750 return S_OK;
14751
14752 return directControl->OnDnDModeChange(aDnDMode);
14753}
14754
14755/**
14756 * @note Locks this object for reading.
14757 */
14758HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14759{
14760 LogFlowThisFunc(("\n"));
14761
14762 AutoCaller autoCaller(this);
14763 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14764
14765 ComPtr<IInternalSessionControl> directControl;
14766 {
14767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14768 if (mData->mSession.mLockType == LockType_VM)
14769 directControl = mData->mSession.mDirectControl;
14770 }
14771
14772 /* ignore notifications sent after #OnSessionEnd() is called */
14773 if (!directControl)
14774 return S_OK;
14775
14776 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14777}
14778
14779/**
14780 * @note Locks this object for reading.
14781 */
14782HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14783{
14784 LogFlowThisFunc(("\n"));
14785
14786 AutoCaller autoCaller(this);
14787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14788
14789 ComPtr<IInternalSessionControl> directControl;
14790 {
14791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14792 if (mData->mSession.mLockType == LockType_VM)
14793 directControl = mData->mSession.mDirectControl;
14794 }
14795
14796 /* ignore notifications sent after #OnSessionEnd() is called */
14797 if (!directControl)
14798 return S_OK;
14799
14800 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14801}
14802
14803/**
14804 * Returns @c true if this machine's USB controller reports it has a matching
14805 * filter for the given USB device and @c false otherwise.
14806 *
14807 * @note locks this object for reading.
14808 */
14809bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14810{
14811 AutoCaller autoCaller(this);
14812 /* silently return if not ready -- this method may be called after the
14813 * direct machine session has been called */
14814 if (!autoCaller.isOk())
14815 return false;
14816
14817#ifdef VBOX_WITH_USB
14818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14819
14820 switch (mData->mMachineState)
14821 {
14822 case MachineState_Starting:
14823 case MachineState_Restoring:
14824 case MachineState_TeleportingIn:
14825 case MachineState_Paused:
14826 case MachineState_Running:
14827 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14828 * elsewhere... */
14829 alock.release();
14830 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14831 default: break;
14832 }
14833#else
14834 NOREF(aDevice);
14835 NOREF(aMaskedIfs);
14836#endif
14837 return false;
14838}
14839
14840/**
14841 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14842 */
14843HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14844 IVirtualBoxErrorInfo *aError,
14845 ULONG aMaskedIfs,
14846 const com::Utf8Str &aCaptureFilename)
14847{
14848 LogFlowThisFunc(("\n"));
14849
14850 AutoCaller autoCaller(this);
14851
14852 /* This notification may happen after the machine object has been
14853 * uninitialized (the session was closed), so don't assert. */
14854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14855
14856 ComPtr<IInternalSessionControl> directControl;
14857 {
14858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14859 if (mData->mSession.mLockType == LockType_VM)
14860 directControl = mData->mSession.mDirectControl;
14861 }
14862
14863 /* fail on notifications sent after #OnSessionEnd() is called, it is
14864 * expected by the caller */
14865 if (!directControl)
14866 return E_FAIL;
14867
14868 /* No locks should be held at this point. */
14869 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14870 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14871
14872 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14873}
14874
14875/**
14876 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14877 */
14878HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14879 IVirtualBoxErrorInfo *aError)
14880{
14881 LogFlowThisFunc(("\n"));
14882
14883 AutoCaller autoCaller(this);
14884
14885 /* This notification may happen after the machine object has been
14886 * uninitialized (the session was closed), so don't assert. */
14887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14888
14889 ComPtr<IInternalSessionControl> directControl;
14890 {
14891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14892 if (mData->mSession.mLockType == LockType_VM)
14893 directControl = mData->mSession.mDirectControl;
14894 }
14895
14896 /* fail on notifications sent after #OnSessionEnd() is called, it is
14897 * expected by the caller */
14898 if (!directControl)
14899 return E_FAIL;
14900
14901 /* No locks should be held at this point. */
14902 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14903 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14904
14905 return directControl->OnUSBDeviceDetach(aId, aError);
14906}
14907
14908// protected methods
14909/////////////////////////////////////////////////////////////////////////////
14910
14911/**
14912 * Deletes the given file if it is no longer in use by either the current machine state
14913 * (if the machine is "saved") or any of the machine's snapshots.
14914 *
14915 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14916 * but is different for each SnapshotMachine. When calling this, the order of calling this
14917 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14918 * is therefore critical. I know, it's all rather messy.
14919 *
14920 * @param strStateFile
14921 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14922 * the test for whether the saved state file is in use.
14923 */
14924void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14925 Snapshot *pSnapshotToIgnore)
14926{
14927 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14928 if ( (strStateFile.isNotEmpty())
14929 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14930 )
14931 // ... and it must also not be shared with other snapshots
14932 if ( !mData->mFirstSnapshot
14933 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14934 // this checks the SnapshotMachine's state file paths
14935 )
14936 RTFileDelete(strStateFile.c_str());
14937}
14938
14939/**
14940 * Locks the attached media.
14941 *
14942 * All attached hard disks are locked for writing and DVD/floppy are locked for
14943 * reading. Parents of attached hard disks (if any) are locked for reading.
14944 *
14945 * This method also performs accessibility check of all media it locks: if some
14946 * media is inaccessible, the method will return a failure and a bunch of
14947 * extended error info objects per each inaccessible medium.
14948 *
14949 * Note that this method is atomic: if it returns a success, all media are
14950 * locked as described above; on failure no media is locked at all (all
14951 * succeeded individual locks will be undone).
14952 *
14953 * The caller is responsible for doing the necessary state sanity checks.
14954 *
14955 * The locks made by this method must be undone by calling #unlockMedia() when
14956 * no more needed.
14957 */
14958HRESULT SessionMachine::i_lockMedia()
14959{
14960 AutoCaller autoCaller(this);
14961 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14962
14963 AutoMultiWriteLock2 alock(this->lockHandle(),
14964 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14965
14966 /* bail out if trying to lock things with already set up locking */
14967 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14968
14969 MultiResult mrc(S_OK);
14970
14971 /* Collect locking information for all medium objects attached to the VM. */
14972 for (MediumAttachmentList::const_iterator
14973 it = mMediumAttachments->begin();
14974 it != mMediumAttachments->end();
14975 ++it)
14976 {
14977 MediumAttachment *pAtt = *it;
14978 DeviceType_T devType = pAtt->i_getType();
14979 Medium *pMedium = pAtt->i_getMedium();
14980
14981 MediumLockList *pMediumLockList(new MediumLockList());
14982 // There can be attachments without a medium (floppy/dvd), and thus
14983 // it's impossible to create a medium lock list. It still makes sense
14984 // to have the empty medium lock list in the map in case a medium is
14985 // attached later.
14986 if (pMedium != NULL)
14987 {
14988 MediumType_T mediumType = pMedium->i_getType();
14989 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14990 || mediumType == MediumType_Shareable;
14991 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14992
14993 alock.release();
14994 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14995 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14996 false /* fMediumLockWriteAll */,
14997 NULL,
14998 *pMediumLockList);
14999 alock.acquire();
15000 if (FAILED(mrc))
15001 {
15002 delete pMediumLockList;
15003 mData->mSession.mLockedMedia.Clear();
15004 break;
15005 }
15006 }
15007
15008 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15009 if (FAILED(rc))
15010 {
15011 mData->mSession.mLockedMedia.Clear();
15012 mrc = setError(rc,
15013 tr("Collecting locking information for all attached media failed"));
15014 break;
15015 }
15016 }
15017
15018 if (SUCCEEDED(mrc))
15019 {
15020 /* Now lock all media. If this fails, nothing is locked. */
15021 alock.release();
15022 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15023 alock.acquire();
15024 if (FAILED(rc))
15025 {
15026 mrc = setError(rc,
15027 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15028 }
15029 }
15030
15031 return mrc;
15032}
15033
15034/**
15035 * Undoes the locks made by by #lockMedia().
15036 */
15037HRESULT SessionMachine::i_unlockMedia()
15038{
15039 AutoCaller autoCaller(this);
15040 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15041
15042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15043
15044 /* we may be holding important error info on the current thread;
15045 * preserve it */
15046 ErrorInfoKeeper eik;
15047
15048 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15049 AssertComRC(rc);
15050 return rc;
15051}
15052
15053/**
15054 * Helper to change the machine state (reimplementation).
15055 *
15056 * @note Locks this object for writing.
15057 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15058 * it can cause crashes in random places due to unexpectedly committing
15059 * the current settings. The caller is responsible for that. The call
15060 * to saveStateSettings is fine, because this method does not commit.
15061 */
15062HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15063{
15064 LogFlowThisFuncEnter();
15065
15066 AutoCaller autoCaller(this);
15067 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15068
15069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15070
15071 MachineState_T oldMachineState = mData->mMachineState;
15072
15073 AssertMsgReturn(oldMachineState != aMachineState,
15074 ("oldMachineState=%s, aMachineState=%s\n",
15075 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15076 E_FAIL);
15077
15078 HRESULT rc = S_OK;
15079
15080 int stsFlags = 0;
15081 bool deleteSavedState = false;
15082
15083 /* detect some state transitions */
15084
15085 if ( ( ( oldMachineState == MachineState_Saved
15086 || oldMachineState == MachineState_AbortedSaved
15087 )
15088 && aMachineState == MachineState_Restoring
15089 )
15090 || ( ( oldMachineState == MachineState_PoweredOff
15091 || oldMachineState == MachineState_Teleported
15092 || oldMachineState == MachineState_Aborted
15093 )
15094 && ( aMachineState == MachineState_TeleportingIn
15095 || aMachineState == MachineState_Starting
15096 )
15097 )
15098 )
15099 {
15100 /* The EMT thread is about to start */
15101
15102 /* Nothing to do here for now... */
15103
15104 /// @todo NEWMEDIA don't let mDVDDrive and other children
15105 /// change anything when in the Starting/Restoring state
15106 }
15107 else if ( ( oldMachineState == MachineState_Running
15108 || oldMachineState == MachineState_Paused
15109 || oldMachineState == MachineState_Teleporting
15110 || oldMachineState == MachineState_OnlineSnapshotting
15111 || oldMachineState == MachineState_LiveSnapshotting
15112 || oldMachineState == MachineState_Stuck
15113 || oldMachineState == MachineState_Starting
15114 || oldMachineState == MachineState_Stopping
15115 || oldMachineState == MachineState_Saving
15116 || oldMachineState == MachineState_Restoring
15117 || oldMachineState == MachineState_TeleportingPausedVM
15118 || oldMachineState == MachineState_TeleportingIn
15119 )
15120 && ( aMachineState == MachineState_PoweredOff
15121 || aMachineState == MachineState_Saved
15122 || aMachineState == MachineState_Teleported
15123 || aMachineState == MachineState_Aborted
15124 || aMachineState == MachineState_AbortedSaved
15125 )
15126 )
15127 {
15128 /* The EMT thread has just stopped, unlock attached media. Note that as
15129 * opposed to locking that is done from Console, we do unlocking here
15130 * because the VM process may have aborted before having a chance to
15131 * properly unlock all media it locked. */
15132
15133 unlockMedia();
15134 }
15135
15136 if (oldMachineState == MachineState_Restoring)
15137 {
15138 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15139 {
15140 /*
15141 * delete the saved state file once the machine has finished
15142 * restoring from it (note that Console sets the state from
15143 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15144 * to give the user an ability to fix an error and retry --
15145 * we keep the saved state file in this case)
15146 */
15147 deleteSavedState = true;
15148 }
15149 }
15150 else if ( oldMachineState == MachineState_Saved
15151 && ( aMachineState == MachineState_PoweredOff
15152 || aMachineState == MachineState_Teleported
15153 )
15154 )
15155 {
15156 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15157 deleteSavedState = true;
15158 mData->mCurrentStateModified = TRUE;
15159 stsFlags |= SaveSTS_CurStateModified;
15160 }
15161 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15162 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15163
15164 if ( aMachineState == MachineState_Starting
15165 || aMachineState == MachineState_Restoring
15166 || aMachineState == MachineState_TeleportingIn
15167 )
15168 {
15169 /* set the current state modified flag to indicate that the current
15170 * state is no more identical to the state in the
15171 * current snapshot */
15172 if (!mData->mCurrentSnapshot.isNull())
15173 {
15174 mData->mCurrentStateModified = TRUE;
15175 stsFlags |= SaveSTS_CurStateModified;
15176 }
15177 }
15178
15179 if (deleteSavedState)
15180 {
15181 if (mRemoveSavedState)
15182 {
15183 Assert(!mSSData->strStateFilePath.isEmpty());
15184
15185 // it is safe to delete the saved state file if ...
15186 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15187 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15188 // ... none of the snapshots share the saved state file
15189 )
15190 RTFileDelete(mSSData->strStateFilePath.c_str());
15191 }
15192
15193 mSSData->strStateFilePath.setNull();
15194 stsFlags |= SaveSTS_StateFilePath;
15195 }
15196
15197 /* redirect to the underlying peer machine */
15198 mPeer->i_setMachineState(aMachineState);
15199
15200 if ( oldMachineState != MachineState_RestoringSnapshot
15201 && ( aMachineState == MachineState_PoweredOff
15202 || aMachineState == MachineState_Teleported
15203 || aMachineState == MachineState_Aborted
15204 || aMachineState == MachineState_AbortedSaved
15205 || aMachineState == MachineState_Saved))
15206 {
15207 /* the machine has stopped execution
15208 * (or the saved state file was adopted) */
15209 stsFlags |= SaveSTS_StateTimeStamp;
15210 }
15211
15212 if ( ( oldMachineState == MachineState_PoweredOff
15213 || oldMachineState == MachineState_Aborted
15214 || oldMachineState == MachineState_Teleported
15215 )
15216 && aMachineState == MachineState_Saved)
15217 {
15218 /* the saved state file was adopted */
15219 Assert(!mSSData->strStateFilePath.isEmpty());
15220 stsFlags |= SaveSTS_StateFilePath;
15221 }
15222
15223#ifdef VBOX_WITH_GUEST_PROPS
15224 if ( aMachineState == MachineState_PoweredOff
15225 || aMachineState == MachineState_Aborted
15226 || aMachineState == MachineState_Teleported)
15227 {
15228 /* Make sure any transient guest properties get removed from the
15229 * property store on shutdown. */
15230 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15231
15232 /* remove it from the settings representation */
15233 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15234 for (settings::GuestPropertiesList::iterator
15235 it = llGuestProperties.begin();
15236 it != llGuestProperties.end();
15237 /*nothing*/)
15238 {
15239 const settings::GuestProperty &prop = *it;
15240 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15241 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15242 {
15243 it = llGuestProperties.erase(it);
15244 fNeedsSaving = true;
15245 }
15246 else
15247 {
15248 ++it;
15249 }
15250 }
15251
15252 /* Additionally remove it from the HWData representation. Required to
15253 * keep everything in sync, as this is what the API keeps using. */
15254 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15255 for (HWData::GuestPropertyMap::iterator
15256 it = llHWGuestProperties.begin();
15257 it != llHWGuestProperties.end();
15258 /*nothing*/)
15259 {
15260 uint32_t fFlags = it->second.mFlags;
15261 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15262 {
15263 /* iterator where we need to continue after the erase call
15264 * (C++03 is a fact still, and it doesn't return the iterator
15265 * which would allow continuing) */
15266 HWData::GuestPropertyMap::iterator it2 = it;
15267 ++it2;
15268 llHWGuestProperties.erase(it);
15269 it = it2;
15270 fNeedsSaving = true;
15271 }
15272 else
15273 {
15274 ++it;
15275 }
15276 }
15277
15278 if (fNeedsSaving)
15279 {
15280 mData->mCurrentStateModified = TRUE;
15281 stsFlags |= SaveSTS_CurStateModified;
15282 }
15283 }
15284#endif /* VBOX_WITH_GUEST_PROPS */
15285
15286 rc = i_saveStateSettings(stsFlags);
15287
15288 if ( ( oldMachineState != MachineState_PoweredOff
15289 && oldMachineState != MachineState_Aborted
15290 && oldMachineState != MachineState_Teleported
15291 )
15292 && ( aMachineState == MachineState_PoweredOff
15293 || aMachineState == MachineState_Aborted
15294 || aMachineState == MachineState_Teleported
15295 )
15296 )
15297 {
15298 /* we've been shut down for any reason */
15299 /* no special action so far */
15300 }
15301
15302 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15303 LogFlowThisFuncLeave();
15304 return rc;
15305}
15306
15307/**
15308 * Sends the current machine state value to the VM process.
15309 *
15310 * @note Locks this object for reading, then calls a client process.
15311 */
15312HRESULT SessionMachine::i_updateMachineStateOnClient()
15313{
15314 AutoCaller autoCaller(this);
15315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15316
15317 ComPtr<IInternalSessionControl> directControl;
15318 {
15319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15320 AssertReturn(!!mData, E_FAIL);
15321 if (mData->mSession.mLockType == LockType_VM)
15322 directControl = mData->mSession.mDirectControl;
15323
15324 /* directControl may be already set to NULL here in #OnSessionEnd()
15325 * called too early by the direct session process while there is still
15326 * some operation (like deleting the snapshot) in progress. The client
15327 * process in this case is waiting inside Session::close() for the
15328 * "end session" process object to complete, while #uninit() called by
15329 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15330 * operation to complete. For now, we accept this inconsistent behavior
15331 * and simply do nothing here. */
15332
15333 if (mData->mSession.mState == SessionState_Unlocking)
15334 return S_OK;
15335 }
15336
15337 /* ignore notifications sent after #OnSessionEnd() is called */
15338 if (!directControl)
15339 return S_OK;
15340
15341 return directControl->UpdateMachineState(mData->mMachineState);
15342}
15343
15344
15345/*static*/
15346HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15347{
15348 va_list args;
15349 va_start(args, pcszMsg);
15350 HRESULT rc = setErrorInternalV(aResultCode,
15351 getStaticClassIID(),
15352 getStaticComponentName(),
15353 pcszMsg, args,
15354 false /* aWarning */,
15355 true /* aLogIt */);
15356 va_end(args);
15357 return rc;
15358}
15359
15360
15361HRESULT Machine::updateState(MachineState_T aState)
15362{
15363 NOREF(aState);
15364 ReturnComNotImplemented();
15365}
15366
15367HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15368{
15369 NOREF(aProgress);
15370 ReturnComNotImplemented();
15371}
15372
15373HRESULT Machine::endPowerUp(LONG aResult)
15374{
15375 NOREF(aResult);
15376 ReturnComNotImplemented();
15377}
15378
15379HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15380{
15381 NOREF(aProgress);
15382 ReturnComNotImplemented();
15383}
15384
15385HRESULT Machine::endPoweringDown(LONG aResult,
15386 const com::Utf8Str &aErrMsg)
15387{
15388 NOREF(aResult);
15389 NOREF(aErrMsg);
15390 ReturnComNotImplemented();
15391}
15392
15393HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15394 BOOL *aMatched,
15395 ULONG *aMaskedInterfaces)
15396{
15397 NOREF(aDevice);
15398 NOREF(aMatched);
15399 NOREF(aMaskedInterfaces);
15400 ReturnComNotImplemented();
15401
15402}
15403
15404HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15405{
15406 NOREF(aId); NOREF(aCaptureFilename);
15407 ReturnComNotImplemented();
15408}
15409
15410HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15411 BOOL aDone)
15412{
15413 NOREF(aId);
15414 NOREF(aDone);
15415 ReturnComNotImplemented();
15416}
15417
15418HRESULT Machine::autoCaptureUSBDevices()
15419{
15420 ReturnComNotImplemented();
15421}
15422
15423HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15424{
15425 NOREF(aDone);
15426 ReturnComNotImplemented();
15427}
15428
15429HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15430 ComPtr<IProgress> &aProgress)
15431{
15432 NOREF(aSession);
15433 NOREF(aProgress);
15434 ReturnComNotImplemented();
15435}
15436
15437HRESULT Machine::finishOnlineMergeMedium()
15438{
15439 ReturnComNotImplemented();
15440}
15441
15442HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15443 std::vector<com::Utf8Str> &aValues,
15444 std::vector<LONG64> &aTimestamps,
15445 std::vector<com::Utf8Str> &aFlags)
15446{
15447 NOREF(aNames);
15448 NOREF(aValues);
15449 NOREF(aTimestamps);
15450 NOREF(aFlags);
15451 ReturnComNotImplemented();
15452}
15453
15454HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15455 const com::Utf8Str &aValue,
15456 LONG64 aTimestamp,
15457 const com::Utf8Str &aFlags,
15458 BOOL fWasDeleted)
15459{
15460 NOREF(aName);
15461 NOREF(aValue);
15462 NOREF(aTimestamp);
15463 NOREF(aFlags);
15464 NOREF(fWasDeleted);
15465 ReturnComNotImplemented();
15466}
15467
15468HRESULT Machine::lockMedia()
15469{
15470 ReturnComNotImplemented();
15471}
15472
15473HRESULT Machine::unlockMedia()
15474{
15475 ReturnComNotImplemented();
15476}
15477
15478HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15479 ComPtr<IMediumAttachment> &aNewAttachment)
15480{
15481 NOREF(aAttachment);
15482 NOREF(aNewAttachment);
15483 ReturnComNotImplemented();
15484}
15485
15486HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15487 ULONG aCpuUser,
15488 ULONG aCpuKernel,
15489 ULONG aCpuIdle,
15490 ULONG aMemTotal,
15491 ULONG aMemFree,
15492 ULONG aMemBalloon,
15493 ULONG aMemShared,
15494 ULONG aMemCache,
15495 ULONG aPagedTotal,
15496 ULONG aMemAllocTotal,
15497 ULONG aMemFreeTotal,
15498 ULONG aMemBalloonTotal,
15499 ULONG aMemSharedTotal,
15500 ULONG aVmNetRx,
15501 ULONG aVmNetTx)
15502{
15503 NOREF(aValidStats);
15504 NOREF(aCpuUser);
15505 NOREF(aCpuKernel);
15506 NOREF(aCpuIdle);
15507 NOREF(aMemTotal);
15508 NOREF(aMemFree);
15509 NOREF(aMemBalloon);
15510 NOREF(aMemShared);
15511 NOREF(aMemCache);
15512 NOREF(aPagedTotal);
15513 NOREF(aMemAllocTotal);
15514 NOREF(aMemFreeTotal);
15515 NOREF(aMemBalloonTotal);
15516 NOREF(aMemSharedTotal);
15517 NOREF(aVmNetRx);
15518 NOREF(aVmNetTx);
15519 ReturnComNotImplemented();
15520}
15521
15522HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15523 com::Utf8Str &aResult)
15524{
15525 NOREF(aAuthParams);
15526 NOREF(aResult);
15527 ReturnComNotImplemented();
15528}
15529
15530com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15531{
15532 com::Utf8Str strControllerName = "Unknown";
15533 switch (aBusType)
15534 {
15535 case StorageBus_IDE:
15536 {
15537 strControllerName = "IDE";
15538 break;
15539 }
15540 case StorageBus_SATA:
15541 {
15542 strControllerName = "SATA";
15543 break;
15544 }
15545 case StorageBus_SCSI:
15546 {
15547 strControllerName = "SCSI";
15548 break;
15549 }
15550 case StorageBus_Floppy:
15551 {
15552 strControllerName = "Floppy";
15553 break;
15554 }
15555 case StorageBus_SAS:
15556 {
15557 strControllerName = "SAS";
15558 break;
15559 }
15560 case StorageBus_USB:
15561 {
15562 strControllerName = "USB";
15563 break;
15564 }
15565 default:
15566 break;
15567 }
15568 return strControllerName;
15569}
15570
15571HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15572{
15573 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15574
15575 AutoCaller autoCaller(this);
15576 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15577
15578 HRESULT rc = S_OK;
15579
15580 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15581 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15582 rc = getUSBDeviceFilters(usbDeviceFilters);
15583 if (FAILED(rc)) return rc;
15584
15585 NOREF(aFlags);
15586 com::Utf8Str osTypeId;
15587 ComObjPtr<GuestOSType> osType = NULL;
15588
15589 /* Get the guest os type as a string from the VB. */
15590 rc = getOSTypeId(osTypeId);
15591 if (FAILED(rc)) return rc;
15592
15593 /* Get the os type obj that coresponds, can be used to get
15594 * the defaults for this guest OS. */
15595 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15596 if (FAILED(rc)) return rc;
15597
15598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15599
15600 /* Let the OS type select 64-bit ness. */
15601 mHWData->mLongMode = osType->i_is64Bit()
15602 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15603
15604 /* Let the OS type enable the X2APIC */
15605 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15606
15607 /* This one covers IOAPICEnabled. */
15608 mBIOSSettings->i_applyDefaults(osType);
15609
15610 /* Initialize default record settings. */
15611 mRecordingSettings->i_applyDefaults();
15612
15613 /* Initialize default BIOS settings here */
15614 /* Hardware virtualization must be ON by default */
15615 mHWData->mAPIC = true;
15616 mHWData->mHWVirtExEnabled = true;
15617
15618 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15619 if (FAILED(rc)) return rc;
15620
15621 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15622 if (FAILED(rc)) return rc;
15623
15624 /* Graphics stuff. */
15625 GraphicsControllerType_T graphicsController;
15626 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15627 if (FAILED(rc)) return rc;
15628
15629 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15630 if (FAILED(rc)) return rc;
15631
15632 ULONG vramSize;
15633 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15634 if (FAILED(rc)) return rc;
15635
15636 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15637 if (FAILED(rc)) return rc;
15638
15639 BOOL fAccelerate2DVideoEnabled;
15640 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15641 if (FAILED(rc)) return rc;
15642
15643 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15644 if (FAILED(rc)) return rc;
15645
15646 BOOL fAccelerate3DEnabled;
15647 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15648 if (FAILED(rc)) return rc;
15649
15650 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15651 if (FAILED(rc)) return rc;
15652
15653 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15654 if (FAILED(rc)) return rc;
15655
15656 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15657 if (FAILED(rc)) return rc;
15658
15659 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15660 if (FAILED(rc)) return rc;
15661
15662 BOOL mRTCUseUTC;
15663 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15664 if (FAILED(rc)) return rc;
15665
15666 setRTCUseUTC(mRTCUseUTC);
15667 if (FAILED(rc)) return rc;
15668
15669 /* the setter does more than just the assignment, so use it */
15670 ChipsetType_T enmChipsetType;
15671 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15672 if (FAILED(rc)) return rc;
15673
15674 rc = COMSETTER(ChipsetType)(enmChipsetType);
15675 if (FAILED(rc)) return rc;
15676
15677 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15678 if (FAILED(rc)) return rc;
15679
15680 /* Apply IOMMU defaults. */
15681 IommuType_T enmIommuType;
15682 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15683 if (FAILED(rc)) return rc;
15684
15685 rc = COMSETTER(IommuType)(enmIommuType);
15686 if (FAILED(rc)) return rc;
15687
15688 /* Apply network adapters defaults */
15689 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15690 mNetworkAdapters[slot]->i_applyDefaults(osType);
15691
15692 /* Apply serial port defaults */
15693 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15694 mSerialPorts[slot]->i_applyDefaults(osType);
15695
15696 /* Apply parallel port defaults - not OS dependent*/
15697 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15698 mParallelPorts[slot]->i_applyDefaults();
15699
15700 /* This one covers the TPM type. */
15701 mTrustedPlatformModule->i_applyDefaults(osType);
15702
15703 /* This one covers secure boot. */
15704 rc = mNvramStore->i_applyDefaults(osType);
15705 if (FAILED(rc)) return rc;
15706
15707 /* Audio stuff. */
15708 AudioControllerType_T audioController;
15709 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15710 if (FAILED(rc)) return rc;
15711
15712 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15713 if (FAILED(rc)) return rc;
15714
15715 AudioCodecType_T audioCodec;
15716 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15717 if (FAILED(rc)) return rc;
15718
15719 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15720 if (FAILED(rc)) return rc;
15721
15722 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15723 if (FAILED(rc)) return rc;
15724
15725 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15726 if (FAILED(rc)) return rc;
15727
15728 /* Storage Controllers */
15729 StorageControllerType_T hdStorageControllerType;
15730 StorageBus_T hdStorageBusType;
15731 StorageControllerType_T dvdStorageControllerType;
15732 StorageBus_T dvdStorageBusType;
15733 BOOL recommendedFloppy;
15734 ComPtr<IStorageController> floppyController;
15735 ComPtr<IStorageController> hdController;
15736 ComPtr<IStorageController> dvdController;
15737 Utf8Str strFloppyName, strDVDName, strHDName;
15738
15739 /* GUI auto generates controller names using bus type. Do the same*/
15740 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15741
15742 /* Floppy recommended? add one. */
15743 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15744 if (FAILED(rc)) return rc;
15745 if (recommendedFloppy)
15746 {
15747 rc = addStorageController(strFloppyName,
15748 StorageBus_Floppy,
15749 floppyController);
15750 if (FAILED(rc)) return rc;
15751 }
15752
15753 /* Setup one DVD storage controller. */
15754 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15755 if (FAILED(rc)) return rc;
15756
15757 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15758 if (FAILED(rc)) return rc;
15759
15760 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15761
15762 rc = addStorageController(strDVDName,
15763 dvdStorageBusType,
15764 dvdController);
15765 if (FAILED(rc)) return rc;
15766
15767 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15768 if (FAILED(rc)) return rc;
15769
15770 /* Setup one HDD storage controller. */
15771 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15772 if (FAILED(rc)) return rc;
15773
15774 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15775 if (FAILED(rc)) return rc;
15776
15777 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15778
15779 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15780 {
15781 rc = addStorageController(strHDName,
15782 hdStorageBusType,
15783 hdController);
15784 if (FAILED(rc)) return rc;
15785
15786 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15787 if (FAILED(rc)) return rc;
15788 }
15789 else
15790 {
15791 /* The HD controller is the same as DVD: */
15792 hdController = dvdController;
15793 }
15794
15795 /* Limit the AHCI port count if it's used because windows has trouble with
15796 * too many ports and other guest (OS X in particular) may take extra long
15797 * boot: */
15798
15799 // pParent = static_cast<Medium*>(aP)
15800 IStorageController *temp = hdController;
15801 ComObjPtr<StorageController> storageController;
15802 storageController = static_cast<StorageController *>(temp);
15803
15804 // tempHDController = aHDController;
15805 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15806 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15807 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15808 storageController->COMSETTER(PortCount)(1);
15809
15810 /* USB stuff */
15811
15812 bool ohciEnabled = false;
15813
15814 ComPtr<IUSBController> usbController;
15815 BOOL recommendedUSB3;
15816 BOOL recommendedUSB;
15817 BOOL usbProxyAvailable;
15818
15819 getUSBProxyAvailable(&usbProxyAvailable);
15820 if (FAILED(rc)) return rc;
15821
15822 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15823 if (FAILED(rc)) return rc;
15824 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15825 if (FAILED(rc)) return rc;
15826
15827 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15828 {
15829#ifdef VBOX_WITH_EXTPACK
15830 /* USB 3.0 is only available if the proper ExtPack is installed. */
15831 ExtPackManager *aManager = mParent->i_getExtPackManager();
15832 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15833 {
15834 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15835 if (FAILED(rc)) return rc;
15836
15837 /* xHci includes OHCI */
15838 ohciEnabled = true;
15839 }
15840#endif
15841 }
15842 if ( !ohciEnabled
15843 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15844 {
15845 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15846 if (FAILED(rc)) return rc;
15847 ohciEnabled = true;
15848
15849#ifdef VBOX_WITH_EXTPACK
15850 /* USB 2.0 is only available if the proper ExtPack is installed.
15851 * Note. Configuring EHCI here and providing messages about
15852 * the missing extpack isn't exactly clean, but it is a
15853 * necessary evil to patch over legacy compatability issues
15854 * introduced by the new distribution model. */
15855 ExtPackManager *manager = mParent->i_getExtPackManager();
15856 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15857 {
15858 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15859 if (FAILED(rc)) return rc;
15860 }
15861#endif
15862 }
15863
15864 /* Set recommended human interface device types: */
15865 BOOL recommendedUSBHID;
15866 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15867 if (FAILED(rc)) return rc;
15868
15869 if (recommendedUSBHID)
15870 {
15871 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15872 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15873 if (!ohciEnabled && !usbDeviceFilters.isNull())
15874 {
15875 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15876 if (FAILED(rc)) return rc;
15877 }
15878 }
15879
15880 BOOL recommendedUSBTablet;
15881 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15882 if (FAILED(rc)) return rc;
15883
15884 if (recommendedUSBTablet)
15885 {
15886 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15887 if (!ohciEnabled && !usbDeviceFilters.isNull())
15888 {
15889 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15890 if (FAILED(rc)) return rc;
15891 }
15892 }
15893
15894 /* Enable the VMMDev testing feature for bootsector VMs: */
15895 if (osTypeId == "VBoxBS_64")
15896 {
15897 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15898 if (FAILED(rc))
15899 return rc;
15900 }
15901
15902 return S_OK;
15903}
15904
15905HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
15906 const com::Utf8Str &aCipher,
15907 const com::Utf8Str &aNewPassword,
15908 const com::Utf8Str &aNewPasswordId,
15909 BOOL aForce,
15910 ComPtr<IProgress> &aProgress)
15911{
15912 LogFlowFuncEnter();
15913
15914#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15915 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15916 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15917#else
15918 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15919 /** @todo */
15920 return E_NOTIMPL;
15921#endif
15922}
15923
15924HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
15925 com::Utf8Str &aPasswordId)
15926{
15927#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15928 RT_NOREF(aCipher, aPasswordId);
15929 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15930#else
15931 AutoLimitedCaller autoCaller(this);
15932 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15933
15934 PCVBOXCRYPTOIF pCryptoIf = NULL;
15935 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
15936 if (FAILED(hrc)) return hrc; /* Error is set */
15937
15938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15939
15940 if (mData->mstrKeyStore.isNotEmpty())
15941 {
15942 char *pszCipher = NULL;
15943 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
15944 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
15945 if (RT_SUCCESS(vrc))
15946 {
15947 aCipher = getCipherStringWithoutMode(pszCipher);
15948 RTStrFree(pszCipher);
15949 aPasswordId = mData->mstrKeyId;
15950 }
15951 else
15952 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
15953 tr("Failed to query the encryption settings with %Rrc"),
15954 vrc);
15955 }
15956 else
15957 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
15958
15959 mParent->i_releaseCryptoIf(pCryptoIf);
15960
15961 return hrc;
15962#endif
15963}
15964
15965HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
15966{
15967#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15968 RT_NOREF(aPassword);
15969 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15970#else
15971 AutoLimitedCaller autoCaller(this);
15972 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15973
15974 PCVBOXCRYPTOIF pCryptoIf = NULL;
15975 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
15976 if (FAILED(hrc)) return hrc; /* Error is set */
15977
15978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15979
15980 if (mData->mstrKeyStore.isNotEmpty())
15981 {
15982 char *pszCipher = NULL;
15983 uint8_t *pbDek = NULL;
15984 size_t cbDek = 0;
15985 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
15986 &pbDek, &cbDek, &pszCipher);
15987 if (RT_SUCCESS(vrc))
15988 {
15989 RTStrFree(pszCipher);
15990 RTMemSaferFree(pbDek, cbDek);
15991 }
15992 else
15993 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
15994 tr("The password supplied for the encrypted machine is incorrect"));
15995 }
15996 else
15997 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
15998
15999 mParent->i_releaseCryptoIf(pCryptoIf);
16000
16001 return hrc;
16002#endif
16003}
16004
16005HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16006 const com::Utf8Str &aPassword)
16007{
16008#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16009 RT_NOREF(aId, aPassword);
16010 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16011#else
16012 AutoLimitedCaller autoCaller(this);
16013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16014
16015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16016
16017 size_t cbPassword = aPassword.length() + 1;
16018 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16019
16020 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16021
16022 if ( mData->mAccessible
16023 && mData->mSession.mState == SessionState_Locked
16024 && mData->mSession.mLockType == LockType_VM
16025 && mData->mSession.mDirectControl != NULL)
16026 {
16027 /* get the console from the direct session */
16028 ComPtr<IConsole> console;
16029 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16030 ComAssertComRC(rc);
16031 /* send passsword to console */
16032 console->AddEncryptionPassword(Bstr(aId).raw(),
16033 Bstr(aPassword).raw(),
16034 TRUE);
16035 }
16036
16037 if (mData->mstrKeyId == aId)
16038 {
16039 HRESULT hrc = checkEncryptionPassword(aPassword);
16040 if (FAILED(hrc))
16041 return hrc;
16042
16043 if (SUCCEEDED(hrc))
16044 {
16045 /*
16046 * Encryption is used and password is correct,
16047 * Reinit the machine if required.
16048 */
16049 BOOL fAccessible;
16050 alock.release();
16051 getAccessible(&fAccessible);
16052 alock.acquire();
16053 }
16054 }
16055
16056 /*
16057 * Add the password into the NvramStore only after
16058 * the machine becomes accessible and the NvramStore
16059 * contains key id and key store.
16060 */
16061 if (mNvramStore.isNotNull())
16062 mNvramStore->i_addPassword(aId, aPassword);
16063
16064 return S_OK;
16065#endif
16066}
16067
16068HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16069 const std::vector<com::Utf8Str> &aPasswords)
16070{
16071#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16072 RT_NOREF(aIds, aPasswords);
16073 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16074#else
16075 if (aIds.size() != aPasswords.size())
16076 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16077
16078 HRESULT hrc = S_OK;
16079 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16080 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16081
16082 return hrc;
16083#endif
16084}
16085
16086HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16087{
16088#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16089 RT_NOREF(autoCaller, aId);
16090 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16091#else
16092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16093
16094 if ( mData->mAccessible
16095 && mData->mSession.mState == SessionState_Locked
16096 && mData->mSession.mLockType == LockType_VM
16097 && mData->mSession.mDirectControl != NULL)
16098 {
16099 /* get the console from the direct session */
16100 ComPtr<IConsole> console;
16101 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16102 ComAssertComRC(rc);
16103 /* send passsword to console */
16104 console->RemoveEncryptionPassword(Bstr(aId).raw());
16105 }
16106
16107 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16108 {
16109 if (Global::IsOnlineOrTransient(mData->mMachineState))
16110 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16111 alock.release();
16112 autoCaller.release();
16113 /* return because all passwords are purged when machine becomes inaccessible; */
16114 return i_setInaccessible();
16115 }
16116
16117 if (mNvramStore.isNotNull())
16118 mNvramStore->i_removePassword(aId);
16119 mData->mpKeyStore->deleteSecretKey(aId);
16120 return S_OK;
16121#endif
16122}
16123
16124HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16125{
16126#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16127 RT_NOREF(autoCaller);
16128 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16129#else
16130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16131
16132 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16133 {
16134 if (Global::IsOnlineOrTransient(mData->mMachineState))
16135 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16136 alock.release();
16137 autoCaller.release();
16138 /* return because all passwords are purged when machine becomes inaccessible; */
16139 return i_setInaccessible();
16140 }
16141
16142 mNvramStore->i_removeAllPasswords();
16143 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16144 return S_OK;
16145#endif
16146}
16147
16148#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16149HRESULT Machine::i_setInaccessible()
16150{
16151 if (!mData->mAccessible)
16152 return S_OK;
16153
16154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16155 VirtualBox *pParent = mParent;
16156 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16157 Guid id(i_getId());
16158
16159 alock.release();
16160
16161 uninit();
16162 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16163
16164 alock.acquire();
16165 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16166 return rc;
16167}
16168#endif
16169
16170/* This isn't handled entirely by the wrapper generator yet. */
16171#ifdef VBOX_WITH_XPCOM
16172NS_DECL_CLASSINFO(SessionMachine)
16173NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16174
16175NS_DECL_CLASSINFO(SnapshotMachine)
16176NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16177#endif
Note: See TracBrowser for help on using the repository browser.

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