VirtualBox

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

Last change on this file since 103131 was 103085, checked in by vboxsync, 9 months ago

Main,FE/VBoxManage,FE/VirtualBox,ValidationKit: Allow setting the primary VM execution engine to make it easier to force particular engine for testing, bugref:10583

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 581.2 KB
Line 
1/* $Id: MachineImpl.cpp 103085 2024-01-26 16:17:43Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "StorageControllerImpl.h"
61#include "MachineImplMoveVM.h"
62#include "ExtPackManagerImpl.h"
63#include "MachineLaunchVMCommonWorker.h"
64#include "CryptoUtils.h"
65
66// generated header
67#include "VBoxEvents.h"
68
69#ifdef VBOX_WITH_USB
70# include "USBProxyService.h"
71#endif
72
73#include "AutoCaller.h"
74#include "HashedPw.h"
75#include "Performance.h"
76#include "StringifyEnums.h"
77
78#include <iprt/asm.h>
79#include <iprt/path.h>
80#include <iprt/dir.h>
81#include <iprt/env.h>
82#include <iprt/lockvalidator.h>
83#include <iprt/memsafer.h>
84#include <iprt/process.h>
85#include <iprt/cpp/utils.h>
86#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
87#include <iprt/sha.h>
88#include <iprt/string.h>
89#include <iprt/ctype.h>
90
91#include <VBox/com/array.h>
92#include <VBox/com/list.h>
93#include <VBox/VBoxCryptoIf.h>
94
95#include <VBox/err.h>
96#include <VBox/param.h>
97#include <VBox/settings.h>
98#include <VBox/VMMDev.h>
99#include <VBox/vmm/ssm.h>
100
101#ifdef VBOX_WITH_GUEST_PROPS
102# include <VBox/HostServices/GuestPropertySvc.h>
103# include <VBox/com/array.h>
104#endif
105
106#ifdef VBOX_WITH_SHARED_CLIPBOARD
107# include <VBox/HostServices/VBoxClipboardSvc.h>
108#endif
109
110#include "VBox/com/MultiResult.h"
111
112#include <algorithm>
113
114#ifdef VBOX_WITH_DTRACE_R3_MAIN
115# include "dtrace/VBoxAPI.h"
116#endif
117
118#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
119# define HOSTSUFF_EXE ".exe"
120#else /* !RT_OS_WINDOWS */
121# define HOSTSUFF_EXE ""
122#endif /* !RT_OS_WINDOWS */
123
124// defines / prototypes
125/////////////////////////////////////////////////////////////////////////////
126
127#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
128# define BUF_DATA_SIZE _64K
129
130enum CipherMode
131{
132 CipherModeGcm = 0,
133 CipherModeCtr,
134 CipherModeXts,
135 CipherModeMax
136};
137
138enum AesSize
139{
140 Aes128 = 0,
141 Aes256,
142 AesMax
143};
144
145const char *g_apszCipher[AesMax][CipherModeMax] =
146{
147 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
148 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
149};
150const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
151
152static const char *getCipherString(const char *pszAlgo, const int iMode)
153{
154 if (iMode >= CipherModeMax)
155 return pszAlgo;
156
157 for (int i = 0; i < AesMax; i++)
158 {
159 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
160 return g_apszCipher[i][iMode];
161 }
162 return pszAlgo;
163}
164
165static const char *getCipherStringWithoutMode(const char *pszAlgo)
166{
167 for (int i = 0; i < AesMax; i++)
168 {
169 for (int j = 0; j < CipherModeMax; j++)
170 {
171 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
172 return g_apszCipherAlgo[i];
173 }
174 }
175 return pszAlgo;
176}
177#endif
178
179/////////////////////////////////////////////////////////////////////////////
180// Machine::Data structure
181/////////////////////////////////////////////////////////////////////////////
182
183Machine::Data::Data()
184{
185 mRegistered = FALSE;
186 pMachineConfigFile = NULL;
187 /* Contains hints on what has changed when the user is using the VM (config
188 * changes, running the VM, ...). This is used to decide if a config needs
189 * to be written to disk. */
190 flModifications = 0;
191 /* VM modification usually also trigger setting the current state to
192 * "Modified". Although this is not always the case. An e.g. is the VM
193 * initialization phase or when snapshot related data is changed. The
194 * actually behavior is controlled by the following flag. */
195 m_fAllowStateModification = false;
196 mAccessible = FALSE;
197 /* mUuid is initialized in Machine::init() */
198
199 mMachineState = MachineState_PoweredOff;
200 RTTimeNow(&mLastStateChange);
201
202 mMachineStateDeps = 0;
203 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
204 mMachineStateChangePending = 0;
205
206 mCurrentStateModified = TRUE;
207 mGuestPropertiesModified = FALSE;
208
209 mSession.mPID = NIL_RTPROCESS;
210 mSession.mLockType = LockType_Null;
211 mSession.mState = SessionState_Unlocked;
212
213#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
214 mpKeyStore = NULL;
215#endif
216}
217
218Machine::Data::~Data()
219{
220 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
221 {
222 RTSemEventMultiDestroy(mMachineStateDepsSem);
223 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
224 }
225 if (pMachineConfigFile)
226 {
227 delete pMachineConfigFile;
228 pMachineConfigFile = NULL;
229 }
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine::HWData structure
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::HWData::HWData()
237{
238 /* default values for a newly created machine for x86. */
239 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
240 mMemorySize = 128;
241 mCPUCount = 1;
242 mCPUHotPlugEnabled = false;
243 mMemoryBalloonSize = 0;
244 mPageFusionEnabled = false;
245 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
246 mCpuIdPortabilityLevel = 0;
247 mCpuProfile = "host";
248
249 /* default boot order: floppy - DVD - HDD */
250 mBootOrder[0] = DeviceType_Floppy;
251 mBootOrder[1] = DeviceType_DVD;
252 mBootOrder[2] = DeviceType_HardDisk;
253 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
254 mBootOrder[i] = DeviceType_Null;
255
256 mClipboardMode = ClipboardMode_Disabled;
257 mClipboardFileTransfersEnabled = FALSE;
258
259 mDnDMode = DnDMode_Disabled;
260
261 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
262 mPointingHIDType = PointingHIDType_PS2Mouse;
263 mParavirtProvider = ParavirtProvider_Default;
264 mEmulatedUSBCardReaderEnabled = FALSE;
265
266 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
267 mCPUAttached[i] = false;
268
269 mIOCacheEnabled = true;
270 mIOCacheSize = 5; /* 5MB */
271}
272
273Machine::HWData::~HWData()
274{
275}
276
277/////////////////////////////////////////////////////////////////////////////
278// Machine class
279/////////////////////////////////////////////////////////////////////////////
280
281// constructor / destructor
282/////////////////////////////////////////////////////////////////////////////
283
284Machine::Machine() :
285#ifdef VBOX_WITH_RESOURCE_USAGE_API
286 mCollectorGuest(NULL),
287#endif
288 mPeer(NULL),
289 mParent(NULL),
290 mSerialPorts(),
291 mParallelPorts(),
292 uRegistryNeedsSaving(0)
293{}
294
295Machine::~Machine()
296{}
297
298HRESULT Machine::FinalConstruct()
299{
300 LogFlowThisFunc(("\n"));
301 return BaseFinalConstruct();
302}
303
304void Machine::FinalRelease()
305{
306 LogFlowThisFunc(("\n"));
307 uninit();
308 BaseFinalRelease();
309}
310
311/**
312 * Initializes a new machine instance; this init() variant creates a new, empty machine.
313 * This gets called from VirtualBox::CreateMachine().
314 *
315 * @param aParent Associated parent object.
316 * @param strConfigFile Local file system path to the VM settings (can be relative to the VirtualBox config directory).
317 * @param strName Name for the machine.
318 * @param aArchitecture Architecture to use for the machine.
319 * If a valid guest OS type is set via \a aOsType, the guest OS' type will be used instead then.
320 * @param llGroups list of groups for the machine.
321 * @param strOsType OS Type string (stored as is if aOsType is NULL).
322 * @param aOsType OS Type of this machine or NULL.
323 * @param aId UUID for the new machine.
324 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
325 * @param fDirectoryIncludesUUID
326 * Whether the use a special VM directory naming scheme (includes the UUID).
327 * @param aCipher The cipher to encrypt the VM with.
328 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
329 * @param aPassword The password to encrypt the VM with.
330 *
331 * @return Success indicator. if not S_OK, the machine object is invalid.
332 */
333HRESULT Machine::init(VirtualBox *aParent,
334 const Utf8Str &strConfigFile,
335 const Utf8Str &strName,
336 PlatformArchitecture_T aArchitecture,
337 const StringsList &llGroups,
338 const Utf8Str &strOsType,
339 GuestOSType *aOsType,
340 const Guid &aId,
341 bool fForceOverwrite,
342 bool fDirectoryIncludesUUID,
343 const com::Utf8Str &aCipher,
344 const com::Utf8Str &aPasswordId,
345 const com::Utf8Str &aPassword)
346{
347 LogFlowThisFuncEnter();
348 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
349
350#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
351 RT_NOREF(aCipher);
352 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
353 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
354#endif
355
356 /* Enclose the state transition NotReady->InInit->Ready */
357 AutoInitSpan autoInitSpan(this);
358 AssertReturn(autoInitSpan.isOk(), E_FAIL);
359
360 HRESULT hrc = initImpl(aParent, strConfigFile);
361 if (FAILED(hrc)) return hrc;
362
363#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
364 com::Utf8Str strSsmKeyId;
365 com::Utf8Str strSsmKeyStore;
366 com::Utf8Str strNVRAMKeyId;
367 com::Utf8Str strNVRAMKeyStore;
368
369 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
370 {
371 /* Resolve the cryptographic interface. */
372 PCVBOXCRYPTOIF pCryptoIf = NULL;
373 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
374 if (SUCCEEDED(hrc))
375 {
376 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
377 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
378 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
379
380 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
381 {
382 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
383 if (!pszCipher)
384 {
385 hrc = setError(VBOX_E_NOT_SUPPORTED,
386 tr("The cipher '%s' is not supported"), aCipher.c_str());
387 break;
388 }
389
390 VBOXCRYPTOCTX hCryptoCtx;
391 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
392 if (RT_FAILURE(vrc))
393 {
394 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
395 break;
396 }
397
398 char *pszKeyStore;
399 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
400 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
401 AssertRC(vrc2);
402
403 if (RT_FAILURE(vrc))
404 {
405 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
406 break;
407 }
408
409 *(astrKeyStore[i]) = pszKeyStore;
410 RTMemFree(pszKeyStore);
411 *(astrKeyId[i]) = aPasswordId;
412 }
413
414 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
415 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
416
417 if (FAILED(hrc))
418 return hrc; /* Error is set. */
419 }
420 else
421 return hrc; /* Error is set. */
422 }
423#endif
424
425 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
426 if (FAILED(hrc)) return hrc;
427
428 if (SUCCEEDED(hrc))
429 {
430 // create an empty machine config
431 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
432
433 hrc = initDataAndChildObjects();
434 }
435
436 if (SUCCEEDED(hrc))
437 {
438#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
439 mSSData->strStateKeyId = strSsmKeyId;
440 mSSData->strStateKeyStore = strSsmKeyStore;
441#endif
442
443 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
444 mData->mAccessible = TRUE;
445
446 unconst(mData->mUuid) = aId;
447
448 mUserData->s.strName = strName;
449
450 if (llGroups.size())
451 mUserData->s.llGroups = llGroups;
452
453 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
454 // the "name sync" flag determines whether the machine directory gets renamed along
455 // with the machine file; say so if the settings file name is the same as the
456 // settings file parent directory (machine directory)
457 mUserData->s.fNameSync = i_isInOwnDir();
458
459 // initialize the default snapshots folder
460 hrc = COMSETTER(SnapshotFolder)(NULL);
461 AssertComRC(hrc);
462
463 /* Use the platform architecture which was handed-in by default. */
464 PlatformArchitecture_T enmPlatformArch = aArchitecture;
465
466 if (aOsType)
467 {
468 /* Store OS type */
469 mUserData->s.strOsType = aOsType->i_id();
470
471 /* Use the platform architecture of the found guest OS type. */
472 enmPlatformArch = aOsType->i_platformArchitecture();
473 }
474 else if (!strOsType.isEmpty())
475 {
476 /* Store OS type */
477 mUserData->s.strOsType = strOsType;
478 }
479
480 /* Set the platform architecture first before applying the defaults below. */
481 hrc = mPlatform->i_initArchitecture(enmPlatformArch);
482 if (FAILED(hrc)) return hrc;
483
484 /* Apply platform defaults. */
485 mPlatform->i_applyDefaults(aOsType);
486 i_platformPropertiesUpdate();
487
488 /* Apply firmware defaults. */
489 mFirmwareSettings->i_applyDefaults(aOsType);
490
491 /* Apply TPM defaults. */
492 mTrustedPlatformModule->i_applyDefaults(aOsType);
493
494 /* Apply recording defaults. */
495 mRecordingSettings->i_applyDefaults();
496
497 /* Apply network adapters defaults */
498 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
499 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
500
501 /* Apply serial port defaults */
502 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
503 mSerialPorts[slot]->i_applyDefaults(aOsType);
504
505 /* Apply parallel port defaults */
506 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
507 mParallelPorts[slot]->i_applyDefaults();
508
509 /* Enable the VMMDev testing feature for bootsector VMs: */
510 if (aOsType && aOsType->i_id() == GUEST_OS_ID_STR_X64("VBoxBS"))
511 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
512
513#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
514 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
515#endif
516 if (SUCCEEDED(hrc))
517 {
518 /* At this point the changing of the current state modification
519 * flag is allowed. */
520 i_allowStateModification();
521
522 /* commit all changes made during the initialization */
523 i_commit();
524 }
525 }
526
527 /* Confirm a successful initialization when it's the case */
528 if (SUCCEEDED(hrc))
529 {
530#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
531 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
532 {
533 size_t cbPassword = aPassword.length() + 1;
534 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
535 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
536 }
537#endif
538
539 if (mData->mAccessible)
540 autoInitSpan.setSucceeded();
541 else
542 autoInitSpan.setLimited();
543 }
544
545 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
546 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
547 mData->mRegistered,
548 mData->mAccessible,
549 hrc));
550
551 LogFlowThisFuncLeave();
552
553 return hrc;
554}
555
556/**
557 * Initializes a new instance with data from machine XML (formerly Init_Registered).
558 * Gets called in two modes:
559 *
560 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
561 * UUID is specified and we mark the machine as "registered";
562 *
563 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
564 * and the machine remains unregistered until RegisterMachine() is called.
565 *
566 * @param aParent Associated parent object
567 * @param strConfigFile Local file system path to the VM settings file (can
568 * be relative to the VirtualBox config directory).
569 * @param aId UUID of the machine or NULL (see above).
570 * @param strPassword Password for decrypting the config
571 *
572 * @return Success indicator. if not S_OK, the machine object is invalid
573 */
574HRESULT Machine::initFromSettings(VirtualBox *aParent,
575 const Utf8Str &strConfigFile,
576 const Guid *aId,
577 const com::Utf8Str &strPassword)
578{
579 LogFlowThisFuncEnter();
580 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
581
582 PCVBOXCRYPTOIF pCryptoIf = NULL;
583#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
584 if (strPassword.isNotEmpty())
585 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
586#else
587 if (strPassword.isNotEmpty())
588 {
589 /* Get at the crpytographic interface. */
590 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
591 if (FAILED(hrc))
592 return hrc; /* Error is set. */
593 }
594#endif
595
596 /* Enclose the state transition NotReady->InInit->Ready */
597 AutoInitSpan autoInitSpan(this);
598 AssertReturn(autoInitSpan.isOk(), E_FAIL);
599
600 HRESULT hrc = initImpl(aParent, strConfigFile);
601 if (FAILED(hrc)) return hrc;
602
603 if (aId)
604 {
605 // loading a registered VM:
606 unconst(mData->mUuid) = *aId;
607 mData->mRegistered = TRUE;
608 // now load the settings from XML:
609 hrc = i_registeredInit();
610 // this calls initDataAndChildObjects() and loadSettings()
611 }
612 else
613 {
614 // opening an unregistered VM (VirtualBox::OpenMachine()):
615 hrc = initDataAndChildObjects();
616 if (SUCCEEDED(hrc))
617 {
618 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
619 mData->mAccessible = TRUE;
620
621 try
622 {
623 // load and parse machine XML; this will throw on XML or logic errors
624 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
625 pCryptoIf,
626 strPassword.c_str());
627
628 // reject VM UUID duplicates, they can happen if someone
629 // tries to register an already known VM config again
630 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
631 true /* fPermitInaccessible */,
632 false /* aDoSetError */,
633 NULL) != VBOX_E_OBJECT_NOT_FOUND)
634 {
635 throw setError(E_FAIL,
636 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
637 mData->m_strConfigFile.c_str());
638 }
639
640 // use UUID from machine config
641 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
642
643#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
644 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
645 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
646 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
647 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
648#endif
649
650 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
651 {
652 // We just set the inaccessible state and fill the error info allowing the caller
653 // to register the machine with encrypted config even if the password is incorrect
654 mData->mAccessible = FALSE;
655
656 /* fetch the current error info */
657 mData->mAccessError = com::ErrorInfo();
658
659 setError(VBOX_E_PASSWORD_INCORRECT,
660 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
661 mData->pMachineConfigFile->uuid.raw());
662 }
663 else
664 {
665#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
666 if (strPassword.isNotEmpty())
667 {
668 size_t cbKey = strPassword.length() + 1; /* Include terminator */
669 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
670 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
671 }
672#endif
673
674 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
675 if (FAILED(hrc)) throw hrc;
676
677 /* At this point the changing of the current state modification
678 * flag is allowed. */
679 i_allowStateModification();
680
681 i_commit();
682 }
683 }
684 catch (HRESULT err)
685 {
686 /* we assume that error info is set by the thrower */
687 hrc = err;
688 }
689 catch (...)
690 {
691 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
692 }
693 }
694 }
695
696 /* Confirm a successful initialization when it's the case */
697 if (SUCCEEDED(hrc))
698 {
699 if (mData->mAccessible)
700 autoInitSpan.setSucceeded();
701 else
702 {
703 autoInitSpan.setLimited();
704
705 // uninit media from this machine's media registry, or else
706 // reloading the settings will fail
707 mParent->i_unregisterMachineMedia(i_getId());
708 }
709 }
710
711#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
712 if (pCryptoIf)
713 {
714 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
715 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
716 }
717#endif
718
719 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
720 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
721
722 LogFlowThisFuncLeave();
723
724 return hrc;
725}
726
727/**
728 * Initializes a new instance from a machine config that is already in memory
729 * (import OVF case). Since we are importing, the UUID in the machine
730 * config is ignored and we always generate a fresh one.
731 *
732 * @param aParent Associated parent object.
733 * @param strName Name for the new machine; this overrides what is specified in config.
734 * @param strSettingsFilename File name of .vbox file.
735 * @param config Machine configuration loaded and parsed from XML.
736 *
737 * @return Success indicator. if not S_OK, the machine object is invalid
738 */
739HRESULT Machine::init(VirtualBox *aParent,
740 const Utf8Str &strName,
741 const Utf8Str &strSettingsFilename,
742 const settings::MachineConfigFile &config)
743{
744 LogFlowThisFuncEnter();
745
746 /* Enclose the state transition NotReady->InInit->Ready */
747 AutoInitSpan autoInitSpan(this);
748 AssertReturn(autoInitSpan.isOk(), E_FAIL);
749
750 HRESULT hrc = initImpl(aParent, strSettingsFilename);
751 if (FAILED(hrc)) return hrc;
752
753 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
754 if (FAILED(hrc)) return hrc;
755
756 hrc = initDataAndChildObjects();
757 if (SUCCEEDED(hrc))
758 {
759 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
760 mData->mAccessible = TRUE;
761
762 // create empty machine config for instance data
763 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
764
765 // generate fresh UUID, ignore machine config
766 unconst(mData->mUuid).create();
767
768 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
769
770 // override VM name as well, it may be different
771 mUserData->s.strName = strName;
772
773 if (SUCCEEDED(hrc))
774 {
775 /* At this point the changing of the current state modification
776 * flag is allowed. */
777 i_allowStateModification();
778
779 /* commit all changes made during the initialization */
780 i_commit();
781 }
782 }
783
784 /* Confirm a successful initialization when it's the case */
785 if (SUCCEEDED(hrc))
786 {
787 if (mData->mAccessible)
788 autoInitSpan.setSucceeded();
789 else
790 {
791 /* Ignore all errors from unregistering, they would destroy
792- * the more interesting error information we already have,
793- * pinpointing the issue with the VM config. */
794 ErrorInfoKeeper eik;
795
796 autoInitSpan.setLimited();
797
798 // uninit media from this machine's media registry, or else
799 // reloading the settings will fail
800 mParent->i_unregisterMachineMedia(i_getId());
801 }
802 }
803
804 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
805 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
806
807 LogFlowThisFuncLeave();
808
809 return hrc;
810}
811
812/**
813 * Shared code between the various init() implementations.
814 *
815 * @returns HRESULT
816 * @param aParent The VirtualBox object.
817 * @param strConfigFile Settings file.
818 */
819HRESULT Machine::initImpl(VirtualBox *aParent,
820 const Utf8Str &strConfigFile)
821{
822 LogFlowThisFuncEnter();
823
824 AssertReturn(aParent, E_INVALIDARG);
825 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
826
827 HRESULT hrc = S_OK;
828
829 /* share the parent weakly */
830 unconst(mParent) = aParent;
831
832 /* allocate the essential machine data structure (the rest will be
833 * allocated later by initDataAndChildObjects() */
834 mData.allocate();
835
836 /* memorize the config file name (as provided) */
837 mData->m_strConfigFile = strConfigFile;
838
839 /* get the full file name */
840 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
841 if (RT_FAILURE(vrc1))
842 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
843 tr("Invalid machine settings file name '%s' (%Rrc)"),
844 strConfigFile.c_str(),
845 vrc1);
846
847#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
848 /** @todo Only create when the machine is going to be encrypted. */
849 /* Non-pageable memory is not accessible for non-VM process */
850 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
851 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
852#endif
853
854 LogFlowThisFuncLeave();
855
856 return hrc;
857}
858
859/**
860 * Tries to create a machine settings file in the path stored in the machine
861 * instance data. Used when a new machine is created to fail gracefully if
862 * the settings file could not be written (e.g. because machine dir is read-only).
863 * @return
864 */
865HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
866{
867 HRESULT hrc = S_OK;
868
869 // when we create a new machine, we must be able to create the settings file
870 RTFILE f = NIL_RTFILE;
871 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
872 if ( RT_SUCCESS(vrc)
873 || vrc == VERR_SHARING_VIOLATION
874 )
875 {
876 if (RT_SUCCESS(vrc))
877 RTFileClose(f);
878 if (!fForceOverwrite)
879 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
880 else
881 {
882 /* try to delete the config file, as otherwise the creation
883 * of a new settings file will fail. */
884 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
885 }
886 }
887 else if ( vrc != VERR_FILE_NOT_FOUND
888 && vrc != VERR_PATH_NOT_FOUND
889 )
890 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
891 mData->m_strConfigFileFull.c_str(), vrc);
892 return hrc;
893}
894
895/**
896 * Initializes the registered machine by loading the settings file.
897 * This method is separated from #init() in order to make it possible to
898 * retry the operation after VirtualBox startup instead of refusing to
899 * startup the whole VirtualBox server in case if the settings file of some
900 * registered VM is invalid or inaccessible.
901 *
902 * @note Must be always called from this object's write lock
903 * (unless called from #init() that doesn't need any locking).
904 * @note Locks the mUSBController method for writing.
905 * @note Subclasses must not call this method.
906 */
907HRESULT Machine::i_registeredInit()
908{
909 AssertReturn(!i_isSessionMachine(), E_FAIL);
910 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
911 AssertReturn(mData->mUuid.isValid(), E_FAIL);
912 AssertReturn(!mData->mAccessible, E_FAIL);
913
914 HRESULT hrc = initDataAndChildObjects();
915 if (SUCCEEDED(hrc))
916 {
917 /* Temporarily reset the registered flag in order to let setters
918 * potentially called from loadSettings() succeed (isMutable() used in
919 * all setters will return FALSE for a Machine instance if mRegistered
920 * is TRUE). */
921 mData->mRegistered = FALSE;
922
923 PCVBOXCRYPTOIF pCryptoIf = NULL;
924 SecretKey *pKey = NULL;
925 const char *pszPassword = NULL;
926#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
927 /* Resolve password and cryptographic support interface if machine is encrypted. */
928 if (mData->mstrKeyId.isNotEmpty())
929 {
930 /* Get at the crpytographic interface. */
931 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
932 if (SUCCEEDED(hrc))
933 {
934 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
935 if (RT_SUCCESS(vrc))
936 pszPassword = (const char *)pKey->getKeyBuffer();
937 else
938 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
939 mData->mstrKeyId.c_str(), vrc);
940 }
941 }
942#else
943 RT_NOREF(pKey);
944#endif
945
946 if (SUCCEEDED(hrc))
947 {
948 try
949 {
950 // load and parse machine XML; this will throw on XML or logic errors
951 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
952 pCryptoIf, pszPassword);
953
954 if (mData->mUuid != mData->pMachineConfigFile->uuid)
955 throw setError(E_FAIL,
956 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
957 mData->pMachineConfigFile->uuid.raw(),
958 mData->m_strConfigFileFull.c_str(),
959 mData->mUuid.toString().c_str(),
960 mParent->i_settingsFilePath().c_str());
961
962#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
963 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
964 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
965 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
966 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
967
968 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
969 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
970 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
971 mData->pMachineConfigFile->uuid.raw());
972 else
973#endif
974 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
975 if (FAILED(hrc)) throw hrc;
976 }
977 catch (HRESULT err)
978 {
979 /* we assume that error info is set by the thrower */
980 hrc = err;
981 }
982 catch (...)
983 {
984 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
985 }
986
987 /* Restore the registered flag (even on failure) */
988 mData->mRegistered = TRUE;
989 }
990
991#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
992 if (pCryptoIf)
993 mParent->i_releaseCryptoIf(pCryptoIf);
994 if (pKey)
995 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
996#endif
997 }
998
999 if (SUCCEEDED(hrc))
1000 {
1001 /* Set mAccessible to TRUE only if we successfully locked and loaded
1002 * the settings file */
1003 mData->mAccessible = TRUE;
1004
1005 /* commit all changes made during loading the settings file */
1006 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1007 /// @todo r=klaus for some reason the settings loading logic backs up
1008 // the settings, and therefore a commit is needed. Should probably be changed.
1009 }
1010 else
1011 {
1012 /* If the machine is registered, then, instead of returning a
1013 * failure, we mark it as inaccessible and set the result to
1014 * success to give it a try later */
1015
1016 /* fetch the current error info */
1017 mData->mAccessError = com::ErrorInfo();
1018 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1019
1020 /* rollback all changes */
1021 i_rollback(false /* aNotify */);
1022
1023 // uninit media from this machine's media registry, or else
1024 // reloading the settings will fail
1025 mParent->i_unregisterMachineMedia(i_getId());
1026
1027 /* uninitialize the common part to make sure all data is reset to
1028 * default (null) values */
1029 uninitDataAndChildObjects();
1030
1031 hrc = S_OK;
1032 }
1033
1034 return hrc;
1035}
1036
1037/**
1038 * Uninitializes the instance.
1039 * Called either from FinalRelease() or by the parent when it gets destroyed.
1040 *
1041 * @note The caller of this method must make sure that this object
1042 * a) doesn't have active callers on the current thread and b) is not locked
1043 * by the current thread; otherwise uninit() will hang either a) due to
1044 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1045 * a dead-lock caused by this thread waiting for all callers on the other
1046 * threads are done but preventing them from doing so by holding a lock.
1047 */
1048void Machine::uninit()
1049{
1050 LogFlowThisFuncEnter();
1051
1052 Assert(!isWriteLockOnCurrentThread());
1053
1054 Assert(!uRegistryNeedsSaving);
1055 if (uRegistryNeedsSaving)
1056 {
1057 AutoCaller autoCaller(this);
1058 if (SUCCEEDED(autoCaller.hrc()))
1059 {
1060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1061 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1062 }
1063 }
1064
1065 /* Enclose the state transition Ready->InUninit->NotReady */
1066 AutoUninitSpan autoUninitSpan(this);
1067 if (autoUninitSpan.uninitDone())
1068 return;
1069
1070 Assert(!i_isSnapshotMachine());
1071 Assert(!i_isSessionMachine());
1072 Assert(!!mData);
1073
1074 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1075 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1076
1077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 if (!mData->mSession.mMachine.isNull())
1080 {
1081 /* Theoretically, this can only happen if the VirtualBox server has been
1082 * terminated while there were clients running that owned open direct
1083 * sessions. Since in this case we are definitely called by
1084 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1085 * won't happen on the client watcher thread (because it has a
1086 * VirtualBox caller for the duration of the
1087 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1088 * cannot happen until the VirtualBox caller is released). This is
1089 * important, because SessionMachine::uninit() cannot correctly operate
1090 * after we return from this method (it expects the Machine instance is
1091 * still valid). We'll call it ourselves below.
1092 */
1093 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1094 (SessionMachine*)mData->mSession.mMachine));
1095
1096 if (Global::IsOnlineOrTransient(mData->mMachineState))
1097 {
1098 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1099 /* set machine state using SessionMachine reimplementation */
1100 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1101 }
1102
1103 /*
1104 * Uninitialize SessionMachine using public uninit() to indicate
1105 * an unexpected uninitialization.
1106 */
1107 mData->mSession.mMachine->uninit();
1108 /* SessionMachine::uninit() must set mSession.mMachine to null */
1109 Assert(mData->mSession.mMachine.isNull());
1110 }
1111
1112 // uninit media from this machine's media registry, if they're still there
1113 Guid uuidMachine(i_getId());
1114
1115 /* the lock is no more necessary (SessionMachine is uninitialized) */
1116 alock.release();
1117
1118 /* XXX This will fail with
1119 * "cannot be closed because it is still attached to 1 virtual machines"
1120 * because at this point we did not call uninitDataAndChildObjects() yet
1121 * and therefore also removeBackReference() for all these media was not called! */
1122
1123 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1124 mParent->i_unregisterMachineMedia(uuidMachine);
1125
1126 // has machine been modified?
1127 if (mData->flModifications)
1128 {
1129 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1130 i_rollback(false /* aNotify */);
1131 }
1132
1133 if (mData->mAccessible)
1134 uninitDataAndChildObjects();
1135
1136#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1137 if (mData->mpKeyStore != NULL)
1138 delete mData->mpKeyStore;
1139#endif
1140
1141 /* free the essential data structure last */
1142 mData.free();
1143
1144 LogFlowThisFuncLeave();
1145}
1146
1147// Wrapped IMachine properties
1148/////////////////////////////////////////////////////////////////////////////
1149HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1150{
1151 /* mParent is constant during life time, no need to lock */
1152 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1153 aParent = pVirtualBox;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPlatform(ComPtr<IPlatform> &aPlatform)
1159{
1160 /* mPlatform is constant during life time, no need to lock */
1161 ComObjPtr<Platform> pPlatform(mPlatform);
1162 aPlatform = pPlatform;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::getAccessible(BOOL *aAccessible)
1168{
1169 /* In some cases (medium registry related), it is necessary to be able to
1170 * go through the list of all machines. Happens when an inaccessible VM
1171 * has a sensible medium registry. */
1172 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT hrc = S_OK;
1176
1177 if (!mData->mAccessible)
1178 {
1179 /* try to initialize the VM once more if not accessible */
1180
1181 AutoReinitSpan autoReinitSpan(this);
1182 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1183
1184#ifdef DEBUG
1185 LogFlowThisFunc(("Dumping media backreferences\n"));
1186 mParent->i_dumpAllBackRefs();
1187#endif
1188
1189 if (mData->pMachineConfigFile)
1190 {
1191 // reset the XML file to force loadSettings() (called from i_registeredInit())
1192 // to parse it again; the file might have changed
1193 delete mData->pMachineConfigFile;
1194 mData->pMachineConfigFile = NULL;
1195 }
1196
1197 hrc = i_registeredInit();
1198
1199 if (SUCCEEDED(hrc) && mData->mAccessible)
1200 {
1201 autoReinitSpan.setSucceeded();
1202
1203 /* make sure interesting parties will notice the accessibility
1204 * state change */
1205 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1206 mParent->i_onMachineDataChanged(mData->mUuid);
1207 }
1208 }
1209
1210 if (SUCCEEDED(hrc))
1211 *aAccessible = mData->mAccessible;
1212
1213 LogFlowThisFuncLeave();
1214
1215 return hrc;
1216}
1217
1218HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1223 {
1224 /* return shortly */
1225 aAccessError = NULL;
1226 return S_OK;
1227 }
1228
1229 HRESULT hrc = S_OK;
1230
1231 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1232 hrc = errorInfo.createObject();
1233 if (SUCCEEDED(hrc))
1234 {
1235 errorInfo->init(mData->mAccessError.getResultCode(),
1236 mData->mAccessError.getInterfaceID().ref(),
1237 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1238 Utf8Str(mData->mAccessError.getText()));
1239 aAccessError = errorInfo;
1240 }
1241
1242 return hrc;
1243}
1244
1245HRESULT Machine::getName(com::Utf8Str &aName)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 aName = mUserData->s.strName;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setName(const com::Utf8Str &aName)
1255{
1256 // prohibit setting a UUID only as the machine name, or else it can
1257 // never be found by findMachine()
1258 Guid test(aName);
1259
1260 if (test.isValid())
1261 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1262
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1266 if (FAILED(hrc)) return hrc;
1267
1268 i_setModified(IsModified_MachineData);
1269 mUserData.backup();
1270 mUserData->s.strName = aName;
1271
1272 return S_OK;
1273}
1274
1275HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1276{
1277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 aDescription = mUserData->s.strDescription;
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1285{
1286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 // this can be done in principle in any state as it doesn't affect the VM
1289 // significantly, but play safe by not messing around while complex
1290 // activities are going on
1291 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1292 if (FAILED(hrc)) return hrc;
1293
1294 i_setModified(IsModified_MachineData);
1295 mUserData.backup();
1296 mUserData->s.strDescription = aDescription;
1297
1298 return S_OK;
1299}
1300
1301HRESULT Machine::getId(com::Guid &aId)
1302{
1303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1304
1305 aId = mData->mUuid;
1306
1307 return S_OK;
1308}
1309
1310HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1311{
1312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1313 aGroups.resize(mUserData->s.llGroups.size());
1314 size_t i = 0;
1315 for (StringsList::const_iterator
1316 it = mUserData->s.llGroups.begin();
1317 it != mUserData->s.llGroups.end();
1318 ++it, ++i)
1319 aGroups[i] = (*it);
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1325{
1326 StringsList llGroups;
1327 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1328 if (FAILED(hrc))
1329 return hrc;
1330
1331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1332
1333 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1334 if (FAILED(hrc)) return hrc;
1335
1336 i_setModified(IsModified_MachineData);
1337 mUserData.backup();
1338 mUserData->s.llGroups = llGroups;
1339
1340 mParent->i_onMachineGroupsChanged(mData->mUuid);
1341 return S_OK;
1342}
1343
1344HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1345{
1346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1347
1348 aOSTypeId = mUserData->s.strOsType;
1349
1350 return S_OK;
1351}
1352
1353HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1354{
1355 /* look up the object by Id to check it is valid */
1356 ComObjPtr<GuestOSType> pGuestOSType;
1357 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1358
1359 /* when setting, always use the "etalon" value for consistency -- lookup
1360 * by ID is case-insensitive and the input value may have different case */
1361 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1362
1363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1364
1365 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1366 if (FAILED(hrc)) return hrc;
1367
1368 i_setModified(IsModified_MachineData);
1369 mUserData.backup();
1370 mUserData->s.strOsType = osTypeId;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1376{
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1380
1381 return S_OK;
1382}
1383
1384HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1385{
1386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1387
1388 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1389 if (FAILED(hrc)) return hrc;
1390
1391 i_setModified(IsModified_MachineData);
1392 mHWData.backup();
1393 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1394
1395 return S_OK;
1396}
1397
1398HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1399{
1400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1401
1402 *aPointingHIDType = mHWData->mPointingHIDType;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1408{
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1412 if (FAILED(hrc)) return hrc;
1413
1414 i_setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mPointingHIDType = aPointingHIDType;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 aParavirtDebug = mHWData->mParavirtDebug;
1426 return S_OK;
1427}
1428
1429HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1430{
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(hrc)) return hrc;
1435
1436 /** @todo Parse/validate options? */
1437 if (aParavirtDebug != mHWData->mParavirtDebug)
1438 {
1439 i_setModified(IsModified_MachineData);
1440 mHWData.backup();
1441 mHWData->mParavirtDebug = aParavirtDebug;
1442 }
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1448{
1449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 *aParavirtProvider = mHWData->mParavirtProvider;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1457{
1458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1461 if (FAILED(hrc)) return hrc;
1462
1463 if (aParavirtProvider != mHWData->mParavirtProvider)
1464 {
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mParavirtProvider = aParavirtProvider;
1468 }
1469
1470 return S_OK;
1471}
1472
1473HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1474{
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 *aParavirtProvider = mHWData->mParavirtProvider;
1478 switch (mHWData->mParavirtProvider)
1479 {
1480 case ParavirtProvider_None:
1481 case ParavirtProvider_HyperV:
1482 case ParavirtProvider_KVM:
1483 case ParavirtProvider_Minimal:
1484 break;
1485
1486 /* Resolve dynamic provider types to the effective types. */
1487 default:
1488 {
1489 ComObjPtr<GuestOSType> pGuestOSType;
1490 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1491 pGuestOSType);
1492 if (FAILED(hrc2) || pGuestOSType.isNull())
1493 {
1494 *aParavirtProvider = ParavirtProvider_None;
1495 break;
1496 }
1497
1498 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1499 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1500
1501 switch (mHWData->mParavirtProvider)
1502 {
1503 case ParavirtProvider_Legacy:
1504 {
1505 if (fOsXGuest)
1506 *aParavirtProvider = ParavirtProvider_Minimal;
1507 else
1508 *aParavirtProvider = ParavirtProvider_None;
1509 break;
1510 }
1511
1512 case ParavirtProvider_Default:
1513 {
1514 Assert(strlen(GUEST_OS_ID_STR_X64("")) > 0);
1515 if (fOsXGuest)
1516 *aParavirtProvider = ParavirtProvider_Minimal;
1517 else if ( mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows11")
1518 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows10")
1519 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows10")
1520 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows81")
1521 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows81")
1522 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows8")
1523 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows8")
1524 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows7")
1525 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows7")
1526 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("WindowsVista")
1527 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("WindowsVista")
1528 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1529 || mUserData->s.strOsType.startsWith("Windows201"))
1530 && mUserData->s.strOsType.endsWith(GUEST_OS_ID_STR_X64("")))
1531 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2012")
1532 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2012")
1533 || mUserData->s.strOsType == GUEST_OS_ID_STR_X86("Windows2008")
1534 || mUserData->s.strOsType == GUEST_OS_ID_STR_X64("Windows2008"))
1535 *aParavirtProvider = ParavirtProvider_HyperV;
1536 else if ( guestTypeFamilyId == "Linux"
1537 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux22") // Linux22 and Linux24{_64} excluded as they're too old
1538 && mUserData->s.strOsType != GUEST_OS_ID_STR_X86("Linux24") // to have any KVM paravirtualization support.
1539 && mUserData->s.strOsType != GUEST_OS_ID_STR_X64("Linux24"))
1540 *aParavirtProvider = ParavirtProvider_KVM;
1541 else
1542 *aParavirtProvider = ParavirtProvider_None;
1543 break;
1544 }
1545
1546 default: AssertFailedBreak(); /* Shut up MSC. */
1547 }
1548 break;
1549 }
1550 }
1551
1552 Assert( *aParavirtProvider == ParavirtProvider_None
1553 || *aParavirtProvider == ParavirtProvider_Minimal
1554 || *aParavirtProvider == ParavirtProvider_HyperV
1555 || *aParavirtProvider == ParavirtProvider_KVM);
1556 return S_OK;
1557}
1558
1559HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1560{
1561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 aHardwareVersion = mHWData->mHWVersion;
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1569{
1570 /* check known version */
1571 Utf8Str hwVersion = aHardwareVersion;
1572 if ( hwVersion.compare("1") != 0
1573 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1574 return setError(E_INVALIDARG,
1575 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(hrc)) return hrc;
1581
1582 i_setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 mHWData->mHWVersion = aHardwareVersion;
1585
1586 return S_OK;
1587}
1588
1589HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1590{
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 if (!mHWData->mHardwareUUID.isZero())
1594 aHardwareUUID = mHWData->mHardwareUUID;
1595 else
1596 aHardwareUUID = mData->mUuid;
1597
1598 return S_OK;
1599}
1600
1601HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1602{
1603 if (!aHardwareUUID.isValid())
1604 return E_INVALIDARG;
1605
1606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1609 if (FAILED(hrc)) return hrc;
1610
1611 i_setModified(IsModified_MachineData);
1612 mHWData.backup();
1613 if (aHardwareUUID == mData->mUuid)
1614 mHWData->mHardwareUUID.clear();
1615 else
1616 mHWData->mHardwareUUID = aHardwareUUID;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aMemorySize = mHWData->mMemorySize;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setMemorySize(ULONG aMemorySize)
1631{
1632 /* check RAM limits */
1633 if ( aMemorySize < MM_RAM_MIN_IN_MB
1634 || aMemorySize > MM_RAM_MAX_IN_MB
1635 )
1636 return setError(E_INVALIDARG,
1637 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1638 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1639
1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1643 if (FAILED(hrc)) return hrc;
1644
1645 i_setModified(IsModified_MachineData);
1646 mHWData.backup();
1647 mHWData->mMemorySize = aMemorySize;
1648
1649 return S_OK;
1650}
1651
1652HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 *aCPUCount = mHWData->mCPUCount;
1657
1658 return S_OK;
1659}
1660
1661HRESULT Machine::setCPUCount(ULONG aCPUCount)
1662{
1663 /* check CPU limits */
1664 if ( aCPUCount < SchemaDefs::MinCPUCount
1665 || aCPUCount > SchemaDefs::MaxCPUCount
1666 )
1667 return setError(E_INVALIDARG,
1668 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1669 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1670
1671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1672
1673 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1674 if (mHWData->mCPUHotPlugEnabled)
1675 {
1676 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1677 {
1678 if (mHWData->mCPUAttached[idx])
1679 return setError(E_INVALIDARG,
1680 tr("There is still a CPU attached to socket %lu."
1681 "Detach the CPU before removing the socket"),
1682 aCPUCount, idx+1);
1683 }
1684 }
1685
1686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1687 if (FAILED(hrc)) return hrc;
1688
1689 i_setModified(IsModified_MachineData);
1690 mHWData.backup();
1691 mHWData->mCPUCount = aCPUCount;
1692
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1701
1702 return S_OK;
1703}
1704
1705HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1706{
1707 /* check throttle limits */
1708 if ( aCPUExecutionCap < 1
1709 || aCPUExecutionCap > 100
1710 )
1711 return setError(E_INVALIDARG,
1712 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1713 aCPUExecutionCap, 1, 100);
1714
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1718 if (FAILED(hrc)) return hrc;
1719
1720 alock.release();
1721 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1722 alock.acquire();
1723 if (FAILED(hrc)) return hrc;
1724
1725 i_setModified(IsModified_MachineData);
1726 mHWData.backup();
1727 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1728
1729 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1730 if (Global::IsOnline(mData->mMachineState))
1731 i_saveSettings(NULL, alock);
1732
1733 return S_OK;
1734}
1735
1736HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1737{
1738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1746{
1747 HRESULT hrc = S_OK;
1748
1749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1750
1751 hrc = i_checkStateDependency(MutableStateDep);
1752 if (FAILED(hrc)) return hrc;
1753
1754 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1755 {
1756 if (aCPUHotPlugEnabled)
1757 {
1758 i_setModified(IsModified_MachineData);
1759 mHWData.backup();
1760
1761 /* Add the amount of CPUs currently attached */
1762 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1763 mHWData->mCPUAttached[i] = true;
1764 }
1765 else
1766 {
1767 /*
1768 * We can disable hotplug only if the amount of maximum CPUs is equal
1769 * to the amount of attached CPUs
1770 */
1771 unsigned cCpusAttached = 0;
1772 unsigned iHighestId = 0;
1773
1774 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1775 {
1776 if (mHWData->mCPUAttached[i])
1777 {
1778 cCpusAttached++;
1779 iHighestId = i;
1780 }
1781 }
1782
1783 if ( (cCpusAttached != mHWData->mCPUCount)
1784 || (iHighestId >= mHWData->mCPUCount))
1785 return setError(E_INVALIDARG,
1786 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1787
1788 i_setModified(IsModified_MachineData);
1789 mHWData.backup();
1790 }
1791 }
1792
1793 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1794
1795 return hrc;
1796}
1797
1798HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1808{
1809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1812 if (SUCCEEDED(hrc))
1813 {
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1817 }
1818 return hrc;
1819}
1820
1821HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824 aCPUProfile = mHWData->mCpuProfile;
1825 return S_OK;
1826}
1827
1828HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1829{
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1832 if (SUCCEEDED(hrc))
1833 {
1834 i_setModified(IsModified_MachineData);
1835 mHWData.backup();
1836 /* Empty equals 'host'. */
1837 if (aCPUProfile.isNotEmpty())
1838 mHWData->mCpuProfile = aCPUProfile;
1839 else
1840 mHWData->mCpuProfile = "host";
1841 }
1842 return hrc;
1843}
1844
1845HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1846{
1847#ifdef VBOX_WITH_USB_CARDREADER
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1851
1852 return S_OK;
1853#else
1854 NOREF(aEmulatedUSBCardReaderEnabled);
1855 return E_NOTIMPL;
1856#endif
1857}
1858
1859HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1860{
1861#ifdef VBOX_WITH_USB_CARDREADER
1862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1865 if (FAILED(hrc)) return hrc;
1866
1867 i_setModified(IsModified_MachineData);
1868 mHWData.backup();
1869 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1870
1871 return S_OK;
1872#else
1873 NOREF(aEmulatedUSBCardReaderEnabled);
1874 return E_NOTIMPL;
1875#endif
1876}
1877
1878/** @todo this method should not be public */
1879HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1880{
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1884
1885 return S_OK;
1886}
1887
1888/**
1889 * Set the memory balloon size.
1890 *
1891 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1892 * we have to make sure that we never call IGuest from here.
1893 */
1894HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1895{
1896 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1897#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1898 /* check limits */
1899 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1900 return setError(E_INVALIDARG,
1901 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1902 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1903
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1907 if (FAILED(hrc)) return hrc;
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1912
1913 return S_OK;
1914#else
1915 NOREF(aMemoryBalloonSize);
1916 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1917#endif
1918}
1919
1920HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1921{
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1925 return S_OK;
1926}
1927
1928HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1929{
1930#ifdef VBOX_WITH_PAGE_SHARING
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1934 if (FAILED(hrc)) return hrc;
1935
1936 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1937 i_setModified(IsModified_MachineData);
1938 mHWData.backup();
1939 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1940 return S_OK;
1941#else
1942 NOREF(aPageFusionEnabled);
1943 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1944#endif
1945}
1946
1947HRESULT Machine::getFirmwareSettings(ComPtr<IFirmwareSettings> &aFirmwareSettings)
1948{
1949 /* mFirmwareSettings is constant during life time, no need to lock */
1950 aFirmwareSettings = mFirmwareSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1956{
1957 /* mTrustedPlatformModule is constant during life time, no need to lock */
1958 aTrustedPlatformModule = mTrustedPlatformModule;
1959
1960 return S_OK;
1961}
1962
1963HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1964{
1965 /* mNvramStore is constant during life time, no need to lock */
1966 aNvramStore = mNvramStore;
1967
1968 return S_OK;
1969}
1970
1971HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1972{
1973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 aRecordingSettings = mRecordingSettings;
1976
1977 return S_OK;
1978}
1979
1980HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1981{
1982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 aGraphicsAdapter = mGraphicsAdapter;
1985
1986 return S_OK;
1987}
1988
1989HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
1994
1995 return S_OK;
1996}
1997
1998HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
1999{
2000 /** @todo (r=dmik):
2001 * 1. Allow to change the name of the snapshot folder containing snapshots
2002 * 2. Rename the folder on disk instead of just changing the property
2003 * value (to be smart and not to leave garbage). Note that it cannot be
2004 * done here because the change may be rolled back. Thus, the right
2005 * place is #saveSettings().
2006 */
2007
2008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
2010 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2011 if (FAILED(hrc)) return hrc;
2012
2013 if (!mData->mCurrentSnapshot.isNull())
2014 return setError(E_FAIL,
2015 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2016
2017 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2018
2019 if (strSnapshotFolder.isEmpty())
2020 strSnapshotFolder = "Snapshots";
2021 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2022 if (RT_FAILURE(vrc))
2023 return setErrorBoth(E_FAIL, vrc,
2024 tr("Invalid snapshot folder '%s' (%Rrc)"),
2025 strSnapshotFolder.c_str(), vrc);
2026
2027 i_setModified(IsModified_MachineData);
2028 mUserData.backup();
2029
2030 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2031
2032 return S_OK;
2033}
2034
2035HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2036{
2037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2038
2039 aMediumAttachments.resize(mMediumAttachments->size());
2040 size_t i = 0;
2041 for (MediumAttachmentList::const_iterator
2042 it = mMediumAttachments->begin();
2043 it != mMediumAttachments->end();
2044 ++it, ++i)
2045 aMediumAttachments[i] = *it;
2046
2047 return S_OK;
2048}
2049
2050HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2051{
2052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 Assert(!!mVRDEServer);
2055
2056 aVRDEServer = mVRDEServer;
2057
2058 return S_OK;
2059}
2060
2061HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2062{
2063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 aAudioSettings = mAudioSettings;
2066
2067 return S_OK;
2068}
2069
2070HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2071{
2072#ifdef VBOX_WITH_VUSB
2073 clearError();
2074 MultiResult hrcMult(S_OK);
2075
2076# ifdef VBOX_WITH_USB
2077 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2078 if (FAILED(hrcMult)) return hrcMult;
2079# endif
2080
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 aUSBControllers.resize(mUSBControllers->size());
2084 size_t i = 0;
2085 for (USBControllerList::const_iterator
2086 it = mUSBControllers->begin();
2087 it != mUSBControllers->end();
2088 ++it, ++i)
2089 aUSBControllers[i] = *it;
2090
2091 return S_OK;
2092#else
2093 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2094 * extended error info to indicate that USB is simply not available
2095 * (w/o treating it as a failure), for example, as in OSE */
2096 NOREF(aUSBControllers);
2097 ReturnComNotImplemented();
2098#endif /* VBOX_WITH_VUSB */
2099}
2100
2101HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2102{
2103#ifdef VBOX_WITH_VUSB
2104 clearError();
2105 MultiResult hrcMult(S_OK);
2106
2107# ifdef VBOX_WITH_USB
2108 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2109 if (FAILED(hrcMult)) return hrcMult;
2110# endif
2111
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 aUSBDeviceFilters = mUSBDeviceFilters;
2115 return hrcMult;
2116#else
2117 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2118 * extended error info to indicate that USB is simply not available
2119 * (w/o treating it as a failure), for example, as in OSE */
2120 NOREF(aUSBDeviceFilters);
2121 ReturnComNotImplemented();
2122#endif /* VBOX_WITH_VUSB */
2123}
2124
2125HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2126{
2127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 aSettingsFilePath = mData->m_strConfigFileFull;
2130
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2135{
2136 RT_NOREF(aSettingsFilePath);
2137 ReturnComNotImplemented();
2138}
2139
2140HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2141{
2142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2145 if (FAILED(hrc)) return hrc;
2146
2147 if (!mData->pMachineConfigFile->fileExists())
2148 // this is a new machine, and no config file exists yet:
2149 *aSettingsModified = TRUE;
2150 else
2151 *aSettingsModified = (mData->flModifications != 0);
2152
2153 return S_OK;
2154}
2155
2156HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2157{
2158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2159
2160 *aSessionState = mData->mSession.mState;
2161
2162 return S_OK;
2163}
2164
2165HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2166{
2167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 aSessionName = mData->mSession.mName;
2170
2171 return S_OK;
2172}
2173
2174HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2175{
2176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2177
2178 *aSessionPID = mData->mSession.mPID;
2179
2180 return S_OK;
2181}
2182
2183HRESULT Machine::getState(MachineState_T *aState)
2184{
2185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 *aState = mData->mMachineState;
2188 Assert(mData->mMachineState != MachineState_Null);
2189
2190 return S_OK;
2191}
2192
2193HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2194{
2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2198
2199 return S_OK;
2200}
2201
2202HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2203{
2204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2205
2206 aStateFilePath = mSSData->strStateFilePath;
2207
2208 return S_OK;
2209}
2210
2211HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2212{
2213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 i_getLogFolder(aLogFolder);
2216
2217 return S_OK;
2218}
2219
2220HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2221{
2222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2223
2224 aCurrentSnapshot = mData->mCurrentSnapshot;
2225
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2234 ? 0
2235 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2236
2237 return S_OK;
2238}
2239
2240HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2241{
2242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 /* Note: for machines with no snapshots, we always return FALSE
2245 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2246 * reasons :) */
2247
2248 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2249 ? FALSE
2250 : mData->mCurrentStateModified;
2251
2252 return S_OK;
2253}
2254
2255HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2256{
2257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 aSharedFolders.resize(mHWData->mSharedFolders.size());
2260 size_t i = 0;
2261 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2262 it = mHWData->mSharedFolders.begin();
2263 it != mHWData->mSharedFolders.end();
2264 ++it, ++i)
2265 aSharedFolders[i] = *it;
2266
2267 return S_OK;
2268}
2269
2270HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2271{
2272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 *aClipboardMode = mHWData->mClipboardMode;
2275
2276 return S_OK;
2277}
2278
2279HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2280{
2281 HRESULT hrc = S_OK;
2282
2283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2284
2285 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2286 if (FAILED(hrc)) return hrc;
2287
2288 alock.release();
2289 hrc = i_onClipboardModeChange(aClipboardMode);
2290 alock.acquire();
2291 if (FAILED(hrc)) return hrc;
2292
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mClipboardMode = aClipboardMode;
2296
2297 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2298 if (Global::IsOnline(mData->mMachineState))
2299 i_saveSettings(NULL, alock);
2300
2301 return S_OK;
2302}
2303
2304HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2305{
2306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2307
2308 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2309
2310 return S_OK;
2311}
2312
2313HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2314{
2315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2316
2317 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2318 if (FAILED(hrc)) return hrc;
2319
2320 alock.release();
2321 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2322 alock.acquire();
2323 if (FAILED(hrc)) return hrc;
2324
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2328
2329 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2330 if (Global::IsOnline(mData->mMachineState))
2331 i_saveSettings(NULL, alock);
2332
2333 return S_OK;
2334}
2335
2336HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2337{
2338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2339
2340 *aDnDMode = mHWData->mDnDMode;
2341
2342 return S_OK;
2343}
2344
2345HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2346{
2347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2348
2349 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2350 if (FAILED(hrc)) return hrc;
2351
2352 alock.release();
2353 hrc = i_onDnDModeChange(aDnDMode);
2354
2355 alock.acquire();
2356 if (FAILED(hrc)) return hrc;
2357
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mDnDMode = aDnDMode;
2361
2362 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2363 if (Global::IsOnline(mData->mMachineState))
2364 i_saveSettings(NULL, alock);
2365
2366 return S_OK;
2367}
2368
2369HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2370{
2371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 aStorageControllers.resize(mStorageControllers->size());
2374 size_t i = 0;
2375 for (StorageControllerList::const_iterator
2376 it = mStorageControllers->begin();
2377 it != mStorageControllers->end();
2378 ++it, ++i)
2379 aStorageControllers[i] = *it;
2380
2381 return S_OK;
2382}
2383
2384HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2385{
2386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2387
2388 *aEnabled = mUserData->s.fTeleporterEnabled;
2389
2390 return S_OK;
2391}
2392
2393HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2394{
2395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2396
2397 /* Only allow it to be set to true when PoweredOff or Aborted.
2398 (Clearing it is always permitted.) */
2399 if ( aTeleporterEnabled
2400 && mData->mRegistered
2401 && ( !i_isSessionMachine()
2402 || ( mData->mMachineState != MachineState_PoweredOff
2403 && mData->mMachineState != MachineState_Teleported
2404 && mData->mMachineState != MachineState_Aborted
2405 )
2406 )
2407 )
2408 return setError(VBOX_E_INVALID_VM_STATE,
2409 tr("The machine is not powered off (state is %s)"),
2410 Global::stringifyMachineState(mData->mMachineState));
2411
2412 i_setModified(IsModified_MachineData);
2413 mUserData.backup();
2414 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2415
2416 return S_OK;
2417}
2418
2419HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2420{
2421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2424
2425 return S_OK;
2426}
2427
2428HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2429{
2430 if (aTeleporterPort >= _64K)
2431 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2432
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2436 if (FAILED(hrc)) return hrc;
2437
2438 i_setModified(IsModified_MachineData);
2439 mUserData.backup();
2440 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2441
2442 return S_OK;
2443}
2444
2445HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2446{
2447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2448
2449 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2450
2451 return S_OK;
2452}
2453
2454HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2455{
2456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2459 if (FAILED(hrc)) return hrc;
2460
2461 i_setModified(IsModified_MachineData);
2462 mUserData.backup();
2463 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2464
2465 return S_OK;
2466}
2467
2468HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2469{
2470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2471 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2472
2473 return S_OK;
2474}
2475
2476HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2477{
2478 /*
2479 * Hash the password first.
2480 */
2481 com::Utf8Str aT = aTeleporterPassword;
2482
2483 if (!aT.isEmpty())
2484 {
2485 if (VBoxIsPasswordHashed(&aT))
2486 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2487 VBoxHashPassword(&aT);
2488 }
2489
2490 /*
2491 * Do the update.
2492 */
2493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2494 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2495 if (SUCCEEDED(hrc))
2496 {
2497 i_setModified(IsModified_MachineData);
2498 mUserData.backup();
2499 mUserData->s.strTeleporterPassword = aT;
2500 }
2501
2502 return hrc;
2503}
2504
2505HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2515{
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2519 if (FAILED(hrc)) return hrc;
2520
2521 i_setModified(IsModified_MachineData);
2522 mHWData.backup();
2523 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2524
2525 return S_OK;
2526}
2527
2528HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2529{
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 *aIOCacheSize = mHWData->mIOCacheSize;
2533
2534 return S_OK;
2535}
2536
2537HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2538{
2539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2540
2541 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2542 if (FAILED(hrc)) return hrc;
2543
2544 i_setModified(IsModified_MachineData);
2545 mHWData.backup();
2546 mHWData->mIOCacheSize = aIOCacheSize;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2556 aKeyId = mSSData->strStateKeyId;
2557#else
2558 aKeyId = com::Utf8Str::Empty;
2559#endif
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2569 aKeyStore = mSSData->strStateKeyStore;
2570#else
2571 aKeyStore = com::Utf8Str::Empty;
2572#endif
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2582 aKeyId = mData->mstrLogKeyId;
2583#else
2584 aKeyId = com::Utf8Str::Empty;
2585#endif
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2595 aKeyStore = mData->mstrLogKeyStore;
2596#else
2597 aKeyStore = com::Utf8Str::Empty;
2598#endif
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
2604{
2605 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
2606
2607 return S_OK;
2608}
2609
2610
2611/**
2612 * @note Locks objects!
2613 */
2614HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2615 LockType_T aLockType)
2616{
2617 /* check the session state */
2618 SessionState_T state;
2619 HRESULT hrc = aSession->COMGETTER(State)(&state);
2620 if (FAILED(hrc)) return hrc;
2621
2622 if (state != SessionState_Unlocked)
2623 return setError(VBOX_E_INVALID_OBJECT_STATE,
2624 tr("The given session is busy"));
2625
2626 // get the client's IInternalSessionControl interface
2627 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2628 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2629 E_INVALIDARG);
2630
2631 // session name (only used in some code paths)
2632 Utf8Str strSessionName;
2633
2634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 if (!mData->mRegistered)
2637 return setError(E_UNEXPECTED,
2638 tr("The machine '%s' is not registered"),
2639 mUserData->s.strName.c_str());
2640
2641 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2642
2643 SessionState_T oldState = mData->mSession.mState;
2644 /* Hack: in case the session is closing and there is a progress object
2645 * which allows waiting for the session to be closed, take the opportunity
2646 * and do a limited wait (max. 1 second). This helps a lot when the system
2647 * is busy and thus session closing can take a little while. */
2648 if ( mData->mSession.mState == SessionState_Unlocking
2649 && mData->mSession.mProgress)
2650 {
2651 alock.release();
2652 mData->mSession.mProgress->WaitForCompletion(1000);
2653 alock.acquire();
2654 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2655 }
2656
2657 // try again now
2658 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2659 // (i.e. session machine exists)
2660 && (aLockType == LockType_Shared) // caller wants a shared link to the
2661 // existing session that holds the write lock:
2662 )
2663 {
2664 // OK, share the session... we are now dealing with three processes:
2665 // 1) VBoxSVC (where this code runs);
2666 // 2) process C: the caller's client process (who wants a shared session);
2667 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2668
2669 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2670 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2671 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2672 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2673 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2674
2675 /*
2676 * Release the lock before calling the client process. It's safe here
2677 * since the only thing to do after we get the lock again is to add
2678 * the remote control to the list (which doesn't directly influence
2679 * anything).
2680 */
2681 alock.release();
2682
2683 // get the console of the session holding the write lock (this is a remote call)
2684 ComPtr<IConsole> pConsoleW;
2685 if (mData->mSession.mLockType == LockType_VM)
2686 {
2687 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2688 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2689 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
2690 if (FAILED(hrc))
2691 // the failure may occur w/o any error info (from RPC), so provide one
2692 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
2693 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2694 }
2695
2696 // share the session machine and W's console with the caller's session
2697 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2698 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2699 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2700
2701 if (FAILED(hrc))
2702 // the failure may occur w/o any error info (from RPC), so provide one
2703 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2704 alock.acquire();
2705
2706 // need to revalidate the state after acquiring the lock again
2707 if (mData->mSession.mState != SessionState_Locked)
2708 {
2709 pSessionControl->Uninitialize();
2710 return setError(VBOX_E_INVALID_SESSION_STATE,
2711 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2712 mUserData->s.strName.c_str());
2713 }
2714
2715 // add the caller's session to the list
2716 mData->mSession.mRemoteControls.push_back(pSessionControl);
2717 }
2718 else if ( mData->mSession.mState == SessionState_Locked
2719 || mData->mSession.mState == SessionState_Unlocking
2720 )
2721 {
2722 // sharing not permitted, or machine still unlocking:
2723 return setError(VBOX_E_INVALID_OBJECT_STATE,
2724 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2725 mUserData->s.strName.c_str());
2726 }
2727 else
2728 {
2729 // machine is not locked: then write-lock the machine (create the session machine)
2730
2731 // must not be busy
2732 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
2733
2734 // get the caller's session PID
2735 RTPROCESS pid = NIL_RTPROCESS;
2736 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
2737 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
2738 Assert(pid != NIL_RTPROCESS);
2739
2740 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
2741
2742 if (fLaunchingVMProcess)
2743 {
2744 if (mData->mSession.mPID == NIL_RTPROCESS)
2745 {
2746 // two or more clients racing for a lock, the one which set the
2747 // session state to Spawning will win, the others will get an
2748 // error as we can't decide here if waiting a little would help
2749 // (only for shared locks this would avoid an error)
2750 return setError(VBOX_E_INVALID_OBJECT_STATE,
2751 tr("The machine '%s' already has a lock request pending"),
2752 mUserData->s.strName.c_str());
2753 }
2754
2755 // this machine is awaiting for a spawning session to be opened:
2756 // then the calling process must be the one that got started by
2757 // LaunchVMProcess()
2758
2759 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
2760 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
2761
2762#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
2763 /* Hardened windows builds spawns three processes when a VM is
2764 launched, the 3rd one is the one that will end up here. */
2765 RTPROCESS pidParent;
2766 int vrc = RTProcQueryParent(pid, &pidParent);
2767 if (RT_SUCCESS(vrc))
2768 vrc = RTProcQueryParent(pidParent, &pidParent);
2769 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
2770 || vrc == VERR_ACCESS_DENIED)
2771 {
2772 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
2773 mData->mSession.mPID = pid;
2774 }
2775#endif
2776
2777 if (mData->mSession.mPID != pid)
2778 return setError(E_ACCESSDENIED,
2779 tr("An unexpected process (PID=0x%08X) has tried to lock the "
2780 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
2781 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
2782 }
2783
2784 // create the mutable SessionMachine from the current machine
2785 ComObjPtr<SessionMachine> sessionMachine;
2786 sessionMachine.createObject();
2787 hrc = sessionMachine->init(this);
2788 AssertComRC(hrc);
2789
2790 /* NOTE: doing return from this function after this point but
2791 * before the end is forbidden since it may call SessionMachine::uninit()
2792 * (through the ComObjPtr's destructor) which requests the VirtualBox write
2793 * lock while still holding the Machine lock in alock so that a deadlock
2794 * is possible due to the wrong lock order. */
2795
2796 if (SUCCEEDED(hrc))
2797 {
2798 /*
2799 * Set the session state to Spawning to protect against subsequent
2800 * attempts to open a session and to unregister the machine after
2801 * we release the lock.
2802 */
2803 SessionState_T origState = mData->mSession.mState;
2804 mData->mSession.mState = SessionState_Spawning;
2805
2806#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2807 /* Get the client token ID to be passed to the client process */
2808 Utf8Str strTokenId;
2809 sessionMachine->i_getTokenId(strTokenId);
2810 Assert(!strTokenId.isEmpty());
2811#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2812 /* Get the client token to be passed to the client process */
2813 ComPtr<IToken> pToken(sessionMachine->i_getToken());
2814 /* The token is now "owned" by pToken, fix refcount */
2815 if (!pToken.isNull())
2816 pToken->Release();
2817#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2818
2819 /*
2820 * Release the lock before calling the client process -- it will call
2821 * Machine/SessionMachine methods. Releasing the lock here is quite safe
2822 * because the state is Spawning, so that LaunchVMProcess() and
2823 * LockMachine() calls will fail. This method, called before we
2824 * acquire the lock again, will fail because of the wrong PID.
2825 *
2826 * Note that mData->mSession.mRemoteControls accessed outside
2827 * the lock may not be modified when state is Spawning, so it's safe.
2828 */
2829 alock.release();
2830
2831 LogFlowThisFunc(("Calling AssignMachine()...\n"));
2832#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
2833 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
2834#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2835 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
2836 /* Now the token is owned by the client process. */
2837 pToken.setNull();
2838#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
2839 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
2840
2841 /* The failure may occur w/o any error info (from RPC), so provide one */
2842 if (FAILED(hrc))
2843 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
2844
2845 // get session name, either to remember or to compare against
2846 // the already known session name.
2847 {
2848 Bstr bstrSessionName;
2849 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
2850 if (SUCCEEDED(hrc2))
2851 strSessionName = bstrSessionName;
2852 }
2853
2854 if ( SUCCEEDED(hrc)
2855 && fLaunchingVMProcess
2856 )
2857 {
2858 /* complete the remote session initialization */
2859
2860 /* get the console from the direct session */
2861 ComPtr<IConsole> console;
2862 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2863 ComAssertComRC(hrc);
2864
2865 if (SUCCEEDED(hrc) && !console)
2866 {
2867 ComAssert(!!console);
2868 hrc = E_FAIL;
2869 }
2870
2871 /* assign machine & console to the remote session */
2872 if (SUCCEEDED(hrc))
2873 {
2874 /*
2875 * after LaunchVMProcess(), the first and the only
2876 * entry in remoteControls is that remote session
2877 */
2878 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2879 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
2880 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
2881
2882 /* The failure may occur w/o any error info (from RPC), so provide one */
2883 if (FAILED(hrc))
2884 setError(VBOX_E_VM_ERROR,
2885 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
2886 }
2887
2888 if (FAILED(hrc))
2889 pSessionControl->Uninitialize();
2890 }
2891
2892 /* acquire the lock again */
2893 alock.acquire();
2894
2895 /* Restore the session state */
2896 mData->mSession.mState = origState;
2897 }
2898
2899 // finalize spawning anyway (this is why we don't return on errors above)
2900 if (fLaunchingVMProcess)
2901 {
2902 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
2903 /* Note that the progress object is finalized later */
2904 /** @todo Consider checking mData->mSession.mProgress for cancellation
2905 * around here. */
2906
2907 /* We don't reset mSession.mPID here because it is necessary for
2908 * SessionMachine::uninit() to reap the child process later. */
2909
2910 if (FAILED(hrc))
2911 {
2912 /* Close the remote session, remove the remote control from the list
2913 * and reset session state to Closed (@note keep the code in sync
2914 * with the relevant part in checkForSpawnFailure()). */
2915
2916 Assert(mData->mSession.mRemoteControls.size() == 1);
2917 if (mData->mSession.mRemoteControls.size() == 1)
2918 {
2919 ErrorInfoKeeper eik;
2920 mData->mSession.mRemoteControls.front()->Uninitialize();
2921 }
2922
2923 mData->mSession.mRemoteControls.clear();
2924 mData->mSession.mState = SessionState_Unlocked;
2925 }
2926 }
2927 else
2928 {
2929 /* memorize PID of the directly opened session */
2930 if (SUCCEEDED(hrc))
2931 mData->mSession.mPID = pid;
2932 }
2933
2934 if (SUCCEEDED(hrc))
2935 {
2936 mData->mSession.mLockType = aLockType;
2937 /* memorize the direct session control and cache IUnknown for it */
2938 mData->mSession.mDirectControl = pSessionControl;
2939 mData->mSession.mState = SessionState_Locked;
2940 if (!fLaunchingVMProcess)
2941 mData->mSession.mName = strSessionName;
2942 /* associate the SessionMachine with this Machine */
2943 mData->mSession.mMachine = sessionMachine;
2944
2945 /* request an IUnknown pointer early from the remote party for later
2946 * identity checks (it will be internally cached within mDirectControl
2947 * at least on XPCOM) */
2948 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
2949 NOREF(unk);
2950
2951#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2952 if (aLockType == LockType_VM)
2953 {
2954 /* get the console from the direct session */
2955 ComPtr<IConsole> console;
2956 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
2957 ComAssertComRC(hrc);
2958 /* send passswords to console */
2959 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
2960 it != mData->mpKeyStore->end();
2961 ++it)
2962 {
2963 SecretKey *pKey = it->second;
2964 pKey->retain();
2965 console->AddEncryptionPassword(Bstr(it->first).raw(),
2966 Bstr((const char*)pKey->getKeyBuffer()).raw(),
2967 TRUE);
2968 pKey->release();
2969 }
2970
2971 }
2972#endif
2973 }
2974
2975 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
2976 * would break the lock order */
2977 alock.release();
2978
2979 /* uninitialize the created session machine on failure */
2980 if (FAILED(hrc))
2981 sessionMachine->uninit();
2982 }
2983
2984 if (SUCCEEDED(hrc))
2985 {
2986 /*
2987 * tell the client watcher thread to update the set of
2988 * machines that have open sessions
2989 */
2990 mParent->i_updateClientWatcher();
2991
2992 if (oldState != SessionState_Locked)
2993 /* fire an event */
2994 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
2995 }
2996
2997 return hrc;
2998}
2999
3000/**
3001 * @note Locks objects!
3002 */
3003HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3004 const com::Utf8Str &aName,
3005 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3006 ComPtr<IProgress> &aProgress)
3007{
3008 Utf8Str strFrontend(aName);
3009 /* "emergencystop" doesn't need the session, so skip the checks/interface
3010 * retrieval. This code doesn't quite fit in here, but introducing a
3011 * special API method would be even more effort, and would require explicit
3012 * support by every API client. It's better to hide the feature a bit. */
3013 if (strFrontend != "emergencystop")
3014 CheckComArgNotNull(aSession);
3015
3016 HRESULT hrc = S_OK;
3017 if (strFrontend.isEmpty())
3018 {
3019 Bstr bstrFrontend;
3020 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3021 if (FAILED(hrc))
3022 return hrc;
3023 strFrontend = bstrFrontend;
3024 if (strFrontend.isEmpty())
3025 {
3026 ComPtr<ISystemProperties> systemProperties;
3027 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3028 if (FAILED(hrc))
3029 return hrc;
3030 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3031 if (FAILED(hrc))
3032 return hrc;
3033 strFrontend = bstrFrontend;
3034 }
3035 /* paranoia - emergencystop is not a valid default */
3036 if (strFrontend == "emergencystop")
3037 strFrontend = Utf8Str::Empty;
3038 }
3039 /* default frontend: Qt GUI */
3040 if (strFrontend.isEmpty())
3041 strFrontend = "GUI/Qt";
3042
3043 if (strFrontend != "emergencystop")
3044 {
3045 /* check the session state */
3046 SessionState_T state;
3047 hrc = aSession->COMGETTER(State)(&state);
3048 if (FAILED(hrc))
3049 return hrc;
3050
3051 if (state != SessionState_Unlocked)
3052 return setError(VBOX_E_INVALID_OBJECT_STATE,
3053 tr("The given session is busy"));
3054
3055 /* get the IInternalSessionControl interface */
3056 ComPtr<IInternalSessionControl> control(aSession);
3057 ComAssertMsgRet(!control.isNull(),
3058 ("No IInternalSessionControl interface"),
3059 E_INVALIDARG);
3060
3061 /* get the teleporter enable state for the progress object init. */
3062 BOOL fTeleporterEnabled;
3063 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3064 if (FAILED(hrc))
3065 return hrc;
3066
3067 /* create a progress object */
3068 ComObjPtr<ProgressProxy> progress;
3069 progress.createObject();
3070 hrc = progress->init(mParent,
3071 static_cast<IMachine*>(this),
3072 Bstr(tr("Starting VM")).raw(),
3073 TRUE /* aCancelable */,
3074 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3075 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3076 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3077 2 /* uFirstOperationWeight */,
3078 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3079 if (SUCCEEDED(hrc))
3080 {
3081 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3082 if (SUCCEEDED(hrc))
3083 {
3084 aProgress = progress;
3085
3086 /* signal the client watcher thread */
3087 mParent->i_updateClientWatcher();
3088
3089 /* fire an event */
3090 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3091 }
3092 }
3093 }
3094 else
3095 {
3096 /* no progress object - either instant success or failure */
3097 aProgress = NULL;
3098
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 if (mData->mSession.mState != SessionState_Locked)
3102 return setError(VBOX_E_INVALID_OBJECT_STATE,
3103 tr("The machine '%s' is not locked by a session"),
3104 mUserData->s.strName.c_str());
3105
3106 /* must have a VM process associated - do not kill normal API clients
3107 * with an open session */
3108 if (!Global::IsOnline(mData->mMachineState))
3109 return setError(VBOX_E_INVALID_OBJECT_STATE,
3110 tr("The machine '%s' does not have a VM process"),
3111 mUserData->s.strName.c_str());
3112
3113 /* forcibly terminate the VM process */
3114 if (mData->mSession.mPID != NIL_RTPROCESS)
3115 RTProcTerminate(mData->mSession.mPID);
3116
3117 /* signal the client watcher thread, as most likely the client has
3118 * been terminated */
3119 mParent->i_updateClientWatcher();
3120 }
3121
3122 return hrc;
3123}
3124
3125HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3126{
3127 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3128 return setError(E_INVALIDARG,
3129 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3130 aPosition, SchemaDefs::MaxBootPosition);
3131
3132 if (aDevice == DeviceType_USB)
3133 return setError(E_NOTIMPL,
3134 tr("Booting from USB device is currently not supported"));
3135
3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3139 if (FAILED(hrc)) return hrc;
3140
3141 i_setModified(IsModified_MachineData);
3142 mHWData.backup();
3143 mHWData->mBootOrder[aPosition - 1] = aDevice;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3149{
3150 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3151 return setError(E_INVALIDARG,
3152 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3153 aPosition, SchemaDefs::MaxBootPosition);
3154
3155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3156
3157 *aDevice = mHWData->mBootOrder[aPosition - 1];
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3163 LONG aControllerPort,
3164 LONG aDevice,
3165 DeviceType_T aType,
3166 const ComPtr<IMedium> &aMedium)
3167{
3168 IMedium *aM = aMedium;
3169 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3170 aName.c_str(), aControllerPort, aDevice, aType, aM));
3171
3172 // request the host lock first, since might be calling Host methods for getting host drives;
3173 // next, protect the media tree all the while we're in here, as well as our member variables
3174 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3175 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3176
3177 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3178 if (FAILED(hrc)) return hrc;
3179
3180 /// @todo NEWMEDIA implicit machine registration
3181 if (!mData->mRegistered)
3182 return setError(VBOX_E_INVALID_OBJECT_STATE,
3183 tr("Cannot attach storage devices to an unregistered machine"));
3184
3185 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3186
3187 /* Check for an existing controller. */
3188 ComObjPtr<StorageController> ctl;
3189 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3190 if (FAILED(hrc)) return hrc;
3191
3192 StorageControllerType_T ctrlType;
3193 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3194 if (FAILED(hrc))
3195 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3196
3197 bool fSilent = false;
3198
3199 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3200 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3201 if ( mData->mMachineState == MachineState_Paused
3202 && strReconfig == "1")
3203 fSilent = true;
3204
3205 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3206 bool fHotplug = false;
3207 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3208 fHotplug = true;
3209
3210 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3211 return setError(VBOX_E_INVALID_VM_STATE,
3212 tr("Controller '%s' does not support hot-plugging"),
3213 aName.c_str());
3214
3215 // check that the port and device are not out of range
3216 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3217 if (FAILED(hrc)) return hrc;
3218
3219 /* check if the device slot is already busy */
3220 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3221 aName,
3222 aControllerPort,
3223 aDevice);
3224 if (pAttachTemp)
3225 {
3226 Medium *pMedium = pAttachTemp->i_getMedium();
3227 if (pMedium)
3228 {
3229 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3230 return setError(VBOX_E_OBJECT_IN_USE,
3231 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3232 pMedium->i_getLocationFull().c_str(),
3233 aControllerPort,
3234 aDevice,
3235 aName.c_str());
3236 }
3237 else
3238 return setError(VBOX_E_OBJECT_IN_USE,
3239 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3240 aControllerPort, aDevice, aName.c_str());
3241 }
3242
3243 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3244 if (aMedium && medium.isNull())
3245 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3246
3247 AutoCaller mediumCaller(medium);
3248 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3249
3250 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3251
3252 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3253 if ( pAttachTemp
3254 && !medium.isNull()
3255 && ( medium->i_getType() != MediumType_Readonly
3256 || medium->i_getDeviceType() != DeviceType_DVD)
3257 )
3258 return setError(VBOX_E_OBJECT_IN_USE,
3259 tr("Medium '%s' is already attached to this virtual machine"),
3260 medium->i_getLocationFull().c_str());
3261
3262 if (!medium.isNull())
3263 {
3264 MediumType_T mtype = medium->i_getType();
3265 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3266 // For DVDs it's not written to the config file, so needs no global config
3267 // version bump. For floppies it's a new attribute "type", which is ignored
3268 // by older VirtualBox version, so needs no global config version bump either.
3269 // For hard disks this type is not accepted.
3270 if (mtype == MediumType_MultiAttach)
3271 {
3272 // This type is new with VirtualBox 4.0 and therefore requires settings
3273 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3274 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3275 // two reasons: The medium type is a property of the media registry tree, which
3276 // can reside in the global config file (for pre-4.0 media); we would therefore
3277 // possibly need to bump the global config version. We don't want to do that though
3278 // because that might make downgrading to pre-4.0 impossible.
3279 // As a result, we can only use these two new types if the medium is NOT in the
3280 // global registry:
3281 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3282 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3283 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3284 )
3285 return setError(VBOX_E_INVALID_OBJECT_STATE,
3286 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3287 "to machines that were created with VirtualBox 4.0 or later"),
3288 medium->i_getLocationFull().c_str());
3289 }
3290 }
3291
3292 bool fIndirect = false;
3293 if (!medium.isNull())
3294 fIndirect = medium->i_isReadOnly();
3295 bool associate = true;
3296
3297 do
3298 {
3299 if ( aType == DeviceType_HardDisk
3300 && mMediumAttachments.isBackedUp())
3301 {
3302 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3303
3304 /* check if the medium was attached to the VM before we started
3305 * changing attachments in which case the attachment just needs to
3306 * be restored */
3307 pAttachTemp = i_findAttachment(oldAtts, medium);
3308 if (pAttachTemp)
3309 {
3310 AssertReturn(!fIndirect, E_FAIL);
3311
3312 /* see if it's the same bus/channel/device */
3313 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3314 {
3315 /* the simplest case: restore the whole attachment
3316 * and return, nothing else to do */
3317 mMediumAttachments->push_back(pAttachTemp);
3318
3319 /* Reattach the medium to the VM. */
3320 if (fHotplug || fSilent)
3321 {
3322 mediumLock.release();
3323 treeLock.release();
3324 alock.release();
3325
3326 MediumLockList *pMediumLockList(new MediumLockList());
3327
3328 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3329 medium /* pToLockWrite */,
3330 false /* fMediumLockWriteAll */,
3331 NULL,
3332 *pMediumLockList);
3333 alock.acquire();
3334 if (FAILED(hrc))
3335 delete pMediumLockList;
3336 else
3337 {
3338 Assert(mData->mSession.mLockedMedia.IsLocked());
3339 mData->mSession.mLockedMedia.Unlock();
3340 alock.release();
3341 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3342 mData->mSession.mLockedMedia.Lock();
3343 alock.acquire();
3344 }
3345 alock.release();
3346
3347 if (SUCCEEDED(hrc))
3348 {
3349 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3350 /* Remove lock list in case of error. */
3351 if (FAILED(hrc))
3352 {
3353 mData->mSession.mLockedMedia.Unlock();
3354 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3355 mData->mSession.mLockedMedia.Lock();
3356 }
3357 }
3358 }
3359
3360 return S_OK;
3361 }
3362
3363 /* bus/channel/device differ; we need a new attachment object,
3364 * but don't try to associate it again */
3365 associate = false;
3366 break;
3367 }
3368 }
3369
3370 /* go further only if the attachment is to be indirect */
3371 if (!fIndirect)
3372 break;
3373
3374 /* perform the so called smart attachment logic for indirect
3375 * attachments. Note that smart attachment is only applicable to base
3376 * hard disks. */
3377
3378 if (medium->i_getParent().isNull())
3379 {
3380 /* first, investigate the backup copy of the current hard disk
3381 * attachments to make it possible to re-attach existing diffs to
3382 * another device slot w/o losing their contents */
3383 if (mMediumAttachments.isBackedUp())
3384 {
3385 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3386
3387 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3388 uint32_t foundLevel = 0;
3389
3390 for (MediumAttachmentList::const_iterator
3391 it = oldAtts.begin();
3392 it != oldAtts.end();
3393 ++it)
3394 {
3395 uint32_t level = 0;
3396 MediumAttachment *pAttach = *it;
3397 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3398 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3399 if (pMedium.isNull())
3400 continue;
3401
3402 if (pMedium->i_getBase(&level) == medium)
3403 {
3404 /* skip the hard disk if its currently attached (we
3405 * cannot attach the same hard disk twice) */
3406 if (i_findAttachment(*mMediumAttachments.data(),
3407 pMedium))
3408 continue;
3409
3410 /* matched device, channel and bus (i.e. attached to the
3411 * same place) will win and immediately stop the search;
3412 * otherwise the attachment that has the youngest
3413 * descendant of medium will be used
3414 */
3415 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3416 {
3417 /* the simplest case: restore the whole attachment
3418 * and return, nothing else to do */
3419 mMediumAttachments->push_back(*it);
3420
3421 /* Reattach the medium to the VM. */
3422 if (fHotplug || fSilent)
3423 {
3424 mediumLock.release();
3425 treeLock.release();
3426 alock.release();
3427
3428 MediumLockList *pMediumLockList(new MediumLockList());
3429
3430 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3431 medium /* pToLockWrite */,
3432 false /* fMediumLockWriteAll */,
3433 NULL,
3434 *pMediumLockList);
3435 alock.acquire();
3436 if (FAILED(hrc))
3437 delete pMediumLockList;
3438 else
3439 {
3440 Assert(mData->mSession.mLockedMedia.IsLocked());
3441 mData->mSession.mLockedMedia.Unlock();
3442 alock.release();
3443 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3444 mData->mSession.mLockedMedia.Lock();
3445 alock.acquire();
3446 }
3447 alock.release();
3448
3449 if (SUCCEEDED(hrc))
3450 {
3451 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3452 /* Remove lock list in case of error. */
3453 if (FAILED(hrc))
3454 {
3455 mData->mSession.mLockedMedia.Unlock();
3456 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3457 mData->mSession.mLockedMedia.Lock();
3458 }
3459 }
3460 }
3461
3462 return S_OK;
3463 }
3464 else if ( foundIt == oldAtts.end()
3465 || level > foundLevel /* prefer younger */
3466 )
3467 {
3468 foundIt = it;
3469 foundLevel = level;
3470 }
3471 }
3472 }
3473
3474 if (foundIt != oldAtts.end())
3475 {
3476 /* use the previously attached hard disk */
3477 medium = (*foundIt)->i_getMedium();
3478 mediumCaller.attach(medium);
3479 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3480 mediumLock.attach(medium);
3481 /* not implicit, doesn't require association with this VM */
3482 fIndirect = false;
3483 associate = false;
3484 /* go right to the MediumAttachment creation */
3485 break;
3486 }
3487 }
3488
3489 /* must give up the medium lock and medium tree lock as below we
3490 * go over snapshots, which needs a lock with higher lock order. */
3491 mediumLock.release();
3492 treeLock.release();
3493
3494 /* then, search through snapshots for the best diff in the given
3495 * hard disk's chain to base the new diff on */
3496
3497 ComObjPtr<Medium> base;
3498 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3499 while (snap)
3500 {
3501 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3502
3503 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3504
3505 MediumAttachment *pAttachFound = NULL;
3506 uint32_t foundLevel = 0;
3507
3508 for (MediumAttachmentList::const_iterator
3509 it = snapAtts.begin();
3510 it != snapAtts.end();
3511 ++it)
3512 {
3513 MediumAttachment *pAttach = *it;
3514 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3515 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3516 if (pMedium.isNull())
3517 continue;
3518
3519 uint32_t level = 0;
3520 if (pMedium->i_getBase(&level) == medium)
3521 {
3522 /* matched device, channel and bus (i.e. attached to the
3523 * same place) will win and immediately stop the search;
3524 * otherwise the attachment that has the youngest
3525 * descendant of medium will be used
3526 */
3527 if ( pAttach->i_getDevice() == aDevice
3528 && pAttach->i_getPort() == aControllerPort
3529 && pAttach->i_getControllerName() == aName
3530 )
3531 {
3532 pAttachFound = pAttach;
3533 break;
3534 }
3535 else if ( !pAttachFound
3536 || level > foundLevel /* prefer younger */
3537 )
3538 {
3539 pAttachFound = pAttach;
3540 foundLevel = level;
3541 }
3542 }
3543 }
3544
3545 if (pAttachFound)
3546 {
3547 base = pAttachFound->i_getMedium();
3548 break;
3549 }
3550
3551 snap = snap->i_getParent();
3552 }
3553
3554 /* re-lock medium tree and the medium, as we need it below */
3555 treeLock.acquire();
3556 mediumLock.acquire();
3557
3558 /* found a suitable diff, use it as a base */
3559 if (!base.isNull())
3560 {
3561 medium = base;
3562 mediumCaller.attach(medium);
3563 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3564 mediumLock.attach(medium);
3565 }
3566 }
3567
3568 Utf8Str strFullSnapshotFolder;
3569 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3570
3571 ComObjPtr<Medium> diff;
3572 diff.createObject();
3573 // store this diff in the same registry as the parent
3574 Guid uuidRegistryParent;
3575 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3576 {
3577 // parent image has no registry: this can happen if we're attaching a new immutable
3578 // image that has not yet been attached (medium then points to the base and we're
3579 // creating the diff image for the immutable, and the parent is not yet registered);
3580 // put the parent in the machine registry then
3581 mediumLock.release();
3582 treeLock.release();
3583 alock.release();
3584 i_addMediumToRegistry(medium);
3585 alock.acquire();
3586 treeLock.acquire();
3587 mediumLock.acquire();
3588 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3589 }
3590 hrc = diff->init(mParent,
3591 medium->i_getPreferredDiffFormat(),
3592 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3593 uuidRegistryParent,
3594 DeviceType_HardDisk);
3595 if (FAILED(hrc)) return hrc;
3596
3597 /* Apply the normal locking logic to the entire chain. */
3598 MediumLockList *pMediumLockList(new MediumLockList());
3599 mediumLock.release();
3600 treeLock.release();
3601 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3602 diff /* pToLockWrite */,
3603 false /* fMediumLockWriteAll */,
3604 medium,
3605 *pMediumLockList);
3606 treeLock.acquire();
3607 mediumLock.acquire();
3608 if (SUCCEEDED(hrc))
3609 {
3610 mediumLock.release();
3611 treeLock.release();
3612 hrc = pMediumLockList->Lock();
3613 treeLock.acquire();
3614 mediumLock.acquire();
3615 if (FAILED(hrc))
3616 setError(hrc,
3617 tr("Could not lock medium when creating diff '%s'"),
3618 diff->i_getLocationFull().c_str());
3619 else
3620 {
3621 /* will release the lock before the potentially lengthy
3622 * operation, so protect with the special state */
3623 MachineState_T oldState = mData->mMachineState;
3624 i_setMachineState(MachineState_SettingUp);
3625
3626 mediumLock.release();
3627 treeLock.release();
3628 alock.release();
3629
3630 hrc = medium->i_createDiffStorage(diff,
3631 medium->i_getPreferredDiffVariant(),
3632 pMediumLockList,
3633 NULL /* aProgress */,
3634 true /* aWait */,
3635 false /* aNotify */);
3636
3637 alock.acquire();
3638 treeLock.acquire();
3639 mediumLock.acquire();
3640
3641 i_setMachineState(oldState);
3642 }
3643 }
3644
3645 /* Unlock the media and free the associated memory. */
3646 delete pMediumLockList;
3647
3648 if (FAILED(hrc)) return hrc;
3649
3650 /* use the created diff for the actual attachment */
3651 medium = diff;
3652 mediumCaller.attach(medium);
3653 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3654 mediumLock.attach(medium);
3655 }
3656 while (0);
3657
3658 ComObjPtr<MediumAttachment> attachment;
3659 attachment.createObject();
3660 hrc = attachment->init(this,
3661 medium,
3662 aName,
3663 aControllerPort,
3664 aDevice,
3665 aType,
3666 fIndirect,
3667 false /* fPassthrough */,
3668 false /* fTempEject */,
3669 false /* fNonRotational */,
3670 false /* fDiscard */,
3671 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
3672 Utf8Str::Empty);
3673 if (FAILED(hrc)) return hrc;
3674
3675 if (associate && !medium.isNull())
3676 {
3677 // as the last step, associate the medium to the VM
3678 hrc = medium->i_addBackReference(mData->mUuid);
3679 // here we can fail because of Deleting, or being in process of creating a Diff
3680 if (FAILED(hrc)) return hrc;
3681
3682 mediumLock.release();
3683 treeLock.release();
3684 alock.release();
3685 i_addMediumToRegistry(medium);
3686 alock.acquire();
3687 treeLock.acquire();
3688 mediumLock.acquire();
3689 }
3690
3691 /* success: finally remember the attachment */
3692 i_setModified(IsModified_Storage);
3693 mMediumAttachments.backup();
3694 mMediumAttachments->push_back(attachment);
3695
3696 mediumLock.release();
3697 treeLock.release();
3698 alock.release();
3699
3700 if (fHotplug || fSilent)
3701 {
3702 if (!medium.isNull())
3703 {
3704 MediumLockList *pMediumLockList(new MediumLockList());
3705
3706 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3707 medium /* pToLockWrite */,
3708 false /* fMediumLockWriteAll */,
3709 NULL,
3710 *pMediumLockList);
3711 alock.acquire();
3712 if (FAILED(hrc))
3713 delete pMediumLockList;
3714 else
3715 {
3716 Assert(mData->mSession.mLockedMedia.IsLocked());
3717 mData->mSession.mLockedMedia.Unlock();
3718 alock.release();
3719 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3720 mData->mSession.mLockedMedia.Lock();
3721 alock.acquire();
3722 }
3723 alock.release();
3724 }
3725
3726 if (SUCCEEDED(hrc))
3727 {
3728 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3729 /* Remove lock list in case of error. */
3730 if (FAILED(hrc))
3731 {
3732 mData->mSession.mLockedMedia.Unlock();
3733 mData->mSession.mLockedMedia.Remove(attachment);
3734 mData->mSession.mLockedMedia.Lock();
3735 }
3736 }
3737 }
3738
3739 /* Save modified registries, but skip this machine as it's the caller's
3740 * job to save its settings like all other settings changes. */
3741 mParent->i_unmarkRegistryModified(i_getId());
3742 mParent->i_saveModifiedRegistries();
3743
3744 if (SUCCEEDED(hrc))
3745 {
3746 if (fIndirect && medium != aM)
3747 mParent->i_onMediumConfigChanged(medium);
3748 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3749 }
3750
3751 return hrc;
3752}
3753
3754HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
3755 LONG aDevice)
3756{
3757 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
3758
3759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3760
3761 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3762 if (FAILED(hrc)) return hrc;
3763
3764 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3765
3766 /* Check for an existing controller. */
3767 ComObjPtr<StorageController> ctl;
3768 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3769 if (FAILED(hrc)) return hrc;
3770
3771 StorageControllerType_T ctrlType;
3772 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3773 if (FAILED(hrc))
3774 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3775
3776 bool fSilent = false;
3777 Utf8Str strReconfig;
3778
3779 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3780 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3781 if ( mData->mMachineState == MachineState_Paused
3782 && strReconfig == "1")
3783 fSilent = true;
3784
3785 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3786 bool fHotplug = false;
3787 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3788 fHotplug = true;
3789
3790 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3791 return setError(VBOX_E_INVALID_VM_STATE,
3792 tr("Controller '%s' does not support hot-plugging"),
3793 aName.c_str());
3794
3795 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3796 aName,
3797 aControllerPort,
3798 aDevice);
3799 if (!pAttach)
3800 return setError(VBOX_E_OBJECT_NOT_FOUND,
3801 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3802 aDevice, aControllerPort, aName.c_str());
3803
3804 if (fHotplug && !pAttach->i_getHotPluggable())
3805 return setError(VBOX_E_NOT_SUPPORTED,
3806 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
3807 aDevice, aControllerPort, aName.c_str());
3808
3809 /*
3810 * The VM has to detach the device before we delete any implicit diffs.
3811 * If this fails we can roll back without loosing data.
3812 */
3813 if (fHotplug || fSilent)
3814 {
3815 alock.release();
3816 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
3817 alock.acquire();
3818 }
3819 if (FAILED(hrc)) return hrc;
3820
3821 /* If we are here everything went well and we can delete the implicit now. */
3822 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
3823
3824 alock.release();
3825
3826 /* Save modified registries, but skip this machine as it's the caller's
3827 * job to save its settings like all other settings changes. */
3828 mParent->i_unmarkRegistryModified(i_getId());
3829 mParent->i_saveModifiedRegistries();
3830
3831 if (SUCCEEDED(hrc))
3832 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
3833
3834 return hrc;
3835}
3836
3837HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
3838 LONG aDevice, BOOL aPassthrough)
3839{
3840 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
3841 aName.c_str(), aControllerPort, aDevice, aPassthrough));
3842
3843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3844
3845 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3846 if (FAILED(hrc)) return hrc;
3847
3848 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3849
3850 /* Check for an existing controller. */
3851 ComObjPtr<StorageController> ctl;
3852 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3853 if (FAILED(hrc)) return hrc;
3854
3855 StorageControllerType_T ctrlType;
3856 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3857 if (FAILED(hrc))
3858 return setError(E_FAIL,
3859 tr("Could not get type of controller '%s'"),
3860 aName.c_str());
3861
3862 bool fSilent = false;
3863 Utf8Str strReconfig;
3864
3865 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3866 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3867 if ( mData->mMachineState == MachineState_Paused
3868 && strReconfig == "1")
3869 fSilent = true;
3870
3871 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
3872 bool fHotplug = false;
3873 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3874 fHotplug = true;
3875
3876 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3877 return setError(VBOX_E_INVALID_VM_STATE,
3878 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
3879 aName.c_str());
3880
3881 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3882 aName,
3883 aControllerPort,
3884 aDevice);
3885 if (!pAttach)
3886 return setError(VBOX_E_OBJECT_NOT_FOUND,
3887 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3888 aDevice, aControllerPort, aName.c_str());
3889
3890
3891 i_setModified(IsModified_Storage);
3892 mMediumAttachments.backup();
3893
3894 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3895
3896 if (pAttach->i_getType() != DeviceType_DVD)
3897 return setError(E_INVALIDARG,
3898 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3899 aDevice, aControllerPort, aName.c_str());
3900
3901 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
3902
3903 pAttach->i_updatePassthrough(!!aPassthrough);
3904
3905 attLock.release();
3906 alock.release();
3907 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
3908 if (SUCCEEDED(hrc) && fValueChanged)
3909 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
3910
3911 return hrc;
3912}
3913
3914HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
3915 LONG aDevice, BOOL aTemporaryEject)
3916{
3917
3918 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
3919 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
3920
3921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3922
3923 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3924 if (FAILED(hrc)) return hrc;
3925
3926 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3927 aName,
3928 aControllerPort,
3929 aDevice);
3930 if (!pAttach)
3931 return setError(VBOX_E_OBJECT_NOT_FOUND,
3932 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3933 aDevice, aControllerPort, aName.c_str());
3934
3935
3936 i_setModified(IsModified_Storage);
3937 mMediumAttachments.backup();
3938
3939 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3940
3941 if (pAttach->i_getType() != DeviceType_DVD)
3942 return setError(E_INVALIDARG,
3943 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
3944 aDevice, aControllerPort, aName.c_str());
3945 pAttach->i_updateTempEject(!!aTemporaryEject);
3946
3947 return S_OK;
3948}
3949
3950HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
3951 LONG aDevice, BOOL aNonRotational)
3952{
3953
3954 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
3955 aName.c_str(), aControllerPort, aDevice, aNonRotational));
3956
3957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3958
3959 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3960 if (FAILED(hrc)) return hrc;
3961
3962 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3963
3964 if (Global::IsOnlineOrTransient(mData->mMachineState))
3965 return setError(VBOX_E_INVALID_VM_STATE,
3966 tr("Invalid machine state: %s"),
3967 Global::stringifyMachineState(mData->mMachineState));
3968
3969 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
3970 aName,
3971 aControllerPort,
3972 aDevice);
3973 if (!pAttach)
3974 return setError(VBOX_E_OBJECT_NOT_FOUND,
3975 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
3976 aDevice, aControllerPort, aName.c_str());
3977
3978
3979 i_setModified(IsModified_Storage);
3980 mMediumAttachments.backup();
3981
3982 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3983
3984 if (pAttach->i_getType() != DeviceType_HardDisk)
3985 return setError(E_INVALIDARG,
3986 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
3987 aDevice, aControllerPort, aName.c_str());
3988 pAttach->i_updateNonRotational(!!aNonRotational);
3989
3990 return S_OK;
3991}
3992
3993HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
3994 LONG aDevice, BOOL aDiscard)
3995{
3996
3997 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
3998 aName.c_str(), aControllerPort, aDevice, aDiscard));
3999
4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4001
4002 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4003 if (FAILED(hrc)) return hrc;
4004
4005 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4006
4007 if (Global::IsOnlineOrTransient(mData->mMachineState))
4008 return setError(VBOX_E_INVALID_VM_STATE,
4009 tr("Invalid machine state: %s"),
4010 Global::stringifyMachineState(mData->mMachineState));
4011
4012 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4013 aName,
4014 aControllerPort,
4015 aDevice);
4016 if (!pAttach)
4017 return setError(VBOX_E_OBJECT_NOT_FOUND,
4018 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4019 aDevice, aControllerPort, aName.c_str());
4020
4021
4022 i_setModified(IsModified_Storage);
4023 mMediumAttachments.backup();
4024
4025 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4026
4027 if (pAttach->i_getType() != DeviceType_HardDisk)
4028 return setError(E_INVALIDARG,
4029 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4030 aDevice, aControllerPort, aName.c_str());
4031 pAttach->i_updateDiscard(!!aDiscard);
4032
4033 return S_OK;
4034}
4035
4036HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4037 LONG aDevice, BOOL aHotPluggable)
4038{
4039 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4040 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4041
4042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4043
4044 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4045 if (FAILED(hrc)) return hrc;
4046
4047 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4048
4049 if (Global::IsOnlineOrTransient(mData->mMachineState))
4050 return setError(VBOX_E_INVALID_VM_STATE,
4051 tr("Invalid machine state: %s"),
4052 Global::stringifyMachineState(mData->mMachineState));
4053
4054 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4055 aName,
4056 aControllerPort,
4057 aDevice);
4058 if (!pAttach)
4059 return setError(VBOX_E_OBJECT_NOT_FOUND,
4060 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4061 aDevice, aControllerPort, aName.c_str());
4062
4063 /* Check for an existing controller. */
4064 ComObjPtr<StorageController> ctl;
4065 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4066 if (FAILED(hrc)) return hrc;
4067
4068 StorageControllerType_T ctrlType;
4069 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4070 if (FAILED(hrc))
4071 return setError(E_FAIL,
4072 tr("Could not get type of controller '%s'"),
4073 aName.c_str());
4074
4075 if (!i_isControllerHotplugCapable(ctrlType))
4076 return setError(VBOX_E_NOT_SUPPORTED,
4077 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4078 aName.c_str());
4079
4080 /* silently ignore attempts to modify the hot-plug status of USB devices */
4081 if (ctrlType == StorageControllerType_USB)
4082 return S_OK;
4083
4084 i_setModified(IsModified_Storage);
4085 mMediumAttachments.backup();
4086
4087 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4088
4089 if (pAttach->i_getType() == DeviceType_Floppy)
4090 return setError(E_INVALIDARG,
4091 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4092 aDevice, aControllerPort, aName.c_str());
4093 pAttach->i_updateHotPluggable(!!aHotPluggable);
4094
4095 return S_OK;
4096}
4097
4098HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4099 LONG aDevice)
4100{
4101 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4102 aName.c_str(), aControllerPort, aDevice));
4103
4104 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4105}
4106
4107HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4108 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4109{
4110 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4111 aName.c_str(), aControllerPort, aDevice));
4112
4113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4114
4115 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4116 if (FAILED(hrc)) return hrc;
4117
4118 if (Global::IsOnlineOrTransient(mData->mMachineState))
4119 return setError(VBOX_E_INVALID_VM_STATE,
4120 tr("Invalid machine state: %s"),
4121 Global::stringifyMachineState(mData->mMachineState));
4122
4123 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4124 aName,
4125 aControllerPort,
4126 aDevice);
4127 if (!pAttach)
4128 return setError(VBOX_E_OBJECT_NOT_FOUND,
4129 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4130 aDevice, aControllerPort, aName.c_str());
4131
4132
4133 i_setModified(IsModified_Storage);
4134 mMediumAttachments.backup();
4135
4136 IBandwidthGroup *iB = aBandwidthGroup;
4137 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4138 if (aBandwidthGroup && group.isNull())
4139 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4140
4141 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4142
4143 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4144 if (strBandwidthGroupOld.isNotEmpty())
4145 {
4146 /* Get the bandwidth group object and release it - this must not fail. */
4147 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4148 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4149 Assert(SUCCEEDED(hrc));
4150
4151 pBandwidthGroupOld->i_release();
4152 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4153 }
4154
4155 if (!group.isNull())
4156 {
4157 group->i_reference();
4158 pAttach->i_updateBandwidthGroup(group->i_getName());
4159 }
4160
4161 return S_OK;
4162}
4163
4164HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4165 LONG aControllerPort,
4166 LONG aDevice,
4167 DeviceType_T aType)
4168{
4169 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4170 aName.c_str(), aControllerPort, aDevice, aType));
4171
4172 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4173}
4174
4175
4176HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4177 LONG aControllerPort,
4178 LONG aDevice,
4179 BOOL aForce)
4180{
4181 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4182 aName.c_str(), aControllerPort, aForce));
4183
4184 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4185}
4186
4187HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4188 LONG aControllerPort,
4189 LONG aDevice,
4190 const ComPtr<IMedium> &aMedium,
4191 BOOL aForce)
4192{
4193 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4194 aName.c_str(), aControllerPort, aDevice, aForce));
4195
4196 // request the host lock first, since might be calling Host methods for getting host drives;
4197 // next, protect the media tree all the while we're in here, as well as our member variables
4198 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4199 this->lockHandle(),
4200 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4201
4202 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4203 if (FAILED(hrc)) return hrc;
4204
4205 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4206 aName,
4207 aControllerPort,
4208 aDevice);
4209 if (pAttach.isNull())
4210 return setError(VBOX_E_OBJECT_NOT_FOUND,
4211 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4212 aDevice, aControllerPort, aName.c_str());
4213
4214 /* Remember previously mounted medium. The medium before taking the
4215 * backup is not necessarily the same thing. */
4216 ComObjPtr<Medium> oldmedium;
4217 oldmedium = pAttach->i_getMedium();
4218
4219 IMedium *iM = aMedium;
4220 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4221 if (aMedium && pMedium.isNull())
4222 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4223
4224 /* Check if potential medium is already mounted */
4225 if (pMedium == oldmedium)
4226 return S_OK;
4227
4228 AutoCaller mediumCaller(pMedium);
4229 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4230
4231 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4232 if (pMedium)
4233 {
4234 DeviceType_T mediumType = pAttach->i_getType();
4235 switch (mediumType)
4236 {
4237 case DeviceType_DVD:
4238 case DeviceType_Floppy:
4239 break;
4240
4241 default:
4242 return setError(VBOX_E_INVALID_OBJECT_STATE,
4243 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4244 aControllerPort,
4245 aDevice,
4246 aName.c_str());
4247 }
4248 }
4249
4250 i_setModified(IsModified_Storage);
4251 mMediumAttachments.backup();
4252
4253 {
4254 // The backup operation makes the pAttach reference point to the
4255 // old settings. Re-get the correct reference.
4256 pAttach = i_findAttachment(*mMediumAttachments.data(),
4257 aName,
4258 aControllerPort,
4259 aDevice);
4260 if (!oldmedium.isNull())
4261 oldmedium->i_removeBackReference(mData->mUuid);
4262 if (!pMedium.isNull())
4263 {
4264 pMedium->i_addBackReference(mData->mUuid);
4265
4266 mediumLock.release();
4267 multiLock.release();
4268 i_addMediumToRegistry(pMedium);
4269 multiLock.acquire();
4270 mediumLock.acquire();
4271 }
4272
4273 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4274 pAttach->i_updateMedium(pMedium);
4275 }
4276
4277 i_setModified(IsModified_Storage);
4278
4279 mediumLock.release();
4280 multiLock.release();
4281 hrc = i_onMediumChange(pAttach, aForce);
4282 multiLock.acquire();
4283 mediumLock.acquire();
4284
4285 /* On error roll back this change only. */
4286 if (FAILED(hrc))
4287 {
4288 if (!pMedium.isNull())
4289 pMedium->i_removeBackReference(mData->mUuid);
4290 pAttach = i_findAttachment(*mMediumAttachments.data(),
4291 aName,
4292 aControllerPort,
4293 aDevice);
4294 /* If the attachment is gone in the meantime, bail out. */
4295 if (pAttach.isNull())
4296 return hrc;
4297 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4298 if (!oldmedium.isNull())
4299 oldmedium->i_addBackReference(mData->mUuid);
4300 pAttach->i_updateMedium(oldmedium);
4301 }
4302
4303 mediumLock.release();
4304 multiLock.release();
4305
4306 /* Save modified registries, but skip this machine as it's the caller's
4307 * job to save its settings like all other settings changes. */
4308 mParent->i_unmarkRegistryModified(i_getId());
4309 mParent->i_saveModifiedRegistries();
4310
4311 return hrc;
4312}
4313HRESULT Machine::getMedium(const com::Utf8Str &aName,
4314 LONG aControllerPort,
4315 LONG aDevice,
4316 ComPtr<IMedium> &aMedium)
4317{
4318 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4319 aName.c_str(), aControllerPort, aDevice));
4320
4321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4322
4323 aMedium = NULL;
4324
4325 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4326 aName,
4327 aControllerPort,
4328 aDevice);
4329 if (pAttach.isNull())
4330 return setError(VBOX_E_OBJECT_NOT_FOUND,
4331 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4332 aDevice, aControllerPort, aName.c_str());
4333
4334 aMedium = pAttach->i_getMedium();
4335
4336 return S_OK;
4337}
4338
4339HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4340{
4341 if (aSlot < RT_ELEMENTS(mSerialPorts))
4342 {
4343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4344 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4345 return S_OK;
4346 }
4347 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4348}
4349
4350HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4351{
4352 if (aSlot < RT_ELEMENTS(mParallelPorts))
4353 {
4354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4355 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4356 return S_OK;
4357 }
4358 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4359}
4360
4361
4362HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4363{
4364 /* Do not assert if slot is out of range, just return the advertised
4365 status. testdriver/vbox.py triggers this in logVmInfo. */
4366 if (aSlot >= mNetworkAdapters.size())
4367 return setError(E_INVALIDARG,
4368 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4369 aSlot, mNetworkAdapters.size());
4370
4371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4374
4375 return S_OK;
4376}
4377
4378HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4379{
4380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4381
4382 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4383 size_t i = 0;
4384 for (settings::StringsMap::const_iterator
4385 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4386 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4387 ++it, ++i)
4388 aKeys[i] = it->first;
4389
4390 return S_OK;
4391}
4392
4393 /**
4394 * @note Locks this object for reading.
4395 */
4396HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4397 com::Utf8Str &aValue)
4398{
4399 /* start with nothing found */
4400 aValue = "";
4401
4402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4403
4404 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4405 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4406 // found:
4407 aValue = it->second; // source is a Utf8Str
4408
4409 /* return the result to caller (may be empty) */
4410 return S_OK;
4411}
4412
4413 /**
4414 * @note Locks mParent for writing + this object for writing.
4415 */
4416HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4417{
4418 /* Because control characters in aKey have caused problems in the settings
4419 * they are rejected unless the key should be deleted. */
4420 if (!aValue.isEmpty())
4421 {
4422 for (size_t i = 0; i < aKey.length(); ++i)
4423 {
4424 char ch = aKey[i];
4425 if (RTLocCIsCntrl(ch))
4426 return E_INVALIDARG;
4427 }
4428 }
4429
4430 Utf8Str strOldValue; // empty
4431
4432 // locking note: we only hold the read lock briefly to look up the old value,
4433 // then release it and call the onExtraCanChange callbacks. There is a small
4434 // chance of a race insofar as the callback might be called twice if two callers
4435 // change the same key at the same time, but that's a much better solution
4436 // than the deadlock we had here before. The actual changing of the extradata
4437 // is then performed under the write lock and race-free.
4438
4439 // look up the old value first; if nothing has changed then we need not do anything
4440 {
4441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4442
4443 // For snapshots don't even think about allowing changes, extradata
4444 // is global for a machine, so there is nothing snapshot specific.
4445 if (i_isSnapshotMachine())
4446 return setError(VBOX_E_INVALID_VM_STATE,
4447 tr("Cannot set extradata for a snapshot"));
4448
4449 // check if the right IMachine instance is used
4450 if (mData->mRegistered && !i_isSessionMachine())
4451 return setError(VBOX_E_INVALID_VM_STATE,
4452 tr("Cannot set extradata for an immutable machine"));
4453
4454 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4455 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4456 strOldValue = it->second;
4457 }
4458
4459 bool fChanged;
4460 if ((fChanged = (strOldValue != aValue)))
4461 {
4462 // ask for permission from all listeners outside the locks;
4463 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4464 // lock to copy the list of callbacks to invoke
4465 Bstr bstrError;
4466 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4467 {
4468 const char *sep = bstrError.isEmpty() ? "" : ": ";
4469 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4470 return setError(E_ACCESSDENIED,
4471 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4472 aKey.c_str(),
4473 aValue.c_str(),
4474 sep,
4475 bstrError.raw());
4476 }
4477
4478 // data is changing and change not vetoed: then write it out under the lock
4479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4480
4481 if (aValue.isEmpty())
4482 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4483 else
4484 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4485 // creates a new key if needed
4486
4487 bool fNeedsGlobalSaveSettings = false;
4488 // This saving of settings is tricky: there is no "old state" for the
4489 // extradata items at all (unlike all other settings), so the old/new
4490 // settings comparison would give a wrong result!
4491 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4492
4493 if (fNeedsGlobalSaveSettings)
4494 {
4495 // save the global settings; for that we should hold only the VirtualBox lock
4496 alock.release();
4497 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4498 mParent->i_saveSettings();
4499 }
4500 }
4501
4502 // fire notification outside the lock
4503 if (fChanged)
4504 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4505
4506 return S_OK;
4507}
4508
4509HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4510{
4511 aProgress = NULL;
4512 NOREF(aSettingsFilePath);
4513 ReturnComNotImplemented();
4514}
4515
4516HRESULT Machine::saveSettings()
4517{
4518 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4519
4520 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4521 if (FAILED(hrc)) return hrc;
4522
4523 /* the settings file path may never be null */
4524 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4525
4526 /* save all VM data excluding snapshots */
4527 bool fNeedsGlobalSaveSettings = false;
4528 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4529 mlock.release();
4530
4531 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
4532 {
4533 // save the global settings; for that we should hold only the VirtualBox lock
4534 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4535 hrc = mParent->i_saveSettings();
4536 }
4537
4538 return hrc;
4539}
4540
4541
4542HRESULT Machine::discardSettings()
4543{
4544 /*
4545 * We need to take the machine list lock here as well as the machine one
4546 * or we'll get into trouble should any media stuff require rolling back.
4547 *
4548 * Details:
4549 *
4550 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4551 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4552 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
4553 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4554 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4555 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4556 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4557 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4558 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4559 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4560 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4561 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4562 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4563 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
4564 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
4565 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4566 * 0:005> k
4567 * # Child-SP RetAddr Call Site
4568 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4569 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4570 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4571 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4572 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4573 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4574 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4575 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4576 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4577 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4578 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4579 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4580 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4581 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4582 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4583 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4584 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4585 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4586 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4587 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4588 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4589 *
4590 */
4591 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4593
4594 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4595 if (FAILED(hrc)) return hrc;
4596
4597 /*
4598 * during this rollback, the session will be notified if data has
4599 * been actually changed
4600 */
4601 i_rollback(true /* aNotify */);
4602
4603 return S_OK;
4604}
4605
4606/** @note Locks objects! */
4607HRESULT Machine::unregister(AutoCaller &autoCaller,
4608 CleanupMode_T aCleanupMode,
4609 std::vector<ComPtr<IMedium> > &aMedia)
4610{
4611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 Guid id(i_getId());
4614
4615 if (mData->mSession.mState != SessionState_Unlocked)
4616 return setError(VBOX_E_INVALID_OBJECT_STATE,
4617 tr("Cannot unregister the machine '%s' while it is locked"),
4618 mUserData->s.strName.c_str());
4619
4620 // wait for state dependents to drop to zero
4621 i_ensureNoStateDependencies(alock);
4622
4623 if (!mData->mAccessible)
4624 {
4625 // inaccessible machines can only be unregistered; uninitialize ourselves
4626 // here because currently there may be no unregistered that are inaccessible
4627 // (this state combination is not supported). Note releasing the caller and
4628 // leaving the lock before calling uninit()
4629 alock.release();
4630 autoCaller.release();
4631
4632 uninit();
4633
4634 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
4635 // calls VirtualBox::i_saveSettings()
4636
4637 return S_OK;
4638 }
4639
4640 HRESULT hrc = S_OK;
4641 mData->llFilesToDelete.clear();
4642
4643 if (!mSSData->strStateFilePath.isEmpty())
4644 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4645
4646 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4647 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4648 mData->llFilesToDelete.push_back(strNVRAMFile);
4649
4650 // This list collects the medium objects from all medium attachments
4651 // which we will detach from the machine and its snapshots, in a specific
4652 // order which allows for closing all media without getting "media in use"
4653 // errors, simply by going through the list from the front to the back:
4654 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4655 // and must be closed before the parent media from the snapshots, or closing the parents
4656 // will fail because they still have children);
4657 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4658 // the root ("first") snapshot of the machine.
4659 MediaList llMedia;
4660
4661 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4662 && mMediumAttachments->size()
4663 )
4664 {
4665 // we have media attachments: detach them all and add the Medium objects to our list
4666 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4667 }
4668
4669 if (mData->mFirstSnapshot)
4670 {
4671 // add the media from the medium attachments of the snapshots to
4672 // llMedia as well, after the "main" machine media;
4673 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
4674 // snapshot machine, depth first.
4675
4676 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4677 MachineState_T oldState = mData->mMachineState;
4678 mData->mMachineState = MachineState_DeletingSnapshot;
4679
4680 // make a copy of the first snapshot reference so the refcount does not
4681 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4682 // (would hang due to the AutoCaller voodoo)
4683 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4684
4685 // GO!
4686 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4687
4688 mData->mMachineState = oldState;
4689 }
4690
4691 if (FAILED(hrc))
4692 {
4693 i_rollbackMedia();
4694 return hrc;
4695 }
4696
4697 // commit all the media changes made above
4698 i_commitMedia();
4699
4700 mData->mRegistered = false;
4701
4702 // machine lock no longer needed
4703 alock.release();
4704
4705 /* Make sure that the settings of the current VM are not saved, because
4706 * they are rather crippled at this point to meet the cleanup expectations
4707 * and there's no point destroying the VM config on disk just because. */
4708 mParent->i_unmarkRegistryModified(id);
4709
4710 // return media to caller
4711 aMedia.resize(llMedia.size());
4712 size_t i = 0;
4713 for (MediaList::const_iterator
4714 it = llMedia.begin();
4715 it != llMedia.end();
4716 ++it, ++i)
4717 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4718
4719 mParent->i_unregisterMachine(this, aCleanupMode, id);
4720 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4721
4722 return S_OK;
4723}
4724
4725/**
4726 * Task record for deleting a machine config.
4727 */
4728class Machine::DeleteConfigTask
4729 : public Machine::Task
4730{
4731public:
4732 DeleteConfigTask(Machine *m,
4733 Progress *p,
4734 const Utf8Str &t,
4735 const RTCList<ComPtr<IMedium> > &llMedia,
4736 const StringsList &llFilesToDelete)
4737 : Task(m, p, t),
4738 m_llMedia(llMedia),
4739 m_llFilesToDelete(llFilesToDelete)
4740 {}
4741
4742private:
4743 void handler()
4744 {
4745 try
4746 {
4747 m_pMachine->i_deleteConfigHandler(*this);
4748 }
4749 catch (...)
4750 {
4751 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4752 }
4753 }
4754
4755 RTCList<ComPtr<IMedium> > m_llMedia;
4756 StringsList m_llFilesToDelete;
4757
4758 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4759};
4760
4761/**
4762 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4763 * SessionMachine::taskHandler().
4764 *
4765 * @note Locks this object for writing.
4766 *
4767 * @param task
4768 */
4769void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
4770{
4771 LogFlowThisFuncEnter();
4772
4773 AutoCaller autoCaller(this);
4774 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
4775 if (FAILED(autoCaller.hrc()))
4776 {
4777 /* we might have been uninitialized because the session was accidentally
4778 * closed by the client, so don't assert */
4779 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
4780 task.m_pProgress->i_notifyComplete(hrc);
4781 LogFlowThisFuncLeave();
4782 return;
4783 }
4784
4785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
4787 HRESULT hrc;
4788 try
4789 {
4790 ULONG uLogHistoryCount = 3;
4791 ComPtr<ISystemProperties> systemProperties;
4792 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4793 if (FAILED(hrc)) throw hrc;
4794
4795 if (!systemProperties.isNull())
4796 {
4797 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4798 if (FAILED(hrc)) throw hrc;
4799 }
4800
4801 MachineState_T oldState = mData->mMachineState;
4802 i_setMachineState(MachineState_SettingUp);
4803 alock.release();
4804 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
4805 {
4806 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
4807 {
4808 AutoCaller mac(pMedium);
4809 if (FAILED(mac.hrc())) throw mac.hrc();
4810 Utf8Str strLocation = pMedium->i_getLocationFull();
4811 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4812 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4813 if (FAILED(hrc)) throw hrc;
4814 }
4815 if (pMedium->i_isMediumFormatFile())
4816 {
4817 ComPtr<IProgress> pProgress2;
4818 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
4819 if (FAILED(hrc)) throw hrc;
4820 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
4821 if (FAILED(hrc)) throw hrc;
4822 }
4823
4824 /* Close the medium, deliberately without checking the return
4825 * code, and without leaving any trace in the error info, as
4826 * a failure here is a very minor issue, which shouldn't happen
4827 * as above we even managed to delete the medium. */
4828 {
4829 ErrorInfoKeeper eik;
4830 pMedium->Close();
4831 }
4832 }
4833 i_setMachineState(oldState);
4834 alock.acquire();
4835
4836 // delete the files pushed on the task list by Machine::Delete()
4837 // (this includes saved states of the machine and snapshots and
4838 // medium storage files from the IMedium list passed in, and the
4839 // machine XML file)
4840 for (StringsList::const_iterator
4841 it = task.m_llFilesToDelete.begin();
4842 it != task.m_llFilesToDelete.end();
4843 ++it)
4844 {
4845 const Utf8Str &strFile = *it;
4846 LogFunc(("Deleting file %s\n", strFile.c_str()));
4847 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4848 if (FAILED(hrc)) throw hrc;
4849 i_deleteFile(strFile);
4850 }
4851
4852 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4853 if (FAILED(hrc)) throw hrc;
4854
4855 /* delete the settings only when the file actually exists */
4856 if (mData->pMachineConfigFile->fileExists())
4857 {
4858 /* Delete any backup or uncommitted XML files. Ignore failures.
4859 See the fSafe parameter of xml::XmlFileWriter::write for details. */
4860 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4861 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4862 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4863 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4864 i_deleteFile(otherXml, true /* fIgnoreFailures */);
4865
4866 /* delete the Logs folder, nothing important should be left
4867 * there (we don't check for errors because the user might have
4868 * some private files there that we don't want to delete) */
4869 Utf8Str logFolder;
4870 getLogFolder(logFolder);
4871 Assert(logFolder.length());
4872 if (RTDirExists(logFolder.c_str()))
4873 {
4874 /* Delete all VBox.log[.N] files from the Logs folder
4875 * (this must be in sync with the rotation logic in
4876 * Console::powerUpThread()). Also, delete the VBox.png[.N]
4877 * files that may have been created by the GUI. */
4878 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
4879 i_deleteFile(log, true /* fIgnoreFailures */);
4880 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
4881 i_deleteFile(log, true /* fIgnoreFailures */);
4882 for (ULONG i = uLogHistoryCount; i > 0; i--)
4883 {
4884 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4885 i_deleteFile(log, true /* fIgnoreFailures */);
4886 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
4887 i_deleteFile(log, true /* fIgnoreFailures */);
4888 }
4889 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
4890 i_deleteFile(log, true /* fIgnoreFailures */);
4891#if defined(RT_OS_WINDOWS)
4892 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
4893 i_deleteFile(log, true /* fIgnoreFailures */);
4894 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
4895 i_deleteFile(log, true /* fIgnoreFailures */);
4896#endif
4897
4898 RTDirRemove(logFolder.c_str());
4899 }
4900
4901 /* delete the Snapshots folder, nothing important should be left
4902 * there (we don't check for errors because the user might have
4903 * some private files there that we don't want to delete) */
4904 Utf8Str strFullSnapshotFolder;
4905 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4906 Assert(!strFullSnapshotFolder.isEmpty());
4907 if (RTDirExists(strFullSnapshotFolder.c_str()))
4908 RTDirRemove(strFullSnapshotFolder.c_str());
4909
4910 // delete the directory that contains the settings file, but only
4911 // if it matches the VM name
4912 Utf8Str settingsDir;
4913 if (i_isInOwnDir(&settingsDir))
4914 RTDirRemove(settingsDir.c_str());
4915 }
4916
4917 alock.release();
4918
4919 mParent->i_saveModifiedRegistries();
4920 }
4921 catch (HRESULT hrcXcpt)
4922 {
4923 hrc = hrcXcpt;
4924 }
4925
4926 task.m_pProgress->i_notifyComplete(hrc);
4927
4928 LogFlowThisFuncLeave();
4929}
4930
4931HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
4932{
4933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4934
4935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4936 if (FAILED(hrc)) return hrc;
4937
4938 if (mData->mRegistered)
4939 return setError(VBOX_E_INVALID_VM_STATE,
4940 tr("Cannot delete settings of a registered machine"));
4941
4942 // collect files to delete
4943 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
4944 // machine config file
4945 if (mData->pMachineConfigFile->fileExists())
4946 llFilesToDelete.push_back(mData->m_strConfigFileFull);
4947 // backup of machine config file
4948 Utf8Str strTmp(mData->m_strConfigFileFull);
4949 strTmp.append("-prev");
4950 if (RTFileExists(strTmp.c_str()))
4951 llFilesToDelete.push_back(strTmp);
4952
4953 RTCList<ComPtr<IMedium> > llMedia;
4954 for (size_t i = 0; i < aMedia.size(); ++i)
4955 {
4956 IMedium *pIMedium(aMedia[i]);
4957 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4958 if (pMedium.isNull())
4959 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
4960 SafeArray<BSTR> ids;
4961 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4962 if (FAILED(hrc)) return hrc;
4963 /* At this point the medium should not have any back references
4964 * anymore. If it has it is attached to another VM and *must* not
4965 * deleted. */
4966 if (ids.size() < 1)
4967 llMedia.append(pMedium);
4968 }
4969
4970 ComObjPtr<Progress> pProgress;
4971 pProgress.createObject();
4972 hrc = pProgress->init(i_getVirtualBox(),
4973 static_cast<IMachine*>(this) /* aInitiator */,
4974 tr("Deleting files"),
4975 true /* fCancellable */,
4976 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
4977 tr("Collecting file inventory"));
4978 if (FAILED(hrc))
4979 return hrc;
4980
4981 /* create and start the task on a separate thread (note that it will not
4982 * start working until we release alock) */
4983 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
4984 hrc = pTask->createThread();
4985 pTask = NULL;
4986 if (FAILED(hrc))
4987 return hrc;
4988
4989 pProgress.queryInterfaceTo(aProgress.asOutParam());
4990
4991 LogFlowFuncLeave();
4992
4993 return S_OK;
4994}
4995
4996HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
4997{
4998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4999
5000 ComObjPtr<Snapshot> pSnapshot;
5001 HRESULT hrc;
5002
5003 if (aNameOrId.isEmpty())
5004 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5005 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5006 else
5007 {
5008 Guid uuid(aNameOrId);
5009 if (uuid.isValid())
5010 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5011 else
5012 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5013 }
5014 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5015
5016 return hrc;
5017}
5018
5019HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5020 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5021{
5022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5023
5024 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5025 if (FAILED(hrc)) return hrc;
5026
5027 ComObjPtr<SharedFolder> sharedFolder;
5028 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5029 if (SUCCEEDED(hrc))
5030 return setError(VBOX_E_OBJECT_IN_USE,
5031 tr("Shared folder named '%s' already exists"),
5032 aName.c_str());
5033
5034 sharedFolder.createObject();
5035 hrc = sharedFolder->init(i_getMachine(),
5036 aName,
5037 aHostPath,
5038 !!aWritable,
5039 !!aAutomount,
5040 aAutoMountPoint,
5041 true /* fFailOnError */);
5042 if (FAILED(hrc)) return hrc;
5043
5044 i_setModified(IsModified_SharedFolders);
5045 mHWData.backup();
5046 mHWData->mSharedFolders.push_back(sharedFolder);
5047
5048 /* inform the direct session if any */
5049 alock.release();
5050 i_onSharedFolderChange();
5051
5052 return S_OK;
5053}
5054
5055HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5056{
5057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5058
5059 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5060 if (FAILED(hrc)) return hrc;
5061
5062 ComObjPtr<SharedFolder> sharedFolder;
5063 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5064 if (FAILED(hrc)) return hrc;
5065
5066 i_setModified(IsModified_SharedFolders);
5067 mHWData.backup();
5068 mHWData->mSharedFolders.remove(sharedFolder);
5069
5070 /* inform the direct session if any */
5071 alock.release();
5072 i_onSharedFolderChange();
5073
5074 return S_OK;
5075}
5076
5077HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5078{
5079 /* start with No */
5080 *aCanShow = FALSE;
5081
5082 ComPtr<IInternalSessionControl> directControl;
5083 {
5084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5085
5086 if (mData->mSession.mState != SessionState_Locked)
5087 return setError(VBOX_E_INVALID_VM_STATE,
5088 tr("Machine is not locked for session (session state: %s)"),
5089 Global::stringifySessionState(mData->mSession.mState));
5090
5091 if (mData->mSession.mLockType == LockType_VM)
5092 directControl = mData->mSession.mDirectControl;
5093 }
5094
5095 /* ignore calls made after #OnSessionEnd() is called */
5096 if (!directControl)
5097 return S_OK;
5098
5099 LONG64 dummy;
5100 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5101}
5102
5103HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5104{
5105 ComPtr<IInternalSessionControl> directControl;
5106 {
5107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5108
5109 if (mData->mSession.mState != SessionState_Locked)
5110 return setError(E_FAIL,
5111 tr("Machine is not locked for session (session state: %s)"),
5112 Global::stringifySessionState(mData->mSession.mState));
5113
5114 if (mData->mSession.mLockType == LockType_VM)
5115 directControl = mData->mSession.mDirectControl;
5116 }
5117
5118 /* ignore calls made after #OnSessionEnd() is called */
5119 if (!directControl)
5120 return S_OK;
5121
5122 BOOL dummy;
5123 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5124}
5125
5126#ifdef VBOX_WITH_GUEST_PROPS
5127/**
5128 * Look up a guest property in VBoxSVC's internal structures.
5129 */
5130HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5131 com::Utf8Str &aValue,
5132 LONG64 *aTimestamp,
5133 com::Utf8Str &aFlags) const
5134{
5135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5136
5137 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5138 if (it != mHWData->mGuestProperties.end())
5139 {
5140 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5141 aValue = it->second.strValue;
5142 *aTimestamp = it->second.mTimestamp;
5143 GuestPropWriteFlags(it->second.mFlags, szFlags);
5144 aFlags = Utf8Str(szFlags);
5145 }
5146
5147 return S_OK;
5148}
5149
5150/**
5151 * Query the VM that a guest property belongs to for the property.
5152 * @returns E_ACCESSDENIED if the VM process is not available or not
5153 * currently handling queries and the lookup should then be done in
5154 * VBoxSVC.
5155 */
5156HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5157 com::Utf8Str &aValue,
5158 LONG64 *aTimestamp,
5159 com::Utf8Str &aFlags) const
5160{
5161 HRESULT hrc = S_OK;
5162 Bstr bstrValue;
5163 Bstr bstrFlags;
5164
5165 ComPtr<IInternalSessionControl> directControl;
5166 {
5167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5168 if (mData->mSession.mLockType == LockType_VM)
5169 directControl = mData->mSession.mDirectControl;
5170 }
5171
5172 /* ignore calls made after #OnSessionEnd() is called */
5173 if (!directControl)
5174 hrc = E_ACCESSDENIED;
5175 else
5176 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5177 0 /* accessMode */,
5178 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5179
5180 aValue = bstrValue;
5181 aFlags = bstrFlags;
5182
5183 return hrc;
5184}
5185#endif // VBOX_WITH_GUEST_PROPS
5186
5187HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5188 com::Utf8Str &aValue,
5189 LONG64 *aTimestamp,
5190 com::Utf8Str &aFlags)
5191{
5192#ifndef VBOX_WITH_GUEST_PROPS
5193 ReturnComNotImplemented();
5194#else // VBOX_WITH_GUEST_PROPS
5195
5196 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5197
5198 if (hrc == E_ACCESSDENIED)
5199 /* The VM is not running or the service is not (yet) accessible */
5200 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5201 return hrc;
5202#endif // VBOX_WITH_GUEST_PROPS
5203}
5204
5205HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5206{
5207 LONG64 dummyTimestamp;
5208 com::Utf8Str dummyFlags;
5209 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5210
5211}
5212HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5213{
5214 com::Utf8Str dummyFlags;
5215 com::Utf8Str dummyValue;
5216 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5217}
5218
5219#ifdef VBOX_WITH_GUEST_PROPS
5220/**
5221 * Set a guest property in VBoxSVC's internal structures.
5222 */
5223HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5224 const com::Utf8Str &aFlags, bool fDelete)
5225{
5226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5227 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5228 if (FAILED(hrc)) return hrc;
5229
5230 try
5231 {
5232 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5233 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5234 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5235
5236 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5237 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5238
5239 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5240 if (it == mHWData->mGuestProperties.end())
5241 {
5242 if (!fDelete)
5243 {
5244 i_setModified(IsModified_MachineData);
5245 mHWData.backupEx();
5246
5247 RTTIMESPEC time;
5248 HWData::GuestProperty prop;
5249 prop.strValue = aValue;
5250 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5251 prop.mFlags = fFlags;
5252 mHWData->mGuestProperties[aName] = prop;
5253 }
5254 }
5255 else
5256 {
5257 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5258 {
5259 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5260 }
5261 else
5262 {
5263 i_setModified(IsModified_MachineData);
5264 mHWData.backupEx();
5265
5266 /* The backupEx() operation invalidates our iterator,
5267 * so get a new one. */
5268 it = mHWData->mGuestProperties.find(aName);
5269 Assert(it != mHWData->mGuestProperties.end());
5270
5271 if (!fDelete)
5272 {
5273 RTTIMESPEC time;
5274 it->second.strValue = aValue;
5275 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5276 it->second.mFlags = fFlags;
5277 }
5278 else
5279 mHWData->mGuestProperties.erase(it);
5280 }
5281 }
5282
5283 if (SUCCEEDED(hrc))
5284 {
5285 alock.release();
5286
5287 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5288 }
5289 }
5290 catch (std::bad_alloc &)
5291 {
5292 hrc = E_OUTOFMEMORY;
5293 }
5294
5295 return hrc;
5296}
5297
5298/**
5299 * Set a property on the VM that that property belongs to.
5300 * @returns E_ACCESSDENIED if the VM process is not available or not
5301 * currently handling queries and the setting should then be done in
5302 * VBoxSVC.
5303 */
5304HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5305 const com::Utf8Str &aFlags, bool fDelete)
5306{
5307 HRESULT hrc;
5308
5309 try
5310 {
5311 ComPtr<IInternalSessionControl> directControl;
5312 {
5313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5314 if (mData->mSession.mLockType == LockType_VM)
5315 directControl = mData->mSession.mDirectControl;
5316 }
5317
5318 Bstr dummy1; /* will not be changed (setter) */
5319 Bstr dummy2; /* will not be changed (setter) */
5320 LONG64 dummy64;
5321 if (!directControl)
5322 hrc = E_ACCESSDENIED;
5323 else
5324 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5325 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5326 fDelete ? 2 : 1 /* accessMode */,
5327 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5328 }
5329 catch (std::bad_alloc &)
5330 {
5331 hrc = E_OUTOFMEMORY;
5332 }
5333
5334 return hrc;
5335}
5336#endif // VBOX_WITH_GUEST_PROPS
5337
5338HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5339 const com::Utf8Str &aFlags)
5340{
5341#ifndef VBOX_WITH_GUEST_PROPS
5342 ReturnComNotImplemented();
5343#else // VBOX_WITH_GUEST_PROPS
5344
5345 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5346 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5347
5348 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5349 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5350
5351 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5352 if (hrc == E_ACCESSDENIED)
5353 /* The VM is not running or the service is not (yet) accessible */
5354 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5355 return hrc;
5356#endif // VBOX_WITH_GUEST_PROPS
5357}
5358
5359HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5360{
5361 return setGuestProperty(aProperty, aValue, "");
5362}
5363
5364HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5365{
5366#ifndef VBOX_WITH_GUEST_PROPS
5367 ReturnComNotImplemented();
5368#else // VBOX_WITH_GUEST_PROPS
5369 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5370 if (hrc == E_ACCESSDENIED)
5371 /* The VM is not running or the service is not (yet) accessible */
5372 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5373 return hrc;
5374#endif // VBOX_WITH_GUEST_PROPS
5375}
5376
5377#ifdef VBOX_WITH_GUEST_PROPS
5378/**
5379 * Enumerate the guest properties in VBoxSVC's internal structures.
5380 */
5381HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5382 std::vector<com::Utf8Str> &aNames,
5383 std::vector<com::Utf8Str> &aValues,
5384 std::vector<LONG64> &aTimestamps,
5385 std::vector<com::Utf8Str> &aFlags)
5386{
5387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5388 Utf8Str strPatterns(aPatterns);
5389
5390 /*
5391 * Look for matching patterns and build up a list.
5392 */
5393 HWData::GuestPropertyMap propMap;
5394 for (HWData::GuestPropertyMap::const_iterator
5395 it = mHWData->mGuestProperties.begin();
5396 it != mHWData->mGuestProperties.end();
5397 ++it)
5398 {
5399 if ( strPatterns.isEmpty()
5400 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5401 RTSTR_MAX,
5402 it->first.c_str(),
5403 RTSTR_MAX,
5404 NULL)
5405 )
5406 propMap.insert(*it);
5407 }
5408
5409 alock.release();
5410
5411 /*
5412 * And build up the arrays for returning the property information.
5413 */
5414 size_t cEntries = propMap.size();
5415
5416 aNames.resize(cEntries);
5417 aValues.resize(cEntries);
5418 aTimestamps.resize(cEntries);
5419 aFlags.resize(cEntries);
5420
5421 size_t i = 0;
5422 for (HWData::GuestPropertyMap::const_iterator
5423 it = propMap.begin();
5424 it != propMap.end();
5425 ++it, ++i)
5426 {
5427 aNames[i] = it->first;
5428 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
5429 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5430
5431 aValues[i] = it->second.strValue;
5432 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
5433 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
5434
5435 aTimestamps[i] = it->second.mTimestamp;
5436
5437 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5438 GuestPropWriteFlags(it->second.mFlags, szFlags);
5439 aFlags[i] = szFlags;
5440 }
5441
5442 return S_OK;
5443}
5444
5445/**
5446 * Enumerate the properties managed by a VM.
5447 * @returns E_ACCESSDENIED if the VM process is not available or not
5448 * currently handling queries and the setting should then be done in
5449 * VBoxSVC.
5450 */
5451HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5452 std::vector<com::Utf8Str> &aNames,
5453 std::vector<com::Utf8Str> &aValues,
5454 std::vector<LONG64> &aTimestamps,
5455 std::vector<com::Utf8Str> &aFlags)
5456{
5457 HRESULT hrc;
5458 ComPtr<IInternalSessionControl> directControl;
5459 {
5460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5461 if (mData->mSession.mLockType == LockType_VM)
5462 directControl = mData->mSession.mDirectControl;
5463 }
5464
5465 com::SafeArray<BSTR> bNames;
5466 com::SafeArray<BSTR> bValues;
5467 com::SafeArray<LONG64> bTimestamps;
5468 com::SafeArray<BSTR> bFlags;
5469
5470 if (!directControl)
5471 hrc = E_ACCESSDENIED;
5472 else
5473 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5474 ComSafeArrayAsOutParam(bNames),
5475 ComSafeArrayAsOutParam(bValues),
5476 ComSafeArrayAsOutParam(bTimestamps),
5477 ComSafeArrayAsOutParam(bFlags));
5478 size_t i;
5479 aNames.resize(bNames.size());
5480 for (i = 0; i < bNames.size(); ++i)
5481 aNames[i] = Utf8Str(bNames[i]);
5482 aValues.resize(bValues.size());
5483 for (i = 0; i < bValues.size(); ++i)
5484 aValues[i] = Utf8Str(bValues[i]);
5485 aTimestamps.resize(bTimestamps.size());
5486 for (i = 0; i < bTimestamps.size(); ++i)
5487 aTimestamps[i] = bTimestamps[i];
5488 aFlags.resize(bFlags.size());
5489 for (i = 0; i < bFlags.size(); ++i)
5490 aFlags[i] = Utf8Str(bFlags[i]);
5491
5492 return hrc;
5493}
5494#endif // VBOX_WITH_GUEST_PROPS
5495HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5496 std::vector<com::Utf8Str> &aNames,
5497 std::vector<com::Utf8Str> &aValues,
5498 std::vector<LONG64> &aTimestamps,
5499 std::vector<com::Utf8Str> &aFlags)
5500{
5501#ifndef VBOX_WITH_GUEST_PROPS
5502 ReturnComNotImplemented();
5503#else // VBOX_WITH_GUEST_PROPS
5504
5505 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5506
5507 if (hrc == E_ACCESSDENIED)
5508 /* The VM is not running or the service is not (yet) accessible */
5509 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5510 return hrc;
5511#endif // VBOX_WITH_GUEST_PROPS
5512}
5513
5514HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5515 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5516{
5517 MediumAttachmentList atts;
5518
5519 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
5520 if (FAILED(hrc)) return hrc;
5521
5522 aMediumAttachments.resize(atts.size());
5523 size_t i = 0;
5524 for (MediumAttachmentList::const_iterator
5525 it = atts.begin();
5526 it != atts.end();
5527 ++it, ++i)
5528 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5529
5530 return S_OK;
5531}
5532
5533HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5534 LONG aControllerPort,
5535 LONG aDevice,
5536 ComPtr<IMediumAttachment> &aAttachment)
5537{
5538 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5539 aName.c_str(), aControllerPort, aDevice));
5540
5541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5542
5543 aAttachment = NULL;
5544
5545 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5546 aName,
5547 aControllerPort,
5548 aDevice);
5549 if (pAttach.isNull())
5550 return setError(VBOX_E_OBJECT_NOT_FOUND,
5551 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5552 aDevice, aControllerPort, aName.c_str());
5553
5554 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5555
5556 return S_OK;
5557}
5558
5559
5560HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5561 StorageBus_T aConnectionType,
5562 ComPtr<IStorageController> &aController)
5563{
5564