VirtualBox

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

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

doc/manual,Main,Frontends: API changes in preparation for full VM encryption, guarded by VBOX_WITH_FULL_VM_ENCRYPTION (disabled by default) and returning a not supported error for now, bugref:9955 [doxygen fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 547.7 KB
Line 
1/* $Id: MachineImpl.cpp 94661 2022-04-21 08:47:26Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "SnapshotImpl.h"
32#include "ClientToken.h"
33#include "ProgressImpl.h"
34#include "ProgressProxyImpl.h"
35#include "MediumAttachmentImpl.h"
36#include "MediumImpl.h"
37#include "MediumLock.h"
38#include "USBControllerImpl.h"
39#include "USBDeviceFiltersImpl.h"
40#include "HostImpl.h"
41#include "SharedFolderImpl.h"
42#include "GuestOSTypeImpl.h"
43#include "VirtualBoxErrorInfoImpl.h"
44#include "StorageControllerImpl.h"
45#include "DisplayImpl.h"
46#include "DisplayUtils.h"
47#include "MachineImplCloneVM.h"
48#include "AutostartDb.h"
49#include "SystemPropertiesImpl.h"
50#include "MachineImplMoveVM.h"
51#include "ExtPackManagerImpl.h"
52#include "MachineLaunchVMCommonWorker.h"
53
54// generated header
55#include "VBoxEvents.h"
56
57#ifdef VBOX_WITH_USB
58# include "USBProxyService.h"
59#endif
60
61#include "AutoCaller.h"
62#include "HashedPw.h"
63#include "Performance.h"
64#include "StringifyEnums.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#include <iprt/ctype.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/VMMDev.h>
85#include <VBox/vmm/ssm.h>
86
87#ifdef VBOX_WITH_GUEST_PROPS
88# include <VBox/HostServices/GuestPropertySvc.h>
89# include <VBox/com/array.h>
90#endif
91
92#ifdef VBOX_WITH_SHARED_CLIPBOARD
93# include <VBox/HostServices/VBoxClipboardSvc.h>
94#endif
95
96#include "VBox/com/MultiResult.h"
97
98#include <algorithm>
99
100#ifdef VBOX_WITH_DTRACE_R3_MAIN
101# include "dtrace/VBoxAPI.h"
102#endif
103
104#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
105# define HOSTSUFF_EXE ".exe"
106#else /* !RT_OS_WINDOWS */
107# define HOSTSUFF_EXE ""
108#endif /* !RT_OS_WINDOWS */
109
110// defines / prototypes
111/////////////////////////////////////////////////////////////////////////////
112
113/////////////////////////////////////////////////////////////////////////////
114// Machine::Data structure
115/////////////////////////////////////////////////////////////////////////////
116
117Machine::Data::Data()
118{
119 mRegistered = FALSE;
120 pMachineConfigFile = NULL;
121 /* Contains hints on what has changed when the user is using the VM (config
122 * changes, running the VM, ...). This is used to decide if a config needs
123 * to be written to disk. */
124 flModifications = 0;
125 /* VM modification usually also trigger setting the current state to
126 * "Modified". Although this is not always the case. An e.g. is the VM
127 * initialization phase or when snapshot related data is changed. The
128 * actually behavior is controlled by the following flag. */
129 m_fAllowStateModification = false;
130 mAccessible = FALSE;
131 /* mUuid is initialized in Machine::init() */
132
133 mMachineState = MachineState_PoweredOff;
134 RTTimeNow(&mLastStateChange);
135
136 mMachineStateDeps = 0;
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 mMachineStateChangePending = 0;
139
140 mCurrentStateModified = TRUE;
141 mGuestPropertiesModified = FALSE;
142
143 mSession.mPID = NIL_RTPROCESS;
144 mSession.mLockType = LockType_Null;
145 mSession.mState = SessionState_Unlocked;
146}
147
148Machine::Data::~Data()
149{
150 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
151 {
152 RTSemEventMultiDestroy(mMachineStateDepsSem);
153 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
154 }
155 if (pMachineConfigFile)
156 {
157 delete pMachineConfigFile;
158 pMachineConfigFile = NULL;
159 }
160}
161
162/////////////////////////////////////////////////////////////////////////////
163// Machine::HWData structure
164/////////////////////////////////////////////////////////////////////////////
165
166Machine::HWData::HWData()
167{
168 /* default values for a newly created machine */
169 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
170 mMemorySize = 128;
171 mCPUCount = 1;
172 mCPUHotPlugEnabled = false;
173 mMemoryBalloonSize = 0;
174 mPageFusionEnabled = false;
175 mHWVirtExEnabled = true;
176 mHWVirtExNestedPagingEnabled = true;
177 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
178 mHWVirtExVPIDEnabled = true;
179 mHWVirtExUXEnabled = true;
180 mHWVirtExForceEnabled = false;
181 mHWVirtExUseNativeApi = false;
182 mHWVirtExVirtVmsaveVmload = true;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mTripleFaultReset = false;
190 mAPIC = true;
191 mX2APIC = false;
192 mIBPBOnVMExit = false;
193 mIBPBOnVMEntry = false;
194 mSpecCtrl = false;
195 mSpecCtrlByHost = false;
196 mL1DFlushOnSched = true;
197 mL1DFlushOnVMEntry = false;
198 mMDSClearOnSched = true;
199 mMDSClearOnVMEntry = false;
200 mNestedHWVirt = false;
201 mHPETEnabled = false;
202 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
203 mCpuIdPortabilityLevel = 0;
204 mCpuProfile = "host";
205
206 /* default boot order: floppy - DVD - HDD */
207 mBootOrder[0] = DeviceType_Floppy;
208 mBootOrder[1] = DeviceType_DVD;
209 mBootOrder[2] = DeviceType_HardDisk;
210 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
211 mBootOrder[i] = DeviceType_Null;
212
213 mClipboardMode = ClipboardMode_Disabled;
214 mClipboardFileTransfersEnabled = FALSE;
215
216 mDnDMode = DnDMode_Disabled;
217
218 mFirmwareType = FirmwareType_BIOS;
219 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
220 mPointingHIDType = PointingHIDType_PS2Mouse;
221 mChipsetType = ChipsetType_PIIX3;
222 mIommuType = IommuType_None;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param strOsType OS Type string (stored as is if aOsType is NULL).
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
285 * scheme (includes the UUID).
286 * @param aCipher The cipher to encrypt the VM with.
287 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
288 * @param aPassword The password to encrypt the VM with.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 const Utf8Str &strOsType,
297 GuestOSType *aOsType,
298 const Guid &aId,
299 bool fForceOverwrite,
300 bool fDirectoryIncludesUUID,
301 const com::Utf8Str &aCipher,
302 const com::Utf8Str &aPasswordId,
303 const com::Utf8Str &aPassword)
304{
305 LogFlowThisFuncEnter();
306 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
307
308#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
309 RT_NOREF(aCipher);
310 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
311 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
312#else
313 /** @todo */
314 RT_NOREF(aCipher, aPasswordId, aPassword);
315#endif
316
317 /* Enclose the state transition NotReady->InInit->Ready */
318 AutoInitSpan autoInitSpan(this);
319 AssertReturn(autoInitSpan.isOk(), E_FAIL);
320
321 HRESULT rc = initImpl(aParent, strConfigFile);
322 if (FAILED(rc)) return rc;
323
324 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
325 if (FAILED(rc)) return rc;
326
327 if (SUCCEEDED(rc))
328 {
329 // create an empty machine config
330 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
331
332 rc = initDataAndChildObjects();
333 }
334
335 if (SUCCEEDED(rc))
336 {
337 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
338 mData->mAccessible = TRUE;
339
340 unconst(mData->mUuid) = aId;
341
342 mUserData->s.strName = strName;
343
344 if (llGroups.size())
345 mUserData->s.llGroups = llGroups;
346
347 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
348 // the "name sync" flag determines whether the machine directory gets renamed along
349 // with the machine file; say so if the settings file name is the same as the
350 // settings file parent directory (machine directory)
351 mUserData->s.fNameSync = i_isInOwnDir();
352
353 // initialize the default snapshots folder
354 rc = COMSETTER(SnapshotFolder)(NULL);
355 AssertComRC(rc);
356
357 if (aOsType)
358 {
359 /* Store OS type */
360 mUserData->s.strOsType = aOsType->i_id();
361
362 /* Let the OS type select 64-bit ness. */
363 mHWData->mLongMode = aOsType->i_is64Bit()
364 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365
366 /* Let the OS type enable the X2APIC */
367 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
368
369 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
370 AssertComRC(rc);
371 }
372 else if (!strOsType.isEmpty())
373 {
374 /* Store OS type */
375 mUserData->s.strOsType = strOsType;
376
377 /* No guest OS type object. Pick some plausible defaults which the
378 * host can handle. There's no way to know or validate anything. */
379 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
380 mHWData->mX2APIC = false;
381 }
382
383 /* Apply BIOS defaults. */
384 mBIOSSettings->i_applyDefaults(aOsType);
385
386 /* Apply TPM defaults. */
387 mTrustedPlatformModule->i_applyDefaults(aOsType);
388
389 /* Apply record defaults. */
390 mRecordingSettings->i_applyDefaults();
391
392 /* Apply network adapters defaults */
393 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
394 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
395
396 /* Apply serial port defaults */
397 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
398 mSerialPorts[slot]->i_applyDefaults(aOsType);
399
400 /* Apply parallel port defaults */
401 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
402 mParallelPorts[slot]->i_applyDefaults();
403
404 /* Enable the VMMDev testing feature for bootsector VMs: */
405 if (aOsType && aOsType->i_id() == "VBoxBS_64")
406 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
407
408 /* At this point the changing of the current state modification
409 * flag is allowed. */
410 i_allowStateModification();
411
412 /* commit all changes made during the initialization */
413 i_commit();
414 }
415
416 /* Confirm a successful initialization when it's the case */
417 if (SUCCEEDED(rc))
418 {
419 if (mData->mAccessible)
420 autoInitSpan.setSucceeded();
421 else
422 autoInitSpan.setLimited();
423 }
424
425 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
426 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
427 mData->mRegistered,
428 mData->mAccessible,
429 rc));
430
431 LogFlowThisFuncLeave();
432
433 return rc;
434}
435
436/**
437 * Initializes a new instance with data from machine XML (formerly Init_Registered).
438 * Gets called in two modes:
439 *
440 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
441 * UUID is specified and we mark the machine as "registered";
442 *
443 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
444 * and the machine remains unregistered until RegisterMachine() is called.
445 *
446 * @param aParent Associated parent object
447 * @param strConfigFile Local file system path to the VM settings file (can
448 * be relative to the VirtualBox config directory).
449 * @param aId UUID of the machine or NULL (see above).
450 * @param strPassword Password for decrypting the config
451 *
452 * @return Success indicator. if not S_OK, the machine object is invalid
453 */
454HRESULT Machine::initFromSettings(VirtualBox *aParent,
455 const Utf8Str &strConfigFile,
456 const Guid *aId,
457 const com::Utf8Str &strPassword)
458{
459 LogFlowThisFuncEnter();
460 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
461
462#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
463 if (strPassword.isNotEmpty())
464 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
465#else
466 /** @todo */
467 RT_NOREF(strPassword);
468#endif
469
470 /* Enclose the state transition NotReady->InInit->Ready */
471 AutoInitSpan autoInitSpan(this);
472 AssertReturn(autoInitSpan.isOk(), E_FAIL);
473
474 HRESULT rc = initImpl(aParent, strConfigFile);
475 if (FAILED(rc)) return rc;
476
477 if (aId)
478 {
479 // loading a registered VM:
480 unconst(mData->mUuid) = *aId;
481 mData->mRegistered = TRUE;
482 // now load the settings from XML:
483 rc = i_registeredInit();
484 // this calls initDataAndChildObjects() and loadSettings()
485 }
486 else
487 {
488 // opening an unregistered VM (VirtualBox::OpenMachine()):
489 rc = initDataAndChildObjects();
490
491 if (SUCCEEDED(rc))
492 {
493 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
494 mData->mAccessible = TRUE;
495
496 try
497 {
498 // load and parse machine XML; this will throw on XML or logic errors
499 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
500
501 // reject VM UUID duplicates, they can happen if someone
502 // tries to register an already known VM config again
503 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
504 true /* fPermitInaccessible */,
505 false /* aDoSetError */,
506 NULL) != VBOX_E_OBJECT_NOT_FOUND)
507 {
508 throw setError(E_FAIL,
509 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
510 mData->m_strConfigFile.c_str());
511 }
512
513 // use UUID from machine config
514 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
515
516 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
517 NULL /* puuidRegistry */);
518 if (FAILED(rc)) throw rc;
519
520 /* At this point the changing of the current state modification
521 * flag is allowed. */
522 i_allowStateModification();
523
524 i_commit();
525 }
526 catch (HRESULT err)
527 {
528 /* we assume that error info is set by the thrower */
529 rc = err;
530 }
531 catch (...)
532 {
533 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
534 }
535 }
536 }
537
538 /* Confirm a successful initialization when it's the case */
539 if (SUCCEEDED(rc))
540 {
541 if (mData->mAccessible)
542 autoInitSpan.setSucceeded();
543 else
544 {
545 autoInitSpan.setLimited();
546
547 // uninit media from this machine's media registry, or else
548 // reloading the settings will fail
549 mParent->i_unregisterMachineMedia(i_getId());
550 }
551 }
552
553 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
554 "rc=%08X\n",
555 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
556 mData->mRegistered, mData->mAccessible, rc));
557
558 LogFlowThisFuncLeave();
559
560 return rc;
561}
562
563/**
564 * Initializes a new instance from a machine config that is already in memory
565 * (import OVF case). Since we are importing, the UUID in the machine
566 * config is ignored and we always generate a fresh one.
567 *
568 * @param aParent Associated parent object.
569 * @param strName Name for the new machine; this overrides what is specified in config.
570 * @param strSettingsFilename File name of .vbox file.
571 * @param config Machine configuration loaded and parsed from XML.
572 *
573 * @return Success indicator. if not S_OK, the machine object is invalid
574 */
575HRESULT Machine::init(VirtualBox *aParent,
576 const Utf8Str &strName,
577 const Utf8Str &strSettingsFilename,
578 const settings::MachineConfigFile &config)
579{
580 LogFlowThisFuncEnter();
581
582 /* Enclose the state transition NotReady->InInit->Ready */
583 AutoInitSpan autoInitSpan(this);
584 AssertReturn(autoInitSpan.isOk(), E_FAIL);
585
586 HRESULT rc = initImpl(aParent, strSettingsFilename);
587 if (FAILED(rc)) return rc;
588
589 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
590 if (FAILED(rc)) return rc;
591
592 rc = initDataAndChildObjects();
593
594 if (SUCCEEDED(rc))
595 {
596 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
597 mData->mAccessible = TRUE;
598
599 // create empty machine config for instance data
600 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
601
602 // generate fresh UUID, ignore machine config
603 unconst(mData->mUuid).create();
604
605 rc = i_loadMachineDataFromSettings(config,
606 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
607
608 // override VM name as well, it may be different
609 mUserData->s.strName = strName;
610
611 if (SUCCEEDED(rc))
612 {
613 /* At this point the changing of the current state modification
614 * flag is allowed. */
615 i_allowStateModification();
616
617 /* commit all changes made during the initialization */
618 i_commit();
619 }
620 }
621
622 /* Confirm a successful initialization when it's the case */
623 if (SUCCEEDED(rc))
624 {
625 if (mData->mAccessible)
626 autoInitSpan.setSucceeded();
627 else
628 {
629 /* Ignore all errors from unregistering, they would destroy
630- * the more interesting error information we already have,
631- * pinpointing the issue with the VM config. */
632 ErrorInfoKeeper eik;
633
634 autoInitSpan.setLimited();
635
636 // uninit media from this machine's media registry, or else
637 // reloading the settings will fail
638 mParent->i_unregisterMachineMedia(i_getId());
639 }
640 }
641
642 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
643 "rc=%08X\n",
644 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
645 mData->mRegistered, mData->mAccessible, rc));
646
647 LogFlowThisFuncLeave();
648
649 return rc;
650}
651
652/**
653 * Shared code between the various init() implementations.
654 * @param aParent The VirtualBox object.
655 * @param strConfigFile Settings file.
656 * @return
657 */
658HRESULT Machine::initImpl(VirtualBox *aParent,
659 const Utf8Str &strConfigFile)
660{
661 LogFlowThisFuncEnter();
662
663 AssertReturn(aParent, E_INVALIDARG);
664 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
665
666 HRESULT rc = S_OK;
667
668 /* share the parent weakly */
669 unconst(mParent) = aParent;
670
671 /* allocate the essential machine data structure (the rest will be
672 * allocated later by initDataAndChildObjects() */
673 mData.allocate();
674
675 /* memorize the config file name (as provided) */
676 mData->m_strConfigFile = strConfigFile;
677
678 /* get the full file name */
679 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
680 if (RT_FAILURE(vrc1))
681 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
682 tr("Invalid machine settings file name '%s' (%Rrc)"),
683 strConfigFile.c_str(),
684 vrc1);
685
686 LogFlowThisFuncLeave();
687
688 return rc;
689}
690
691/**
692 * Tries to create a machine settings file in the path stored in the machine
693 * instance data. Used when a new machine is created to fail gracefully if
694 * the settings file could not be written (e.g. because machine dir is read-only).
695 * @return
696 */
697HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
698{
699 HRESULT rc = S_OK;
700
701 // when we create a new machine, we must be able to create the settings file
702 RTFILE f = NIL_RTFILE;
703 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
704 if ( RT_SUCCESS(vrc)
705 || vrc == VERR_SHARING_VIOLATION
706 )
707 {
708 if (RT_SUCCESS(vrc))
709 RTFileClose(f);
710 if (!fForceOverwrite)
711 rc = setError(VBOX_E_FILE_ERROR,
712 tr("Machine settings file '%s' already exists"),
713 mData->m_strConfigFileFull.c_str());
714 else
715 {
716 /* try to delete the config file, as otherwise the creation
717 * of a new settings file will fail. */
718 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
719 if (RT_FAILURE(vrc2))
720 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
721 tr("Could not delete the existing settings file '%s' (%Rrc)"),
722 mData->m_strConfigFileFull.c_str(), vrc2);
723 }
724 }
725 else if ( vrc != VERR_FILE_NOT_FOUND
726 && vrc != VERR_PATH_NOT_FOUND
727 )
728 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
729 tr("Invalid machine settings file name '%s' (%Rrc)"),
730 mData->m_strConfigFileFull.c_str(),
731 vrc);
732 return rc;
733}
734
735/**
736 * Initializes the registered machine by loading the settings file.
737 * This method is separated from #init() in order to make it possible to
738 * retry the operation after VirtualBox startup instead of refusing to
739 * startup the whole VirtualBox server in case if the settings file of some
740 * registered VM is invalid or inaccessible.
741 *
742 * @note Must be always called from this object's write lock
743 * (unless called from #init() that doesn't need any locking).
744 * @note Locks the mUSBController method for writing.
745 * @note Subclasses must not call this method.
746 */
747HRESULT Machine::i_registeredInit()
748{
749 AssertReturn(!i_isSessionMachine(), E_FAIL);
750 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
751 AssertReturn(mData->mUuid.isValid(), E_FAIL);
752 AssertReturn(!mData->mAccessible, E_FAIL);
753
754 HRESULT rc = initDataAndChildObjects();
755
756 if (SUCCEEDED(rc))
757 {
758 /* Temporarily reset the registered flag in order to let setters
759 * potentially called from loadSettings() succeed (isMutable() used in
760 * all setters will return FALSE for a Machine instance if mRegistered
761 * is TRUE). */
762 mData->mRegistered = FALSE;
763
764 try
765 {
766 // load and parse machine XML; this will throw on XML or logic errors
767 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
768
769 if (mData->mUuid != mData->pMachineConfigFile->uuid)
770 throw setError(E_FAIL,
771 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
772 mData->pMachineConfigFile->uuid.raw(),
773 mData->m_strConfigFileFull.c_str(),
774 mData->mUuid.toString().c_str(),
775 mParent->i_settingsFilePath().c_str());
776
777 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
778 NULL /* const Guid *puuidRegistry */);
779 if (FAILED(rc)) throw rc;
780 }
781 catch (HRESULT err)
782 {
783 /* we assume that error info is set by the thrower */
784 rc = err;
785 }
786 catch (...)
787 {
788 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
789 }
790
791 /* Restore the registered flag (even on failure) */
792 mData->mRegistered = TRUE;
793 }
794
795 if (SUCCEEDED(rc))
796 {
797 /* Set mAccessible to TRUE only if we successfully locked and loaded
798 * the settings file */
799 mData->mAccessible = TRUE;
800
801 /* commit all changes made during loading the settings file */
802 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
803 /// @todo r=klaus for some reason the settings loading logic backs up
804 // the settings, and therefore a commit is needed. Should probably be changed.
805 }
806 else
807 {
808 /* If the machine is registered, then, instead of returning a
809 * failure, we mark it as inaccessible and set the result to
810 * success to give it a try later */
811
812 /* fetch the current error info */
813 mData->mAccessError = com::ErrorInfo();
814 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
815
816 /* rollback all changes */
817 i_rollback(false /* aNotify */);
818
819 // uninit media from this machine's media registry, or else
820 // reloading the settings will fail
821 mParent->i_unregisterMachineMedia(i_getId());
822
823 /* uninitialize the common part to make sure all data is reset to
824 * default (null) values */
825 uninitDataAndChildObjects();
826
827 rc = S_OK;
828 }
829
830 return rc;
831}
832
833/**
834 * Uninitializes the instance.
835 * Called either from FinalRelease() or by the parent when it gets destroyed.
836 *
837 * @note The caller of this method must make sure that this object
838 * a) doesn't have active callers on the current thread and b) is not locked
839 * by the current thread; otherwise uninit() will hang either a) due to
840 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
841 * a dead-lock caused by this thread waiting for all callers on the other
842 * threads are done but preventing them from doing so by holding a lock.
843 */
844void Machine::uninit()
845{
846 LogFlowThisFuncEnter();
847
848 Assert(!isWriteLockOnCurrentThread());
849
850 Assert(!uRegistryNeedsSaving);
851 if (uRegistryNeedsSaving)
852 {
853 AutoCaller autoCaller(this);
854 if (SUCCEEDED(autoCaller.rc()))
855 {
856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
857 i_saveSettings(NULL, alock, Machine::SaveS_Force);
858 }
859 }
860
861 /* Enclose the state transition Ready->InUninit->NotReady */
862 AutoUninitSpan autoUninitSpan(this);
863 if (autoUninitSpan.uninitDone())
864 return;
865
866 Assert(!i_isSnapshotMachine());
867 Assert(!i_isSessionMachine());
868 Assert(!!mData);
869
870 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
871 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
872
873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
874
875 if (!mData->mSession.mMachine.isNull())
876 {
877 /* Theoretically, this can only happen if the VirtualBox server has been
878 * terminated while there were clients running that owned open direct
879 * sessions. Since in this case we are definitely called by
880 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
881 * won't happen on the client watcher thread (because it has a
882 * VirtualBox caller for the duration of the
883 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
884 * cannot happen until the VirtualBox caller is released). This is
885 * important, because SessionMachine::uninit() cannot correctly operate
886 * after we return from this method (it expects the Machine instance is
887 * still valid). We'll call it ourselves below.
888 */
889 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
890 (SessionMachine*)mData->mSession.mMachine));
891
892 if (Global::IsOnlineOrTransient(mData->mMachineState))
893 {
894 Log1WarningThisFunc(("Setting state to Aborted!\n"));
895 /* set machine state using SessionMachine reimplementation */
896 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
897 }
898
899 /*
900 * Uninitialize SessionMachine using public uninit() to indicate
901 * an unexpected uninitialization.
902 */
903 mData->mSession.mMachine->uninit();
904 /* SessionMachine::uninit() must set mSession.mMachine to null */
905 Assert(mData->mSession.mMachine.isNull());
906 }
907
908 // uninit media from this machine's media registry, if they're still there
909 Guid uuidMachine(i_getId());
910
911 /* the lock is no more necessary (SessionMachine is uninitialized) */
912 alock.release();
913
914 /* XXX This will fail with
915 * "cannot be closed because it is still attached to 1 virtual machines"
916 * because at this point we did not call uninitDataAndChildObjects() yet
917 * and therefore also removeBackReference() for all these mediums was not called! */
918
919 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
920 mParent->i_unregisterMachineMedia(uuidMachine);
921
922 // has machine been modified?
923 if (mData->flModifications)
924 {
925 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
926 i_rollback(false /* aNotify */);
927 }
928
929 if (mData->mAccessible)
930 uninitDataAndChildObjects();
931
932 /* free the essential data structure last */
933 mData.free();
934
935 LogFlowThisFuncLeave();
936}
937
938// Wrapped IMachine properties
939/////////////////////////////////////////////////////////////////////////////
940HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
941{
942 /* mParent is constant during life time, no need to lock */
943 ComObjPtr<VirtualBox> pVirtualBox(mParent);
944 aParent = pVirtualBox;
945
946 return S_OK;
947}
948
949
950HRESULT Machine::getAccessible(BOOL *aAccessible)
951{
952 /* In some cases (medium registry related), it is necessary to be able to
953 * go through the list of all machines. Happens when an inaccessible VM
954 * has a sensible medium registry. */
955 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 HRESULT rc = S_OK;
959
960 if (!mData->mAccessible)
961 {
962 /* try to initialize the VM once more if not accessible */
963
964 AutoReinitSpan autoReinitSpan(this);
965 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
966
967#ifdef DEBUG
968 LogFlowThisFunc(("Dumping media backreferences\n"));
969 mParent->i_dumpAllBackRefs();
970#endif
971
972 if (mData->pMachineConfigFile)
973 {
974 // reset the XML file to force loadSettings() (called from i_registeredInit())
975 // to parse it again; the file might have changed
976 delete mData->pMachineConfigFile;
977 mData->pMachineConfigFile = NULL;
978 }
979
980 rc = i_registeredInit();
981
982 if (SUCCEEDED(rc) && mData->mAccessible)
983 {
984 autoReinitSpan.setSucceeded();
985
986 /* make sure interesting parties will notice the accessibility
987 * state change */
988 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
989 mParent->i_onMachineDataChanged(mData->mUuid);
990 }
991 }
992
993 if (SUCCEEDED(rc))
994 *aAccessible = mData->mAccessible;
995
996 LogFlowThisFuncLeave();
997
998 return rc;
999}
1000
1001HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1002{
1003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1004
1005 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1006 {
1007 /* return shortly */
1008 aAccessError = NULL;
1009 return S_OK;
1010 }
1011
1012 HRESULT rc = S_OK;
1013
1014 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1015 rc = errorInfo.createObject();
1016 if (SUCCEEDED(rc))
1017 {
1018 errorInfo->init(mData->mAccessError.getResultCode(),
1019 mData->mAccessError.getInterfaceID().ref(),
1020 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1021 Utf8Str(mData->mAccessError.getText()));
1022 aAccessError = errorInfo;
1023 }
1024
1025 return rc;
1026}
1027
1028HRESULT Machine::getName(com::Utf8Str &aName)
1029{
1030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1031
1032 aName = mUserData->s.strName;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::setName(const com::Utf8Str &aName)
1038{
1039 // prohibit setting a UUID only as the machine name, or else it can
1040 // never be found by findMachine()
1041 Guid test(aName);
1042
1043 if (test.isValid())
1044 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1045
1046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1047
1048 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1049 if (FAILED(rc)) return rc;
1050
1051 i_setModified(IsModified_MachineData);
1052 mUserData.backup();
1053 mUserData->s.strName = aName;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 aDescription = mUserData->s.strDescription;
1063
1064 return S_OK;
1065}
1066
1067HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1068{
1069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1070
1071 // this can be done in principle in any state as it doesn't affect the VM
1072 // significantly, but play safe by not messing around while complex
1073 // activities are going on
1074 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1075 if (FAILED(rc)) return rc;
1076
1077 i_setModified(IsModified_MachineData);
1078 mUserData.backup();
1079 mUserData->s.strDescription = aDescription;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::getId(com::Guid &aId)
1085{
1086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1087
1088 aId = mData->mUuid;
1089
1090 return S_OK;
1091}
1092
1093HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1094{
1095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1096 aGroups.resize(mUserData->s.llGroups.size());
1097 size_t i = 0;
1098 for (StringsList::const_iterator
1099 it = mUserData->s.llGroups.begin();
1100 it != mUserData->s.llGroups.end();
1101 ++it, ++i)
1102 aGroups[i] = (*it);
1103
1104 return S_OK;
1105}
1106
1107HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1108{
1109 StringsList llGroups;
1110 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1111 if (FAILED(rc))
1112 return rc;
1113
1114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 rc = i_checkStateDependency(MutableOrSavedStateDep);
1117 if (FAILED(rc)) return rc;
1118
1119 i_setModified(IsModified_MachineData);
1120 mUserData.backup();
1121 mUserData->s.llGroups = llGroups;
1122
1123 return S_OK;
1124}
1125
1126HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1127{
1128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1129
1130 aOSTypeId = mUserData->s.strOsType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1136{
1137 /* look up the object by Id to check it is valid */
1138 ComObjPtr<GuestOSType> pGuestOSType;
1139 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1140
1141 /* when setting, always use the "etalon" value for consistency -- lookup
1142 * by ID is case-insensitive and the input value may have different case */
1143 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1144
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mUserData.backup();
1152 mUserData->s.strOsType = osTypeId;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aFirmwareType = mHWData->mFirmwareType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mFirmwareType = aFirmwareType;
1176 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1177 alock.release();
1178
1179 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1185{
1186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1189
1190 return S_OK;
1191}
1192
1193HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1194{
1195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 HRESULT rc = i_checkStateDependency(MutableStateDep);
1198 if (FAILED(rc)) return rc;
1199
1200 i_setModified(IsModified_MachineData);
1201 mHWData.backup();
1202 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1203
1204 return S_OK;
1205}
1206
1207HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1208{
1209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 *aPointingHIDType = mHWData->mPointingHIDType;
1212
1213 return S_OK;
1214}
1215
1216HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1217{
1218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 HRESULT rc = i_checkStateDependency(MutableStateDep);
1221 if (FAILED(rc)) return rc;
1222
1223 i_setModified(IsModified_MachineData);
1224 mHWData.backup();
1225 mHWData->mPointingHIDType = aPointingHIDType;
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1231{
1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 *aChipsetType = mHWData->mChipsetType;
1235
1236 return S_OK;
1237}
1238
1239HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1240{
1241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 HRESULT rc = i_checkStateDependency(MutableStateDep);
1244 if (FAILED(rc)) return rc;
1245
1246 if (aChipsetType != mHWData->mChipsetType)
1247 {
1248 i_setModified(IsModified_MachineData);
1249 mHWData.backup();
1250 mHWData->mChipsetType = aChipsetType;
1251
1252 // Resize network adapter array, to be finalized on commit/rollback.
1253 // We must not throw away entries yet, otherwise settings are lost
1254 // without a way to roll back.
1255 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1256 size_t oldCount = mNetworkAdapters.size();
1257 if (newCount > oldCount)
1258 {
1259 mNetworkAdapters.resize(newCount);
1260 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1261 {
1262 unconst(mNetworkAdapters[slot]).createObject();
1263 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1264 }
1265 }
1266 }
1267
1268 return S_OK;
1269}
1270
1271HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1272{
1273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1274
1275 *aIommuType = mHWData->mIommuType;
1276
1277 return S_OK;
1278}
1279
1280HRESULT Machine::setIommuType(IommuType_T aIommuType)
1281{
1282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1283
1284 HRESULT rc = i_checkStateDependency(MutableStateDep);
1285 if (FAILED(rc)) return rc;
1286
1287 if (aIommuType != mHWData->mIommuType)
1288 {
1289 if (aIommuType == IommuType_Intel)
1290 {
1291#ifndef VBOX_WITH_IOMMU_INTEL
1292 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1293 return E_UNEXPECTED;
1294#endif
1295 }
1296
1297 i_setModified(IsModified_MachineData);
1298 mHWData.backup();
1299 mHWData->mIommuType = aIommuType;
1300 }
1301
1302 return S_OK;
1303}
1304
1305HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1306{
1307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 aParavirtDebug = mHWData->mParavirtDebug;
1310 return S_OK;
1311}
1312
1313HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1314{
1315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 HRESULT rc = i_checkStateDependency(MutableStateDep);
1318 if (FAILED(rc)) return rc;
1319
1320 /** @todo Parse/validate options? */
1321 if (aParavirtDebug != mHWData->mParavirtDebug)
1322 {
1323 i_setModified(IsModified_MachineData);
1324 mHWData.backup();
1325 mHWData->mParavirtDebug = aParavirtDebug;
1326 }
1327
1328 return S_OK;
1329}
1330
1331HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1332{
1333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 *aParavirtProvider = mHWData->mParavirtProvider;
1336
1337 return S_OK;
1338}
1339
1340HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1341{
1342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1343
1344 HRESULT rc = i_checkStateDependency(MutableStateDep);
1345 if (FAILED(rc)) return rc;
1346
1347 if (aParavirtProvider != mHWData->mParavirtProvider)
1348 {
1349 i_setModified(IsModified_MachineData);
1350 mHWData.backup();
1351 mHWData->mParavirtProvider = aParavirtProvider;
1352 }
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1358{
1359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1360
1361 *aParavirtProvider = mHWData->mParavirtProvider;
1362 switch (mHWData->mParavirtProvider)
1363 {
1364 case ParavirtProvider_None:
1365 case ParavirtProvider_HyperV:
1366 case ParavirtProvider_KVM:
1367 case ParavirtProvider_Minimal:
1368 break;
1369
1370 /* Resolve dynamic provider types to the effective types. */
1371 default:
1372 {
1373 ComObjPtr<GuestOSType> pGuestOSType;
1374 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1375 pGuestOSType);
1376 if (FAILED(hrc2) || pGuestOSType.isNull())
1377 {
1378 *aParavirtProvider = ParavirtProvider_None;
1379 break;
1380 }
1381
1382 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1383 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1384
1385 switch (mHWData->mParavirtProvider)
1386 {
1387 case ParavirtProvider_Legacy:
1388 {
1389 if (fOsXGuest)
1390 *aParavirtProvider = ParavirtProvider_Minimal;
1391 else
1392 *aParavirtProvider = ParavirtProvider_None;
1393 break;
1394 }
1395
1396 case ParavirtProvider_Default:
1397 {
1398 if (fOsXGuest)
1399 *aParavirtProvider = ParavirtProvider_Minimal;
1400 else if ( mUserData->s.strOsType == "Windows11_64"
1401 || mUserData->s.strOsType == "Windows10"
1402 || mUserData->s.strOsType == "Windows10_64"
1403 || mUserData->s.strOsType == "Windows81"
1404 || mUserData->s.strOsType == "Windows81_64"
1405 || mUserData->s.strOsType == "Windows8"
1406 || mUserData->s.strOsType == "Windows8_64"
1407 || mUserData->s.strOsType == "Windows7"
1408 || mUserData->s.strOsType == "Windows7_64"
1409 || mUserData->s.strOsType == "WindowsVista"
1410 || mUserData->s.strOsType == "WindowsVista_64"
1411 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1412 || mUserData->s.strOsType.startsWith("Windows201"))
1413 && mUserData->s.strOsType.endsWith("_64"))
1414 || mUserData->s.strOsType == "Windows2012"
1415 || mUserData->s.strOsType == "Windows2012_64"
1416 || mUserData->s.strOsType == "Windows2008"
1417 || mUserData->s.strOsType == "Windows2008_64")
1418 {
1419 *aParavirtProvider = ParavirtProvider_HyperV;
1420 }
1421 else if (guestTypeFamilyId == "Linux" &&
1422 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1423 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1424 mUserData->s.strOsType != "Linux24_64")
1425 {
1426 *aParavirtProvider = ParavirtProvider_KVM;
1427 }
1428 else
1429 *aParavirtProvider = ParavirtProvider_None;
1430 break;
1431 }
1432
1433 default: AssertFailedBreak(); /* Shut up MSC. */
1434 }
1435 break;
1436 }
1437 }
1438
1439 Assert( *aParavirtProvider == ParavirtProvider_None
1440 || *aParavirtProvider == ParavirtProvider_Minimal
1441 || *aParavirtProvider == ParavirtProvider_HyperV
1442 || *aParavirtProvider == ParavirtProvider_KVM);
1443 return S_OK;
1444}
1445
1446HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1447{
1448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1449
1450 aHardwareVersion = mHWData->mHWVersion;
1451
1452 return S_OK;
1453}
1454
1455HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1456{
1457 /* check known version */
1458 Utf8Str hwVersion = aHardwareVersion;
1459 if ( hwVersion.compare("1") != 0
1460 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1461 return setError(E_INVALIDARG,
1462 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1463
1464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1465
1466 HRESULT rc = i_checkStateDependency(MutableStateDep);
1467 if (FAILED(rc)) return rc;
1468
1469 i_setModified(IsModified_MachineData);
1470 mHWData.backup();
1471 mHWData->mHWVersion = aHardwareVersion;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1477{
1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 if (!mHWData->mHardwareUUID.isZero())
1481 aHardwareUUID = mHWData->mHardwareUUID;
1482 else
1483 aHardwareUUID = mData->mUuid;
1484
1485 return S_OK;
1486}
1487
1488HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1489{
1490 if (!aHardwareUUID.isValid())
1491 return E_INVALIDARG;
1492
1493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
1495 HRESULT rc = i_checkStateDependency(MutableStateDep);
1496 if (FAILED(rc)) return rc;
1497
1498 i_setModified(IsModified_MachineData);
1499 mHWData.backup();
1500 if (aHardwareUUID == mData->mUuid)
1501 mHWData->mHardwareUUID.clear();
1502 else
1503 mHWData->mHardwareUUID = aHardwareUUID;
1504
1505 return S_OK;
1506}
1507
1508HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1509{
1510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1511
1512 *aMemorySize = mHWData->mMemorySize;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::setMemorySize(ULONG aMemorySize)
1518{
1519 /* check RAM limits */
1520 if ( aMemorySize < MM_RAM_MIN_IN_MB
1521 || aMemorySize > MM_RAM_MAX_IN_MB
1522 )
1523 return setError(E_INVALIDARG,
1524 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1525 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1526
1527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 HRESULT rc = i_checkStateDependency(MutableStateDep);
1530 if (FAILED(rc)) return rc;
1531
1532 i_setModified(IsModified_MachineData);
1533 mHWData.backup();
1534 mHWData->mMemorySize = aMemorySize;
1535
1536 return S_OK;
1537}
1538
1539HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1540{
1541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 *aCPUCount = mHWData->mCPUCount;
1544
1545 return S_OK;
1546}
1547
1548HRESULT Machine::setCPUCount(ULONG aCPUCount)
1549{
1550 /* check CPU limits */
1551 if ( aCPUCount < SchemaDefs::MinCPUCount
1552 || aCPUCount > SchemaDefs::MaxCPUCount
1553 )
1554 return setError(E_INVALIDARG,
1555 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1556 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1557
1558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1561 if (mHWData->mCPUHotPlugEnabled)
1562 {
1563 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1564 {
1565 if (mHWData->mCPUAttached[idx])
1566 return setError(E_INVALIDARG,
1567 tr("There is still a CPU attached to socket %lu."
1568 "Detach the CPU before removing the socket"),
1569 aCPUCount, idx+1);
1570 }
1571 }
1572
1573 HRESULT rc = i_checkStateDependency(MutableStateDep);
1574 if (FAILED(rc)) return rc;
1575
1576 i_setModified(IsModified_MachineData);
1577 mHWData.backup();
1578 mHWData->mCPUCount = aCPUCount;
1579
1580 return S_OK;
1581}
1582
1583HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1584{
1585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1586
1587 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1588
1589 return S_OK;
1590}
1591
1592HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1593{
1594 HRESULT rc = S_OK;
1595
1596 /* check throttle limits */
1597 if ( aCPUExecutionCap < 1
1598 || aCPUExecutionCap > 100
1599 )
1600 return setError(E_INVALIDARG,
1601 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1602 aCPUExecutionCap, 1, 100);
1603
1604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1605
1606 rc = i_checkStateDependency(MutableOrRunningStateDep);
1607 if (FAILED(rc)) return rc;
1608
1609 alock.release();
1610 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1611 alock.acquire();
1612 if (FAILED(rc)) return rc;
1613
1614 i_setModified(IsModified_MachineData);
1615 mHWData.backup();
1616 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1617
1618 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1619 if (Global::IsOnline(mData->mMachineState))
1620 i_saveSettings(NULL, alock);
1621
1622 return S_OK;
1623}
1624
1625HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1626{
1627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1628
1629 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1630
1631 return S_OK;
1632}
1633
1634HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1635{
1636 HRESULT rc = S_OK;
1637
1638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 rc = i_checkStateDependency(MutableStateDep);
1641 if (FAILED(rc)) return rc;
1642
1643 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1644 {
1645 if (aCPUHotPlugEnabled)
1646 {
1647 i_setModified(IsModified_MachineData);
1648 mHWData.backup();
1649
1650 /* Add the amount of CPUs currently attached */
1651 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1652 mHWData->mCPUAttached[i] = true;
1653 }
1654 else
1655 {
1656 /*
1657 * We can disable hotplug only if the amount of maximum CPUs is equal
1658 * to the amount of attached CPUs
1659 */
1660 unsigned cCpusAttached = 0;
1661 unsigned iHighestId = 0;
1662
1663 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1664 {
1665 if (mHWData->mCPUAttached[i])
1666 {
1667 cCpusAttached++;
1668 iHighestId = i;
1669 }
1670 }
1671
1672 if ( (cCpusAttached != mHWData->mCPUCount)
1673 || (iHighestId >= mHWData->mCPUCount))
1674 return setError(E_INVALIDARG,
1675 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1676
1677 i_setModified(IsModified_MachineData);
1678 mHWData.backup();
1679 }
1680 }
1681
1682 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1683
1684 return rc;
1685}
1686
1687HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1688{
1689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1690
1691 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1692
1693 return S_OK;
1694}
1695
1696HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1697{
1698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1701 if (SUCCEEDED(hrc))
1702 {
1703 i_setModified(IsModified_MachineData);
1704 mHWData.backup();
1705 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1706 }
1707 return hrc;
1708}
1709
1710HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1711{
1712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1713 aCPUProfile = mHWData->mCpuProfile;
1714 return S_OK;
1715}
1716
1717HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1718{
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1721 if (SUCCEEDED(hrc))
1722 {
1723 i_setModified(IsModified_MachineData);
1724 mHWData.backup();
1725 /* Empty equals 'host'. */
1726 if (aCPUProfile.isNotEmpty())
1727 mHWData->mCpuProfile = aCPUProfile;
1728 else
1729 mHWData->mCpuProfile = "host";
1730 }
1731 return hrc;
1732}
1733
1734HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1735{
1736#ifdef VBOX_WITH_USB_CARDREADER
1737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1740
1741 return S_OK;
1742#else
1743 NOREF(aEmulatedUSBCardReaderEnabled);
1744 return E_NOTIMPL;
1745#endif
1746}
1747
1748HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1749{
1750#ifdef VBOX_WITH_USB_CARDREADER
1751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1752
1753 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1754 if (FAILED(rc)) return rc;
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1759
1760 return S_OK;
1761#else
1762 NOREF(aEmulatedUSBCardReaderEnabled);
1763 return E_NOTIMPL;
1764#endif
1765}
1766
1767HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1768{
1769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1770
1771 *aHPETEnabled = mHWData->mHPETEnabled;
1772
1773 return S_OK;
1774}
1775
1776HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1777{
1778 HRESULT rc = S_OK;
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 rc = i_checkStateDependency(MutableStateDep);
1783 if (FAILED(rc)) return rc;
1784
1785 i_setModified(IsModified_MachineData);
1786 mHWData.backup();
1787
1788 mHWData->mHPETEnabled = aHPETEnabled;
1789
1790 return rc;
1791}
1792
1793/** @todo this method should not be public */
1794HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1795{
1796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1797
1798 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1799
1800 return S_OK;
1801}
1802
1803/**
1804 * Set the memory balloon size.
1805 *
1806 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1807 * we have to make sure that we never call IGuest from here.
1808 */
1809HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1810{
1811 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1812#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1813 /* check limits */
1814 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1815 return setError(E_INVALIDARG,
1816 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1817 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1818
1819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1820
1821 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
1822 if (FAILED(rc)) return rc;
1823
1824 i_setModified(IsModified_MachineData);
1825 mHWData.backup();
1826 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1827
1828 return S_OK;
1829#else
1830 NOREF(aMemoryBalloonSize);
1831 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1832#endif
1833}
1834
1835HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1836{
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838
1839 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1840 return S_OK;
1841}
1842
1843HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1844{
1845#ifdef VBOX_WITH_PAGE_SHARING
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 HRESULT rc = i_checkStateDependency(MutableStateDep);
1849 if (FAILED(rc)) return rc;
1850
1851 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1855 return S_OK;
1856#else
1857 NOREF(aPageFusionEnabled);
1858 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1859#endif
1860}
1861
1862HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1863{
1864 /* mBIOSSettings is constant during life time, no need to lock */
1865 aBIOSSettings = mBIOSSettings;
1866
1867 return S_OK;
1868}
1869
1870HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1871{
1872 /* mTrustedPlatformModule is constant during life time, no need to lock */
1873 aTrustedPlatformModule = mTrustedPlatformModule;
1874
1875 return S_OK;
1876}
1877
1878HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1879{
1880 /* mNvramStore is constant during life time, no need to lock */
1881 aNvramStore = mNvramStore;
1882
1883 return S_OK;
1884}
1885
1886HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1887{
1888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 aRecordingSettings = mRecordingSettings;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 aGraphicsAdapter = mGraphicsAdapter;
1900
1901 return S_OK;
1902}
1903
1904HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1905{
1906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1907
1908 switch (aProperty)
1909 {
1910 case CPUPropertyType_PAE:
1911 *aValue = mHWData->mPAEEnabled;
1912 break;
1913
1914 case CPUPropertyType_LongMode:
1915 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1916 *aValue = TRUE;
1917 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1918 *aValue = FALSE;
1919#if HC_ARCH_BITS == 64
1920 else
1921 *aValue = TRUE;
1922#else
1923 else
1924 {
1925 *aValue = FALSE;
1926
1927 ComObjPtr<GuestOSType> pGuestOSType;
1928 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1929 pGuestOSType);
1930 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1931 {
1932 if (pGuestOSType->i_is64Bit())
1933 {
1934 ComObjPtr<Host> pHost = mParent->i_host();
1935 alock.release();
1936
1937 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1938 if (FAILED(hrc2))
1939 *aValue = FALSE;
1940 }
1941 }
1942 }
1943#endif
1944 break;
1945
1946 case CPUPropertyType_TripleFaultReset:
1947 *aValue = mHWData->mTripleFaultReset;
1948 break;
1949
1950 case CPUPropertyType_APIC:
1951 *aValue = mHWData->mAPIC;
1952 break;
1953
1954 case CPUPropertyType_X2APIC:
1955 *aValue = mHWData->mX2APIC;
1956 break;
1957
1958 case CPUPropertyType_IBPBOnVMExit:
1959 *aValue = mHWData->mIBPBOnVMExit;
1960 break;
1961
1962 case CPUPropertyType_IBPBOnVMEntry:
1963 *aValue = mHWData->mIBPBOnVMEntry;
1964 break;
1965
1966 case CPUPropertyType_SpecCtrl:
1967 *aValue = mHWData->mSpecCtrl;
1968 break;
1969
1970 case CPUPropertyType_SpecCtrlByHost:
1971 *aValue = mHWData->mSpecCtrlByHost;
1972 break;
1973
1974 case CPUPropertyType_HWVirt:
1975 *aValue = mHWData->mNestedHWVirt;
1976 break;
1977
1978 case CPUPropertyType_L1DFlushOnEMTScheduling:
1979 *aValue = mHWData->mL1DFlushOnSched;
1980 break;
1981
1982 case CPUPropertyType_L1DFlushOnVMEntry:
1983 *aValue = mHWData->mL1DFlushOnVMEntry;
1984 break;
1985
1986 case CPUPropertyType_MDSClearOnEMTScheduling:
1987 *aValue = mHWData->mMDSClearOnSched;
1988 break;
1989
1990 case CPUPropertyType_MDSClearOnVMEntry:
1991 *aValue = mHWData->mMDSClearOnVMEntry;
1992 break;
1993
1994 default:
1995 return E_INVALIDARG;
1996 }
1997 return S_OK;
1998}
1999
2000HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2001{
2002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 HRESULT rc = i_checkStateDependency(MutableStateDep);
2005 if (FAILED(rc)) return rc;
2006
2007 switch (aProperty)
2008 {
2009 case CPUPropertyType_PAE:
2010 i_setModified(IsModified_MachineData);
2011 mHWData.backup();
2012 mHWData->mPAEEnabled = !!aValue;
2013 break;
2014
2015 case CPUPropertyType_LongMode:
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2019 break;
2020
2021 case CPUPropertyType_TripleFaultReset:
2022 i_setModified(IsModified_MachineData);
2023 mHWData.backup();
2024 mHWData->mTripleFaultReset = !!aValue;
2025 break;
2026
2027 case CPUPropertyType_APIC:
2028 if (mHWData->mX2APIC)
2029 aValue = TRUE;
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mAPIC = !!aValue;
2033 break;
2034
2035 case CPUPropertyType_X2APIC:
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mX2APIC = !!aValue;
2039 if (aValue)
2040 mHWData->mAPIC = !!aValue;
2041 break;
2042
2043 case CPUPropertyType_IBPBOnVMExit:
2044 i_setModified(IsModified_MachineData);
2045 mHWData.backup();
2046 mHWData->mIBPBOnVMExit = !!aValue;
2047 break;
2048
2049 case CPUPropertyType_IBPBOnVMEntry:
2050 i_setModified(IsModified_MachineData);
2051 mHWData.backup();
2052 mHWData->mIBPBOnVMEntry = !!aValue;
2053 break;
2054
2055 case CPUPropertyType_SpecCtrl:
2056 i_setModified(IsModified_MachineData);
2057 mHWData.backup();
2058 mHWData->mSpecCtrl = !!aValue;
2059 break;
2060
2061 case CPUPropertyType_SpecCtrlByHost:
2062 i_setModified(IsModified_MachineData);
2063 mHWData.backup();
2064 mHWData->mSpecCtrlByHost = !!aValue;
2065 break;
2066
2067 case CPUPropertyType_HWVirt:
2068 i_setModified(IsModified_MachineData);
2069 mHWData.backup();
2070 mHWData->mNestedHWVirt = !!aValue;
2071 break;
2072
2073 case CPUPropertyType_L1DFlushOnEMTScheduling:
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mL1DFlushOnSched = !!aValue;
2077 break;
2078
2079 case CPUPropertyType_L1DFlushOnVMEntry:
2080 i_setModified(IsModified_MachineData);
2081 mHWData.backup();
2082 mHWData->mL1DFlushOnVMEntry = !!aValue;
2083 break;
2084
2085 case CPUPropertyType_MDSClearOnEMTScheduling:
2086 i_setModified(IsModified_MachineData);
2087 mHWData.backup();
2088 mHWData->mMDSClearOnSched = !!aValue;
2089 break;
2090
2091 case CPUPropertyType_MDSClearOnVMEntry:
2092 i_setModified(IsModified_MachineData);
2093 mHWData.backup();
2094 mHWData->mMDSClearOnVMEntry = !!aValue;
2095 break;
2096
2097 default:
2098 return E_INVALIDARG;
2099 }
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2104 ULONG *aValEcx, ULONG *aValEdx)
2105{
2106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2107 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2108 {
2109 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2110 it != mHWData->mCpuIdLeafList.end();
2111 ++it)
2112 {
2113 if (aOrdinal == 0)
2114 {
2115 const settings::CpuIdLeaf &rLeaf= *it;
2116 *aIdx = rLeaf.idx;
2117 *aSubIdx = rLeaf.idxSub;
2118 *aValEax = rLeaf.uEax;
2119 *aValEbx = rLeaf.uEbx;
2120 *aValEcx = rLeaf.uEcx;
2121 *aValEdx = rLeaf.uEdx;
2122 return S_OK;
2123 }
2124 aOrdinal--;
2125 }
2126 }
2127 return E_INVALIDARG;
2128}
2129
2130HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2131{
2132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 /*
2135 * Search the list.
2136 */
2137 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2138 {
2139 const settings::CpuIdLeaf &rLeaf= *it;
2140 if ( rLeaf.idx == aIdx
2141 && ( aSubIdx == UINT32_MAX
2142 || rLeaf.idxSub == aSubIdx) )
2143 {
2144 *aValEax = rLeaf.uEax;
2145 *aValEbx = rLeaf.uEbx;
2146 *aValEcx = rLeaf.uEcx;
2147 *aValEdx = rLeaf.uEdx;
2148 return S_OK;
2149 }
2150 }
2151
2152 return E_INVALIDARG;
2153}
2154
2155
2156HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2157{
2158 /*
2159 * Validate input before taking locks and checking state.
2160 */
2161 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2162 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2163 if ( aIdx >= UINT32_C(0x20)
2164 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2165 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2166 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2167
2168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2169 HRESULT rc = i_checkStateDependency(MutableStateDep);
2170 if (FAILED(rc)) return rc;
2171
2172 /*
2173 * Impose a maximum number of leaves.
2174 */
2175 if (mHWData->mCpuIdLeafList.size() > 256)
2176 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2177
2178 /*
2179 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2180 */
2181 i_setModified(IsModified_MachineData);
2182 mHWData.backup();
2183
2184 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2185 {
2186 settings::CpuIdLeaf &rLeaf= *it;
2187 if ( rLeaf.idx == aIdx
2188 && ( aSubIdx == UINT32_MAX
2189 || rLeaf.idxSub == aSubIdx) )
2190 it = mHWData->mCpuIdLeafList.erase(it);
2191 else
2192 ++it;
2193 }
2194
2195 settings::CpuIdLeaf NewLeaf;
2196 NewLeaf.idx = aIdx;
2197 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2198 NewLeaf.uEax = aValEax;
2199 NewLeaf.uEbx = aValEbx;
2200 NewLeaf.uEcx = aValEcx;
2201 NewLeaf.uEdx = aValEdx;
2202 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2203 return S_OK;
2204}
2205
2206HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2207{
2208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 HRESULT rc = i_checkStateDependency(MutableStateDep);
2211 if (FAILED(rc)) return rc;
2212
2213 /*
2214 * Do the removal.
2215 */
2216 bool fModified = mHWData.isBackedUp();
2217 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2218 {
2219 settings::CpuIdLeaf &rLeaf= *it;
2220 if ( rLeaf.idx == aIdx
2221 && ( aSubIdx == UINT32_MAX
2222 || rLeaf.idxSub == aSubIdx) )
2223 {
2224 if (!fModified)
2225 {
2226 fModified = true;
2227 i_setModified(IsModified_MachineData);
2228 mHWData.backup();
2229 // Start from the beginning, since mHWData.backup() creates
2230 // a new list, causing iterator mixup. This makes sure that
2231 // the settings are not unnecessarily marked as modified,
2232 // at the price of extra list walking.
2233 it = mHWData->mCpuIdLeafList.begin();
2234 }
2235 else
2236 it = mHWData->mCpuIdLeafList.erase(it);
2237 }
2238 else
2239 ++it;
2240 }
2241
2242 return S_OK;
2243}
2244
2245HRESULT Machine::removeAllCPUIDLeaves()
2246{
2247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2248
2249 HRESULT rc = i_checkStateDependency(MutableStateDep);
2250 if (FAILED(rc)) return rc;
2251
2252 if (mHWData->mCpuIdLeafList.size() > 0)
2253 {
2254 i_setModified(IsModified_MachineData);
2255 mHWData.backup();
2256
2257 mHWData->mCpuIdLeafList.clear();
2258 }
2259
2260 return S_OK;
2261}
2262HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2263{
2264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2265
2266 switch(aProperty)
2267 {
2268 case HWVirtExPropertyType_Enabled:
2269 *aValue = mHWData->mHWVirtExEnabled;
2270 break;
2271
2272 case HWVirtExPropertyType_VPID:
2273 *aValue = mHWData->mHWVirtExVPIDEnabled;
2274 break;
2275
2276 case HWVirtExPropertyType_NestedPaging:
2277 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2278 break;
2279
2280 case HWVirtExPropertyType_UnrestrictedExecution:
2281 *aValue = mHWData->mHWVirtExUXEnabled;
2282 break;
2283
2284 case HWVirtExPropertyType_LargePages:
2285 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2286 break;
2287
2288 case HWVirtExPropertyType_Force:
2289 *aValue = mHWData->mHWVirtExForceEnabled;
2290 break;
2291
2292 case HWVirtExPropertyType_UseNativeApi:
2293 *aValue = mHWData->mHWVirtExUseNativeApi;
2294 break;
2295
2296 case HWVirtExPropertyType_VirtVmsaveVmload:
2297 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2298 break;
2299
2300 default:
2301 return E_INVALIDARG;
2302 }
2303 return S_OK;
2304}
2305
2306HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2307{
2308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2309
2310 HRESULT rc = i_checkStateDependency(MutableStateDep);
2311 if (FAILED(rc)) return rc;
2312
2313 switch (aProperty)
2314 {
2315 case HWVirtExPropertyType_Enabled:
2316 i_setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mHWVirtExEnabled = !!aValue;
2319 break;
2320
2321 case HWVirtExPropertyType_VPID:
2322 i_setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2325 break;
2326
2327 case HWVirtExPropertyType_NestedPaging:
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2331 break;
2332
2333 case HWVirtExPropertyType_UnrestrictedExecution:
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mHWVirtExUXEnabled = !!aValue;
2337 break;
2338
2339 case HWVirtExPropertyType_LargePages:
2340 i_setModified(IsModified_MachineData);
2341 mHWData.backup();
2342 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2343 break;
2344
2345 case HWVirtExPropertyType_Force:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mHWVirtExForceEnabled = !!aValue;
2349 break;
2350
2351 case HWVirtExPropertyType_UseNativeApi:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mHWVirtExUseNativeApi = !!aValue;
2355 break;
2356
2357 case HWVirtExPropertyType_VirtVmsaveVmload:
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2361 break;
2362
2363 default:
2364 return E_INVALIDARG;
2365 }
2366
2367 return S_OK;
2368}
2369
2370HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2371{
2372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2375
2376 return S_OK;
2377}
2378
2379HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2380{
2381 /** @todo (r=dmik):
2382 * 1. Allow to change the name of the snapshot folder containing snapshots
2383 * 2. Rename the folder on disk instead of just changing the property
2384 * value (to be smart and not to leave garbage). Note that it cannot be
2385 * done here because the change may be rolled back. Thus, the right
2386 * place is #saveSettings().
2387 */
2388
2389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2390
2391 HRESULT rc = i_checkStateDependency(MutableStateDep);
2392 if (FAILED(rc)) return rc;
2393
2394 if (!mData->mCurrentSnapshot.isNull())
2395 return setError(E_FAIL,
2396 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2397
2398 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2399
2400 if (strSnapshotFolder.isEmpty())
2401 strSnapshotFolder = "Snapshots";
2402 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2403 if (RT_FAILURE(vrc))
2404 return setErrorBoth(E_FAIL, vrc,
2405 tr("Invalid snapshot folder '%s' (%Rrc)"),
2406 strSnapshotFolder.c_str(), vrc);
2407
2408 i_setModified(IsModified_MachineData);
2409 mUserData.backup();
2410
2411 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2412
2413 return S_OK;
2414}
2415
2416HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2417{
2418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
2420 aMediumAttachments.resize(mMediumAttachments->size());
2421 size_t i = 0;
2422 for (MediumAttachmentList::const_iterator
2423 it = mMediumAttachments->begin();
2424 it != mMediumAttachments->end();
2425 ++it, ++i)
2426 aMediumAttachments[i] = *it;
2427
2428 return S_OK;
2429}
2430
2431HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2432{
2433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 Assert(!!mVRDEServer);
2436
2437 aVRDEServer = mVRDEServer;
2438
2439 return S_OK;
2440}
2441
2442HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2443{
2444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2445
2446 aAudioAdapter = mAudioAdapter;
2447
2448 return S_OK;
2449}
2450
2451HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2452{
2453#ifdef VBOX_WITH_VUSB
2454 clearError();
2455 MultiResult rc(S_OK);
2456
2457# ifdef VBOX_WITH_USB
2458 rc = mParent->i_host()->i_checkUSBProxyService();
2459 if (FAILED(rc)) return rc;
2460# endif
2461
2462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2463
2464 aUSBControllers.resize(mUSBControllers->size());
2465 size_t i = 0;
2466 for (USBControllerList::const_iterator
2467 it = mUSBControllers->begin();
2468 it != mUSBControllers->end();
2469 ++it, ++i)
2470 aUSBControllers[i] = *it;
2471
2472 return S_OK;
2473#else
2474 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2475 * extended error info to indicate that USB is simply not available
2476 * (w/o treating it as a failure), for example, as in OSE */
2477 NOREF(aUSBControllers);
2478 ReturnComNotImplemented();
2479#endif /* VBOX_WITH_VUSB */
2480}
2481
2482HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2483{
2484#ifdef VBOX_WITH_VUSB
2485 clearError();
2486 MultiResult rc(S_OK);
2487
2488# ifdef VBOX_WITH_USB
2489 rc = mParent->i_host()->i_checkUSBProxyService();
2490 if (FAILED(rc)) return rc;
2491# endif
2492
2493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2494
2495 aUSBDeviceFilters = mUSBDeviceFilters;
2496 return rc;
2497#else
2498 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2499 * extended error info to indicate that USB is simply not available
2500 * (w/o treating it as a failure), for example, as in OSE */
2501 NOREF(aUSBDeviceFilters);
2502 ReturnComNotImplemented();
2503#endif /* VBOX_WITH_VUSB */
2504}
2505
2506HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2507{
2508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 aSettingsFilePath = mData->m_strConfigFileFull;
2511
2512 return S_OK;
2513}
2514
2515HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2516{
2517 RT_NOREF(aSettingsFilePath);
2518 ReturnComNotImplemented();
2519}
2520
2521HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2526 if (FAILED(rc)) return rc;
2527
2528 if (!mData->pMachineConfigFile->fileExists())
2529 // this is a new machine, and no config file exists yet:
2530 *aSettingsModified = TRUE;
2531 else
2532 *aSettingsModified = (mData->flModifications != 0);
2533
2534 return S_OK;
2535}
2536
2537HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2538{
2539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2540
2541 *aSessionState = mData->mSession.mState;
2542
2543 return S_OK;
2544}
2545
2546HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2547{
2548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 aSessionName = mData->mSession.mName;
2551
2552 return S_OK;
2553}
2554
2555HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2556{
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 *aSessionPID = mData->mSession.mPID;
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getState(MachineState_T *aState)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 *aState = mData->mMachineState;
2569 Assert(mData->mMachineState != MachineState_Null);
2570
2571 return S_OK;
2572}
2573
2574HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2579
2580 return S_OK;
2581}
2582
2583HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2584{
2585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2586
2587 aStateFilePath = mSSData->strStateFilePath;
2588
2589 return S_OK;
2590}
2591
2592HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2593{
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 i_getLogFolder(aLogFolder);
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2602{
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 aCurrentSnapshot = mData->mCurrentSnapshot;
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2611{
2612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2613
2614 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2615 ? 0
2616 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2617
2618 return S_OK;
2619}
2620
2621HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2622{
2623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2624
2625 /* Note: for machines with no snapshots, we always return FALSE
2626 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2627 * reasons :) */
2628
2629 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2630 ? FALSE
2631 : mData->mCurrentStateModified;
2632
2633 return S_OK;
2634}
2635
2636HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2637{
2638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2639
2640 aSharedFolders.resize(mHWData->mSharedFolders.size());
2641 size_t i = 0;
2642 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2643 it = mHWData->mSharedFolders.begin();
2644 it != mHWData->mSharedFolders.end();
2645 ++it, ++i)
2646 aSharedFolders[i] = *it;
2647
2648 return S_OK;
2649}
2650
2651HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2652{
2653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2654
2655 *aClipboardMode = mHWData->mClipboardMode;
2656
2657 return S_OK;
2658}
2659
2660HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2661{
2662 HRESULT rc = S_OK;
2663
2664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2665
2666 rc = i_checkStateDependency(MutableOrRunningStateDep);
2667 if (FAILED(rc)) return rc;
2668
2669 alock.release();
2670 rc = i_onClipboardModeChange(aClipboardMode);
2671 alock.acquire();
2672 if (FAILED(rc)) return rc;
2673
2674 i_setModified(IsModified_MachineData);
2675 mHWData.backup();
2676 mHWData->mClipboardMode = aClipboardMode;
2677
2678 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2679 if (Global::IsOnline(mData->mMachineState))
2680 i_saveSettings(NULL, alock);
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2686{
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2690
2691 return S_OK;
2692}
2693
2694HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2695{
2696 HRESULT rc = S_OK;
2697
2698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 rc = i_checkStateDependency(MutableOrRunningStateDep);
2701 if (FAILED(rc)) return rc;
2702
2703 alock.release();
2704 rc = i_onClipboardFileTransferModeChange(aEnabled);
2705 alock.acquire();
2706 if (FAILED(rc)) return rc;
2707
2708 i_setModified(IsModified_MachineData);
2709 mHWData.backup();
2710 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2711
2712 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2713 if (Global::IsOnline(mData->mMachineState))
2714 i_saveSettings(NULL, alock);
2715
2716 return S_OK;
2717}
2718
2719HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2720{
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aDnDMode = mHWData->mDnDMode;
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2729{
2730 HRESULT rc = S_OK;
2731
2732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 rc = i_checkStateDependency(MutableOrRunningStateDep);
2735 if (FAILED(rc)) return rc;
2736
2737 alock.release();
2738 rc = i_onDnDModeChange(aDnDMode);
2739
2740 alock.acquire();
2741 if (FAILED(rc)) return rc;
2742
2743 i_setModified(IsModified_MachineData);
2744 mHWData.backup();
2745 mHWData->mDnDMode = aDnDMode;
2746
2747 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2748 if (Global::IsOnline(mData->mMachineState))
2749 i_saveSettings(NULL, alock);
2750
2751 return S_OK;
2752}
2753
2754HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 aStorageControllers.resize(mStorageControllers->size());
2759 size_t i = 0;
2760 for (StorageControllerList::const_iterator
2761 it = mStorageControllers->begin();
2762 it != mStorageControllers->end();
2763 ++it, ++i)
2764 aStorageControllers[i] = *it;
2765
2766 return S_OK;
2767}
2768
2769HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2770{
2771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2772
2773 *aEnabled = mUserData->s.fTeleporterEnabled;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2779{
2780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 /* Only allow it to be set to true when PoweredOff or Aborted.
2783 (Clearing it is always permitted.) */
2784 if ( aTeleporterEnabled
2785 && mData->mRegistered
2786 && ( !i_isSessionMachine()
2787 || ( mData->mMachineState != MachineState_PoweredOff
2788 && mData->mMachineState != MachineState_Teleported
2789 && mData->mMachineState != MachineState_Aborted
2790 )
2791 )
2792 )
2793 return setError(VBOX_E_INVALID_VM_STATE,
2794 tr("The machine is not powered off (state is %s)"),
2795 Global::stringifyMachineState(mData->mMachineState));
2796
2797 i_setModified(IsModified_MachineData);
2798 mUserData.backup();
2799 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2814{
2815 if (aTeleporterPort >= _64K)
2816 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2817
2818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2821 if (FAILED(rc)) return rc;
2822
2823 i_setModified(IsModified_MachineData);
2824 mUserData.backup();
2825 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2826
2827 return S_OK;
2828}
2829
2830HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2831{
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2840{
2841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2844 if (FAILED(rc)) return rc;
2845
2846 i_setModified(IsModified_MachineData);
2847 mUserData.backup();
2848 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2854{
2855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2856 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2862{
2863 /*
2864 * Hash the password first.
2865 */
2866 com::Utf8Str aT = aTeleporterPassword;
2867
2868 if (!aT.isEmpty())
2869 {
2870 if (VBoxIsPasswordHashed(&aT))
2871 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2872 VBoxHashPassword(&aT);
2873 }
2874
2875 /*
2876 * Do the update.
2877 */
2878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2879 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2880 if (SUCCEEDED(hrc))
2881 {
2882 i_setModified(IsModified_MachineData);
2883 mUserData.backup();
2884 mUserData->s.strTeleporterPassword = aT;
2885 }
2886
2887 return hrc;
2888}
2889
2890HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2891{
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2900{
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 /* Only allow it to be set to true when PoweredOff or Aborted.
2904 (Clearing it is always permitted.) */
2905 if ( aRTCUseUTC
2906 && mData->mRegistered
2907 && ( !i_isSessionMachine()
2908 || ( mData->mMachineState != MachineState_PoweredOff
2909 && mData->mMachineState != MachineState_Teleported
2910 && mData->mMachineState != MachineState_Aborted
2911 )
2912 )
2913 )
2914 return setError(VBOX_E_INVALID_VM_STATE,
2915 tr("The machine is not powered off (state is %s)"),
2916 Global::stringifyMachineState(mData->mMachineState));
2917
2918 i_setModified(IsModified_MachineData);
2919 mUserData.backup();
2920 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2935{
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 HRESULT rc = i_checkStateDependency(MutableStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 i_setModified(IsModified_MachineData);
2942 mHWData.backup();
2943 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2949{
2950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 *aIOCacheSize = mHWData->mIOCacheSize;
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2958{
2959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 HRESULT rc = i_checkStateDependency(MutableStateDep);
2962 if (FAILED(rc)) return rc;
2963
2964 i_setModified(IsModified_MachineData);
2965 mHWData.backup();
2966 mHWData->mIOCacheSize = aIOCacheSize;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
2972{
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2976 aKeyId = mSSData->strStateKeyId;
2977#else
2978 aKeyId = com::Utf8Str::Empty;
2979#endif
2980
2981 return S_OK;
2982}
2983
2984HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
2985{
2986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2987
2988#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
2989 aKeyStore = mSSData->strStateKeyStore;
2990#else
2991 aKeyStore = com::Utf8Str::Empty;
2992#endif
2993
2994 return S_OK;
2995}
2996
2997HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
2998{
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3002 aKeyId = mData->mstrLogKeyId;
3003#else
3004 aKeyId = com::Utf8Str::Empty;
3005#endif
3006
3007 return S_OK;
3008}
3009
3010HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3011{
3012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3015 aKeyStore = mData->mstrLogKeyStore;
3016#else
3017 aKeyStore = com::Utf8Str::Empty;
3018#endif
3019
3020 return S_OK;
3021}
3022
3023
3024/**
3025 * @note Locks objects!
3026 */
3027HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3028 LockType_T aLockType)
3029{
3030 /* check the session state */
3031 SessionState_T state;
3032 HRESULT rc = aSession->COMGETTER(State)(&state);
3033 if (FAILED(rc)) return rc;
3034
3035 if (state != SessionState_Unlocked)
3036 return setError(VBOX_E_INVALID_OBJECT_STATE,
3037 tr("The given session is busy"));
3038
3039 // get the client's IInternalSessionControl interface
3040 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3041 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3042 E_INVALIDARG);
3043
3044 // session name (only used in some code paths)
3045 Utf8Str strSessionName;
3046
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 if (!mData->mRegistered)
3050 return setError(E_UNEXPECTED,
3051 tr("The machine '%s' is not registered"),
3052 mUserData->s.strName.c_str());
3053
3054 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3055
3056 SessionState_T oldState = mData->mSession.mState;
3057 /* Hack: in case the session is closing and there is a progress object
3058 * which allows waiting for the session to be closed, take the opportunity
3059 * and do a limited wait (max. 1 second). This helps a lot when the system
3060 * is busy and thus session closing can take a little while. */
3061 if ( mData->mSession.mState == SessionState_Unlocking
3062 && mData->mSession.mProgress)
3063 {
3064 alock.release();
3065 mData->mSession.mProgress->WaitForCompletion(1000);
3066 alock.acquire();
3067 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3068 }
3069
3070 // try again now
3071 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3072 // (i.e. session machine exists)
3073 && (aLockType == LockType_Shared) // caller wants a shared link to the
3074 // existing session that holds the write lock:
3075 )
3076 {
3077 // OK, share the session... we are now dealing with three processes:
3078 // 1) VBoxSVC (where this code runs);
3079 // 2) process C: the caller's client process (who wants a shared session);
3080 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3081
3082 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3083 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3084 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3085 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3086 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3087
3088 /*
3089 * Release the lock before calling the client process. It's safe here
3090 * since the only thing to do after we get the lock again is to add
3091 * the remote control to the list (which doesn't directly influence
3092 * anything).
3093 */
3094 alock.release();
3095
3096 // get the console of the session holding the write lock (this is a remote call)
3097 ComPtr<IConsole> pConsoleW;
3098 if (mData->mSession.mLockType == LockType_VM)
3099 {
3100 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3101 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3102 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3103 if (FAILED(rc))
3104 // the failure may occur w/o any error info (from RPC), so provide one
3105 return setError(VBOX_E_VM_ERROR,
3106 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3107 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3108 }
3109
3110 // share the session machine and W's console with the caller's session
3111 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3112 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3113 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3114
3115 if (FAILED(rc))
3116 // the failure may occur w/o any error info (from RPC), so provide one
3117 return setError(VBOX_E_VM_ERROR,
3118 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3119 alock.acquire();
3120
3121 // need to revalidate the state after acquiring the lock again
3122 if (mData->mSession.mState != SessionState_Locked)
3123 {
3124 pSessionControl->Uninitialize();
3125 return setError(VBOX_E_INVALID_SESSION_STATE,
3126 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3127 mUserData->s.strName.c_str());
3128 }
3129
3130 // add the caller's session to the list
3131 mData->mSession.mRemoteControls.push_back(pSessionControl);
3132 }
3133 else if ( mData->mSession.mState == SessionState_Locked
3134 || mData->mSession.mState == SessionState_Unlocking
3135 )
3136 {
3137 // sharing not permitted, or machine still unlocking:
3138 return setError(VBOX_E_INVALID_OBJECT_STATE,
3139 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3140 mUserData->s.strName.c_str());
3141 }
3142 else
3143 {
3144 // machine is not locked: then write-lock the machine (create the session machine)
3145
3146 // must not be busy
3147 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3148
3149 // get the caller's session PID
3150 RTPROCESS pid = NIL_RTPROCESS;
3151 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3152 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3153 Assert(pid != NIL_RTPROCESS);
3154
3155 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3156
3157 if (fLaunchingVMProcess)
3158 {
3159 if (mData->mSession.mPID == NIL_RTPROCESS)
3160 {
3161 // two or more clients racing for a lock, the one which set the
3162 // session state to Spawning will win, the others will get an
3163 // error as we can't decide here if waiting a little would help
3164 // (only for shared locks this would avoid an error)
3165 return setError(VBOX_E_INVALID_OBJECT_STATE,
3166 tr("The machine '%s' already has a lock request pending"),
3167 mUserData->s.strName.c_str());
3168 }
3169
3170 // this machine is awaiting for a spawning session to be opened:
3171 // then the calling process must be the one that got started by
3172 // LaunchVMProcess()
3173
3174 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3175 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3176
3177#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3178 /* Hardened windows builds spawns three processes when a VM is
3179 launched, the 3rd one is the one that will end up here. */
3180 RTPROCESS pidParent;
3181 int vrc = RTProcQueryParent(pid, &pidParent);
3182 if (RT_SUCCESS(vrc))
3183 vrc = RTProcQueryParent(pidParent, &pidParent);
3184 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3185 || vrc == VERR_ACCESS_DENIED)
3186 {
3187 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3188 mData->mSession.mPID = pid;
3189 }
3190#endif
3191
3192 if (mData->mSession.mPID != pid)
3193 return setError(E_ACCESSDENIED,
3194 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3195 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3196 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3197 }
3198
3199 // create the mutable SessionMachine from the current machine
3200 ComObjPtr<SessionMachine> sessionMachine;
3201 sessionMachine.createObject();
3202 rc = sessionMachine->init(this);
3203 AssertComRC(rc);
3204
3205 /* NOTE: doing return from this function after this point but
3206 * before the end is forbidden since it may call SessionMachine::uninit()
3207 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3208 * lock while still holding the Machine lock in alock so that a deadlock
3209 * is possible due to the wrong lock order. */
3210
3211 if (SUCCEEDED(rc))
3212 {
3213 /*
3214 * Set the session state to Spawning to protect against subsequent
3215 * attempts to open a session and to unregister the machine after
3216 * we release the lock.
3217 */
3218 SessionState_T origState = mData->mSession.mState;
3219 mData->mSession.mState = SessionState_Spawning;
3220
3221#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3222 /* Get the client token ID to be passed to the client process */
3223 Utf8Str strTokenId;
3224 sessionMachine->i_getTokenId(strTokenId);
3225 Assert(!strTokenId.isEmpty());
3226#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3227 /* Get the client token to be passed to the client process */
3228 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3229 /* The token is now "owned" by pToken, fix refcount */
3230 if (!pToken.isNull())
3231 pToken->Release();
3232#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3233
3234 /*
3235 * Release the lock before calling the client process -- it will call
3236 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3237 * because the state is Spawning, so that LaunchVMProcess() and
3238 * LockMachine() calls will fail. This method, called before we
3239 * acquire the lock again, will fail because of the wrong PID.
3240 *
3241 * Note that mData->mSession.mRemoteControls accessed outside
3242 * the lock may not be modified when state is Spawning, so it's safe.
3243 */
3244 alock.release();
3245
3246 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3247#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3248 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3249#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3250 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3251 /* Now the token is owned by the client process. */
3252 pToken.setNull();
3253#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3254 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3255
3256 /* The failure may occur w/o any error info (from RPC), so provide one */
3257 if (FAILED(rc))
3258 setError(VBOX_E_VM_ERROR,
3259 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3260
3261 // get session name, either to remember or to compare against
3262 // the already known session name.
3263 {
3264 Bstr bstrSessionName;
3265 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3266 if (SUCCEEDED(rc2))
3267 strSessionName = bstrSessionName;
3268 }
3269
3270 if ( SUCCEEDED(rc)
3271 && fLaunchingVMProcess
3272 )
3273 {
3274 /* complete the remote session initialization */
3275
3276 /* get the console from the direct session */
3277 ComPtr<IConsole> console;
3278 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3279 ComAssertComRC(rc);
3280
3281 if (SUCCEEDED(rc) && !console)
3282 {
3283 ComAssert(!!console);
3284 rc = E_FAIL;
3285 }
3286
3287 /* assign machine & console to the remote session */
3288 if (SUCCEEDED(rc))
3289 {
3290 /*
3291 * after LaunchVMProcess(), the first and the only
3292 * entry in remoteControls is that remote session
3293 */
3294 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3295 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3296 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3297
3298 /* The failure may occur w/o any error info (from RPC), so provide one */
3299 if (FAILED(rc))
3300 setError(VBOX_E_VM_ERROR,
3301 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3302 }
3303
3304 if (FAILED(rc))
3305 pSessionControl->Uninitialize();
3306 }
3307
3308 /* acquire the lock again */
3309 alock.acquire();
3310
3311 /* Restore the session state */
3312 mData->mSession.mState = origState;
3313 }
3314
3315 // finalize spawning anyway (this is why we don't return on errors above)
3316 if (fLaunchingVMProcess)
3317 {
3318 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3319 /* Note that the progress object is finalized later */
3320 /** @todo Consider checking mData->mSession.mProgress for cancellation
3321 * around here. */
3322
3323 /* We don't reset mSession.mPID here because it is necessary for
3324 * SessionMachine::uninit() to reap the child process later. */
3325
3326 if (FAILED(rc))
3327 {
3328 /* Close the remote session, remove the remote control from the list
3329 * and reset session state to Closed (@note keep the code in sync
3330 * with the relevant part in checkForSpawnFailure()). */
3331
3332 Assert(mData->mSession.mRemoteControls.size() == 1);
3333 if (mData->mSession.mRemoteControls.size() == 1)
3334 {
3335 ErrorInfoKeeper eik;
3336 mData->mSession.mRemoteControls.front()->Uninitialize();
3337 }
3338
3339 mData->mSession.mRemoteControls.clear();
3340 mData->mSession.mState = SessionState_Unlocked;
3341 }
3342 }
3343 else
3344 {
3345 /* memorize PID of the directly opened session */
3346 if (SUCCEEDED(rc))
3347 mData->mSession.mPID = pid;
3348 }
3349
3350 if (SUCCEEDED(rc))
3351 {
3352 mData->mSession.mLockType = aLockType;
3353 /* memorize the direct session control and cache IUnknown for it */
3354 mData->mSession.mDirectControl = pSessionControl;
3355 mData->mSession.mState = SessionState_Locked;
3356 if (!fLaunchingVMProcess)
3357 mData->mSession.mName = strSessionName;
3358 /* associate the SessionMachine with this Machine */
3359 mData->mSession.mMachine = sessionMachine;
3360
3361 /* request an IUnknown pointer early from the remote party for later
3362 * identity checks (it will be internally cached within mDirectControl
3363 * at least on XPCOM) */
3364 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3365 NOREF(unk);
3366 }
3367
3368 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3369 * would break the lock order */
3370 alock.release();
3371
3372 /* uninitialize the created session machine on failure */
3373 if (FAILED(rc))
3374 sessionMachine->uninit();
3375 }
3376
3377 if (SUCCEEDED(rc))
3378 {
3379 /*
3380 * tell the client watcher thread to update the set of
3381 * machines that have open sessions
3382 */
3383 mParent->i_updateClientWatcher();
3384
3385 if (oldState != SessionState_Locked)
3386 /* fire an event */
3387 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3388 }
3389
3390 return rc;
3391}
3392
3393/**
3394 * @note Locks objects!
3395 */
3396HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3397 const com::Utf8Str &aName,
3398 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3399 ComPtr<IProgress> &aProgress)
3400{
3401 Utf8Str strFrontend(aName);
3402 /* "emergencystop" doesn't need the session, so skip the checks/interface
3403 * retrieval. This code doesn't quite fit in here, but introducing a
3404 * special API method would be even more effort, and would require explicit
3405 * support by every API client. It's better to hide the feature a bit. */
3406 if (strFrontend != "emergencystop")
3407 CheckComArgNotNull(aSession);
3408
3409 HRESULT rc = S_OK;
3410 if (strFrontend.isEmpty())
3411 {
3412 Bstr bstrFrontend;
3413 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3414 if (FAILED(rc))
3415 return rc;
3416 strFrontend = bstrFrontend;
3417 if (strFrontend.isEmpty())
3418 {
3419 ComPtr<ISystemProperties> systemProperties;
3420 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3421 if (FAILED(rc))
3422 return rc;
3423 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3424 if (FAILED(rc))
3425 return rc;
3426 strFrontend = bstrFrontend;
3427 }
3428 /* paranoia - emergencystop is not a valid default */
3429 if (strFrontend == "emergencystop")
3430 strFrontend = Utf8Str::Empty;
3431 }
3432 /* default frontend: Qt GUI */
3433 if (strFrontend.isEmpty())
3434 strFrontend = "GUI/Qt";
3435
3436 if (strFrontend != "emergencystop")
3437 {
3438 /* check the session state */
3439 SessionState_T state;
3440 rc = aSession->COMGETTER(State)(&state);
3441 if (FAILED(rc))
3442 return rc;
3443
3444 if (state != SessionState_Unlocked)
3445 return setError(VBOX_E_INVALID_OBJECT_STATE,
3446 tr("The given session is busy"));
3447
3448 /* get the IInternalSessionControl interface */
3449 ComPtr<IInternalSessionControl> control(aSession);
3450 ComAssertMsgRet(!control.isNull(),
3451 ("No IInternalSessionControl interface"),
3452 E_INVALIDARG);
3453
3454 /* get the teleporter enable state for the progress object init. */
3455 BOOL fTeleporterEnabled;
3456 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3457 if (FAILED(rc))
3458 return rc;
3459
3460 /* create a progress object */
3461 ComObjPtr<ProgressProxy> progress;
3462 progress.createObject();
3463 rc = progress->init(mParent,
3464 static_cast<IMachine*>(this),
3465 Bstr(tr("Starting VM")).raw(),
3466 TRUE /* aCancelable */,
3467 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3468 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3469 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3470 2 /* uFirstOperationWeight */,
3471 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3472
3473 if (SUCCEEDED(rc))
3474 {
3475 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3476 if (SUCCEEDED(rc))
3477 {
3478 aProgress = progress;
3479
3480 /* signal the client watcher thread */
3481 mParent->i_updateClientWatcher();
3482
3483 /* fire an event */
3484 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3485 }
3486 }
3487 }
3488 else
3489 {
3490 /* no progress object - either instant success or failure */
3491 aProgress = NULL;
3492
3493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3494
3495 if (mData->mSession.mState != SessionState_Locked)
3496 return setError(VBOX_E_INVALID_OBJECT_STATE,
3497 tr("The machine '%s' is not locked by a session"),
3498 mUserData->s.strName.c_str());
3499
3500 /* must have a VM process associated - do not kill normal API clients
3501 * with an open session */
3502 if (!Global::IsOnline(mData->mMachineState))
3503 return setError(VBOX_E_INVALID_OBJECT_STATE,
3504 tr("The machine '%s' does not have a VM process"),
3505 mUserData->s.strName.c_str());
3506
3507 /* forcibly terminate the VM process */
3508 if (mData->mSession.mPID != NIL_RTPROCESS)
3509 RTProcTerminate(mData->mSession.mPID);
3510
3511 /* signal the client watcher thread, as most likely the client has
3512 * been terminated */
3513 mParent->i_updateClientWatcher();
3514 }
3515
3516 return rc;
3517}
3518
3519HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3520{
3521 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3522 return setError(E_INVALIDARG,
3523 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3524 aPosition, SchemaDefs::MaxBootPosition);
3525
3526 if (aDevice == DeviceType_USB)
3527 return setError(E_NOTIMPL,
3528 tr("Booting from USB device is currently not supported"));
3529
3530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3531
3532 HRESULT rc = i_checkStateDependency(MutableStateDep);
3533 if (FAILED(rc)) return rc;
3534
3535 i_setModified(IsModified_MachineData);
3536 mHWData.backup();
3537 mHWData->mBootOrder[aPosition - 1] = aDevice;
3538
3539 return S_OK;
3540}
3541
3542HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3543{
3544 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3545 return setError(E_INVALIDARG,
3546 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3547 aPosition, SchemaDefs::MaxBootPosition);
3548
3549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3550
3551 *aDevice = mHWData->mBootOrder[aPosition - 1];
3552
3553 return S_OK;
3554}
3555
3556HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3557 LONG aControllerPort,
3558 LONG aDevice,
3559 DeviceType_T aType,
3560 const ComPtr<IMedium> &aMedium)
3561{
3562 IMedium *aM = aMedium;
3563 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3564 aName.c_str(), aControllerPort, aDevice, aType, aM));
3565
3566 // request the host lock first, since might be calling Host methods for getting host drives;
3567 // next, protect the media tree all the while we're in here, as well as our member variables
3568 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3569 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3570
3571 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3572 if (FAILED(rc)) return rc;
3573
3574 /// @todo NEWMEDIA implicit machine registration
3575 if (!mData->mRegistered)
3576 return setError(VBOX_E_INVALID_OBJECT_STATE,
3577 tr("Cannot attach storage devices to an unregistered machine"));
3578
3579 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3580
3581 /* Check for an existing controller. */
3582 ComObjPtr<StorageController> ctl;
3583 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3584 if (FAILED(rc)) return rc;
3585
3586 StorageControllerType_T ctrlType;
3587 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3588 if (FAILED(rc))
3589 return setError(E_FAIL,
3590 tr("Could not get type of controller '%s'"),
3591 aName.c_str());
3592
3593 bool fSilent = false;
3594 Utf8Str strReconfig;
3595
3596 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3597 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3598 if ( mData->mMachineState == MachineState_Paused
3599 && strReconfig == "1")
3600 fSilent = true;
3601
3602 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3603 bool fHotplug = false;
3604 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3605 fHotplug = true;
3606
3607 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3608 return setError(VBOX_E_INVALID_VM_STATE,
3609 tr("Controller '%s' does not support hot-plugging"),
3610 aName.c_str());
3611
3612 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3613 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3614 fHotplug = true;
3615
3616 // check that the port and device are not out of range
3617 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3618 if (FAILED(rc)) return rc;
3619
3620 /* check if the device slot is already busy */
3621 MediumAttachment *pAttachTemp;
3622 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3623 aName,
3624 aControllerPort,
3625 aDevice)))
3626 {
3627 Medium *pMedium = pAttachTemp->i_getMedium();
3628 if (pMedium)
3629 {
3630 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3631 return setError(VBOX_E_OBJECT_IN_USE,
3632 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3633 pMedium->i_getLocationFull().c_str(),
3634 aControllerPort,
3635 aDevice,
3636 aName.c_str());
3637 }
3638 else
3639 return setError(VBOX_E_OBJECT_IN_USE,
3640 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3641 aControllerPort, aDevice, aName.c_str());
3642 }
3643
3644 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3645 if (aMedium && medium.isNull())
3646 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3647
3648 AutoCaller mediumCaller(medium);
3649 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3650
3651 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3652
3653 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3654 && !medium.isNull()
3655 && ( medium->i_getType() != MediumType_Readonly
3656 || medium->i_getDeviceType() != DeviceType_DVD)
3657 )
3658 return setError(VBOX_E_OBJECT_IN_USE,
3659 tr("Medium '%s' is already attached to this virtual machine"),
3660 medium->i_getLocationFull().c_str());
3661
3662 if (!medium.isNull())
3663 {
3664 MediumType_T mtype = medium->i_getType();
3665 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3666 // For DVDs it's not written to the config file, so needs no global config
3667 // version bump. For floppies it's a new attribute "type", which is ignored
3668 // by older VirtualBox version, so needs no global config version bump either.
3669 // For hard disks this type is not accepted.
3670 if (mtype == MediumType_MultiAttach)
3671 {
3672 // This type is new with VirtualBox 4.0 and therefore requires settings
3673 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3674 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3675 // two reasons: The medium type is a property of the media registry tree, which
3676 // can reside in the global config file (for pre-4.0 media); we would therefore
3677 // possibly need to bump the global config version. We don't want to do that though
3678 // because that might make downgrading to pre-4.0 impossible.
3679 // As a result, we can only use these two new types if the medium is NOT in the
3680 // global registry:
3681 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3682 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3683 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3684 )
3685 return setError(VBOX_E_INVALID_OBJECT_STATE,
3686 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3687 "to machines that were created with VirtualBox 4.0 or later"),
3688 medium->i_getLocationFull().c_str());
3689 }
3690 }
3691
3692 bool fIndirect = false;
3693 if (!medium.isNull())
3694 fIndirect = medium->i_isReadOnly();
3695 bool associate = true;
3696
3697 do
3698 {
3699 if ( aType == DeviceType_HardDisk
3700 && mMediumAttachments.isBackedUp())
3701 {
3702 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3703
3704 /* check if the medium was attached to the VM before we started
3705 * changing attachments in which case the attachment just needs to
3706 * be restored */
3707 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3708 {
3709 AssertReturn(!fIndirect, E_FAIL);
3710
3711 /* see if it's the same bus/channel/device */
3712 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3713 {
3714 /* the simplest case: restore the whole attachment
3715 * and return, nothing else to do */
3716 mMediumAttachments->push_back(pAttachTemp);
3717
3718 /* Reattach the medium to the VM. */
3719 if (fHotplug || fSilent)
3720 {
3721 mediumLock.release();
3722 treeLock.release();
3723 alock.release();
3724
3725 MediumLockList *pMediumLockList(new MediumLockList());
3726
3727 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3728 medium /* pToLockWrite */,
3729 false /* fMediumLockWriteAll */,
3730 NULL,
3731 *pMediumLockList);
3732 alock.acquire();
3733 if (FAILED(rc))
3734 delete pMediumLockList;
3735 else
3736 {
3737 mData->mSession.mLockedMedia.Unlock();
3738 alock.release();
3739 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3740 mData->mSession.mLockedMedia.Lock();
3741 alock.acquire();
3742 }
3743 alock.release();
3744
3745 if (SUCCEEDED(rc))
3746 {
3747 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3748 /* Remove lock list in case of error. */
3749 if (FAILED(rc))
3750 {
3751 mData->mSession.mLockedMedia.Unlock();
3752 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3753 mData->mSession.mLockedMedia.Lock();
3754 }
3755 }
3756 }
3757
3758 return S_OK;
3759 }
3760
3761 /* bus/channel/device differ; we need a new attachment object,
3762 * but don't try to associate it again */
3763 associate = false;
3764 break;
3765 }
3766 }
3767
3768 /* go further only if the attachment is to be indirect */
3769 if (!fIndirect)
3770 break;
3771
3772 /* perform the so called smart attachment logic for indirect
3773 * attachments. Note that smart attachment is only applicable to base
3774 * hard disks. */
3775
3776 if (medium->i_getParent().isNull())
3777 {
3778 /* first, investigate the backup copy of the current hard disk
3779 * attachments to make it possible to re-attach existing diffs to
3780 * another device slot w/o losing their contents */
3781 if (mMediumAttachments.isBackedUp())
3782 {
3783 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3784
3785 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3786 uint32_t foundLevel = 0;
3787
3788 for (MediumAttachmentList::const_iterator
3789 it = oldAtts.begin();
3790 it != oldAtts.end();
3791 ++it)
3792 {
3793 uint32_t level = 0;
3794 MediumAttachment *pAttach = *it;
3795 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3796 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3797 if (pMedium.isNull())
3798 continue;
3799
3800 if (pMedium->i_getBase(&level) == medium)
3801 {
3802 /* skip the hard disk if its currently attached (we
3803 * cannot attach the same hard disk twice) */
3804 if (i_findAttachment(*mMediumAttachments.data(),
3805 pMedium))
3806 continue;
3807
3808 /* matched device, channel and bus (i.e. attached to the
3809 * same place) will win and immediately stop the search;
3810 * otherwise the attachment that has the youngest
3811 * descendant of medium will be used
3812 */
3813 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3814 {
3815 /* the simplest case: restore the whole attachment
3816 * and return, nothing else to do */
3817 mMediumAttachments->push_back(*it);
3818
3819 /* Reattach the medium to the VM. */
3820 if (fHotplug || fSilent)
3821 {
3822 mediumLock.release();
3823 treeLock.release();
3824 alock.release();
3825
3826 MediumLockList *pMediumLockList(new MediumLockList());
3827
3828 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3829 medium /* pToLockWrite */,
3830 false /* fMediumLockWriteAll */,
3831 NULL,
3832 *pMediumLockList);
3833 alock.acquire();
3834 if (FAILED(rc))
3835 delete pMediumLockList;
3836 else
3837 {
3838 mData->mSession.mLockedMedia.Unlock();
3839 alock.release();
3840 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3841 mData->mSession.mLockedMedia.Lock();
3842 alock.acquire();
3843 }
3844 alock.release();
3845
3846 if (SUCCEEDED(rc))
3847 {
3848 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3849 /* Remove lock list in case of error. */
3850 if (FAILED(rc))
3851 {
3852 mData->mSession.mLockedMedia.Unlock();
3853 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3854 mData->mSession.mLockedMedia.Lock();
3855 }
3856 }
3857 }
3858
3859 return S_OK;
3860 }
3861 else if ( foundIt == oldAtts.end()
3862 || level > foundLevel /* prefer younger */
3863 )
3864 {
3865 foundIt = it;
3866 foundLevel = level;
3867 }
3868 }
3869 }
3870
3871 if (foundIt != oldAtts.end())
3872 {
3873 /* use the previously attached hard disk */
3874 medium = (*foundIt)->i_getMedium();
3875 mediumCaller.attach(medium);
3876 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3877 mediumLock.attach(medium);
3878 /* not implicit, doesn't require association with this VM */
3879 fIndirect = false;
3880 associate = false;
3881 /* go right to the MediumAttachment creation */
3882 break;
3883 }
3884 }
3885
3886 /* must give up the medium lock and medium tree lock as below we
3887 * go over snapshots, which needs a lock with higher lock order. */
3888 mediumLock.release();
3889 treeLock.release();
3890
3891 /* then, search through snapshots for the best diff in the given
3892 * hard disk's chain to base the new diff on */
3893
3894 ComObjPtr<Medium> base;
3895 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3896 while (snap)
3897 {
3898 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3899
3900 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3901
3902 MediumAttachment *pAttachFound = NULL;
3903 uint32_t foundLevel = 0;
3904
3905 for (MediumAttachmentList::const_iterator
3906 it = snapAtts.begin();
3907 it != snapAtts.end();
3908 ++it)
3909 {
3910 MediumAttachment *pAttach = *it;
3911 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3912 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3913 if (pMedium.isNull())
3914 continue;
3915
3916 uint32_t level = 0;
3917 if (pMedium->i_getBase(&level) == medium)
3918 {
3919 /* matched device, channel and bus (i.e. attached to the
3920 * same place) will win and immediately stop the search;
3921 * otherwise the attachment that has the youngest
3922 * descendant of medium will be used
3923 */
3924 if ( pAttach->i_getDevice() == aDevice
3925 && pAttach->i_getPort() == aControllerPort
3926 && pAttach->i_getControllerName() == aName
3927 )
3928 {
3929 pAttachFound = pAttach;
3930 break;
3931 }
3932 else if ( !pAttachFound
3933 || level > foundLevel /* prefer younger */
3934 )
3935 {
3936 pAttachFound = pAttach;
3937 foundLevel = level;
3938 }
3939 }
3940 }
3941
3942 if (pAttachFound)
3943 {
3944 base = pAttachFound->i_getMedium();
3945 break;
3946 }
3947
3948 snap = snap->i_getParent();
3949 }
3950
3951 /* re-lock medium tree and the medium, as we need it below */
3952 treeLock.acquire();
3953 mediumLock.acquire();
3954
3955 /* found a suitable diff, use it as a base */
3956 if (!base.isNull())
3957 {
3958 medium = base;
3959 mediumCaller.attach(medium);
3960 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3961 mediumLock.attach(medium);
3962 }
3963 }
3964
3965 Utf8Str strFullSnapshotFolder;
3966 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3967
3968 ComObjPtr<Medium> diff;
3969 diff.createObject();
3970 // store this diff in the same registry as the parent
3971 Guid uuidRegistryParent;
3972 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3973 {
3974 // parent image has no registry: this can happen if we're attaching a new immutable
3975 // image that has not yet been attached (medium then points to the base and we're
3976 // creating the diff image for the immutable, and the parent is not yet registered);
3977 // put the parent in the machine registry then
3978 mediumLock.release();
3979 treeLock.release();
3980 alock.release();
3981 i_addMediumToRegistry(medium);
3982 alock.acquire();
3983 treeLock.acquire();
3984 mediumLock.acquire();
3985 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3986 }
3987 rc = diff->init(mParent,
3988 medium->i_getPreferredDiffFormat(),
3989 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3990 uuidRegistryParent,
3991 DeviceType_HardDisk);
3992 if (FAILED(rc)) return rc;
3993
3994 /* Apply the normal locking logic to the entire chain. */
3995 MediumLockList *pMediumLockList(new MediumLockList());
3996 mediumLock.release();
3997 treeLock.release();
3998 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3999 diff /* pToLockWrite */,
4000 false /* fMediumLockWriteAll */,
4001 medium,
4002 *pMediumLockList);
4003 treeLock.acquire();
4004 mediumLock.acquire();
4005 if (SUCCEEDED(rc))
4006 {
4007 mediumLock.release();
4008 treeLock.release();
4009 rc = pMediumLockList->Lock();
4010 treeLock.acquire();
4011 mediumLock.acquire();
4012 if (FAILED(rc))
4013 setError(rc,
4014 tr("Could not lock medium when creating diff '%s'"),
4015 diff->i_getLocationFull().c_str());
4016 else
4017 {
4018 /* will release the lock before the potentially lengthy
4019 * operation, so protect with the special state */
4020 MachineState_T oldState = mData->mMachineState;
4021 i_setMachineState(MachineState_SettingUp);
4022
4023 mediumLock.release();
4024 treeLock.release();
4025 alock.release();
4026
4027 rc = medium->i_createDiffStorage(diff,
4028 medium->i_getPreferredDiffVariant(),
4029 pMediumLockList,
4030 NULL /* aProgress */,
4031 true /* aWait */,
4032 false /* aNotify */);
4033
4034 alock.acquire();
4035 treeLock.acquire();
4036 mediumLock.acquire();
4037
4038 i_setMachineState(oldState);
4039 }
4040 }
4041
4042 /* Unlock the media and free the associated memory. */
4043 delete pMediumLockList;
4044
4045 if (FAILED(rc)) return rc;
4046
4047 /* use the created diff for the actual attachment */
4048 medium = diff;
4049 mediumCaller.attach(medium);
4050 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4051 mediumLock.attach(medium);
4052 }
4053 while (0);
4054
4055 ComObjPtr<MediumAttachment> attachment;
4056 attachment.createObject();
4057 rc = attachment->init(this,
4058 medium,
4059 aName,
4060 aControllerPort,
4061 aDevice,
4062 aType,
4063 fIndirect,
4064 false /* fPassthrough */,
4065 false /* fTempEject */,
4066 false /* fNonRotational */,
4067 false /* fDiscard */,
4068 fHotplug /* fHotPluggable */,
4069 Utf8Str::Empty);
4070 if (FAILED(rc)) return rc;
4071
4072 if (associate && !medium.isNull())
4073 {
4074 // as the last step, associate the medium to the VM
4075 rc = medium->i_addBackReference(mData->mUuid);
4076 // here we can fail because of Deleting, or being in process of creating a Diff
4077 if (FAILED(rc)) return rc;
4078
4079 mediumLock.release();
4080 treeLock.release();
4081 alock.release();
4082 i_addMediumToRegistry(medium);
4083 alock.acquire();
4084 treeLock.acquire();
4085 mediumLock.acquire();
4086 }
4087
4088 /* success: finally remember the attachment */
4089 i_setModified(IsModified_Storage);
4090 mMediumAttachments.backup();
4091 mMediumAttachments->push_back(attachment);
4092
4093 mediumLock.release();
4094 treeLock.release();
4095 alock.release();
4096
4097 if (fHotplug || fSilent)
4098 {
4099 if (!medium.isNull())
4100 {
4101 MediumLockList *pMediumLockList(new MediumLockList());
4102
4103 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4104 medium /* pToLockWrite */,
4105 false /* fMediumLockWriteAll */,
4106 NULL,
4107 *pMediumLockList);
4108 alock.acquire();
4109 if (FAILED(rc))
4110 delete pMediumLockList;
4111 else
4112 {
4113 mData->mSession.mLockedMedia.Unlock();
4114 alock.release();
4115 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4116 mData->mSession.mLockedMedia.Lock();
4117 alock.acquire();
4118 }
4119 alock.release();
4120 }
4121
4122 if (SUCCEEDED(rc))
4123 {
4124 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4125 /* Remove lock list in case of error. */
4126 if (FAILED(rc))
4127 {
4128 mData->mSession.mLockedMedia.Unlock();
4129 mData->mSession.mLockedMedia.Remove(attachment);
4130 mData->mSession.mLockedMedia.Lock();
4131 }
4132 }
4133 }
4134
4135 /* Save modified registries, but skip this machine as it's the caller's
4136 * job to save its settings like all other settings changes. */
4137 mParent->i_unmarkRegistryModified(i_getId());
4138 mParent->i_saveModifiedRegistries();
4139
4140 if (SUCCEEDED(rc))
4141 {
4142 if (fIndirect && medium != aM)
4143 mParent->i_onMediumConfigChanged(medium);
4144 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4145 }
4146
4147 return rc;
4148}
4149
4150HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4151 LONG aDevice)
4152{
4153 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4154 aName.c_str(), aControllerPort, aDevice));
4155
4156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4157
4158 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4159 if (FAILED(rc)) return rc;
4160
4161 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4162
4163 /* Check for an existing controller. */
4164 ComObjPtr<StorageController> ctl;
4165 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4166 if (FAILED(rc)) return rc;
4167
4168 StorageControllerType_T ctrlType;
4169 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4170 if (FAILED(rc))
4171 return setError(E_FAIL,
4172 tr("Could not get type of controller '%s'"),
4173 aName.c_str());
4174
4175 bool fSilent = false;
4176 Utf8Str strReconfig;
4177
4178 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4179 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4180 if ( mData->mMachineState == MachineState_Paused
4181 && strReconfig == "1")
4182 fSilent = true;
4183
4184 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4185 bool fHotplug = false;
4186 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4187 fHotplug = true;
4188
4189 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4190 return setError(VBOX_E_INVALID_VM_STATE,
4191 tr("Controller '%s' does not support hot-plugging"),
4192 aName.c_str());
4193
4194 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4195 aName,
4196 aControllerPort,
4197 aDevice);
4198 if (!pAttach)
4199 return setError(VBOX_E_OBJECT_NOT_FOUND,
4200 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4201 aDevice, aControllerPort, aName.c_str());
4202
4203 if (fHotplug && !pAttach->i_getHotPluggable())
4204 return setError(VBOX_E_NOT_SUPPORTED,
4205 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4206 aDevice, aControllerPort, aName.c_str());
4207
4208 /*
4209 * The VM has to detach the device before we delete any implicit diffs.
4210 * If this fails we can roll back without loosing data.
4211 */
4212 if (fHotplug || fSilent)
4213 {
4214 alock.release();
4215 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4216 alock.acquire();
4217 }
4218 if (FAILED(rc)) return rc;
4219
4220 /* If we are here everything went well and we can delete the implicit now. */
4221 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4222
4223 alock.release();
4224
4225 /* Save modified registries, but skip this machine as it's the caller's
4226 * job to save its settings like all other settings changes. */
4227 mParent->i_unmarkRegistryModified(i_getId());
4228 mParent->i_saveModifiedRegistries();
4229
4230 if (SUCCEEDED(rc))
4231 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4232
4233 return rc;
4234}
4235
4236HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4237 LONG aDevice, BOOL aPassthrough)
4238{
4239 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4240 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4241
4242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4243
4244 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4245 if (FAILED(rc)) return rc;
4246
4247 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4248
4249 /* Check for an existing controller. */
4250 ComObjPtr<StorageController> ctl;
4251 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4252 if (FAILED(rc)) return rc;
4253
4254 StorageControllerType_T ctrlType;
4255 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4256 if (FAILED(rc))
4257 return setError(E_FAIL,
4258 tr("Could not get type of controller '%s'"),
4259 aName.c_str());
4260
4261 bool fSilent = false;
4262 Utf8Str strReconfig;
4263
4264 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4265 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4266 if ( mData->mMachineState == MachineState_Paused
4267 && strReconfig == "1")
4268 fSilent = true;
4269
4270 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4271 bool fHotplug = false;
4272 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4273 fHotplug = true;
4274
4275 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4276 return setError(VBOX_E_INVALID_VM_STATE,
4277 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4278 aName.c_str());
4279
4280 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4281 aName,
4282 aControllerPort,
4283 aDevice);
4284 if (!pAttach)
4285 return setError(VBOX_E_OBJECT_NOT_FOUND,
4286 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4287 aDevice, aControllerPort, aName.c_str());
4288
4289
4290 i_setModified(IsModified_Storage);
4291 mMediumAttachments.backup();
4292
4293 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4294
4295 if (pAttach->i_getType() != DeviceType_DVD)
4296 return setError(E_INVALIDARG,
4297 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4298 aDevice, aControllerPort, aName.c_str());
4299
4300 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4301
4302 pAttach->i_updatePassthrough(!!aPassthrough);
4303
4304 attLock.release();
4305 alock.release();
4306 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4307 if (SUCCEEDED(rc) && fValueChanged)
4308 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4309
4310 return rc;
4311}
4312
4313HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4314 LONG aDevice, BOOL aTemporaryEject)
4315{
4316
4317 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4318 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4319
4320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4321
4322 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4323 if (FAILED(rc)) return rc;
4324
4325 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4326 aName,
4327 aControllerPort,
4328 aDevice);
4329 if (!pAttach)
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
4335 i_setModified(IsModified_Storage);
4336 mMediumAttachments.backup();
4337
4338 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4339
4340 if (pAttach->i_getType() != DeviceType_DVD)
4341 return setError(E_INVALIDARG,
4342 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4343 aDevice, aControllerPort, aName.c_str());
4344 pAttach->i_updateTempEject(!!aTemporaryEject);
4345
4346 return S_OK;
4347}
4348
4349HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4350 LONG aDevice, BOOL aNonRotational)
4351{
4352
4353 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4354 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4355
4356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4357
4358 HRESULT rc = i_checkStateDependency(MutableStateDep);
4359 if (FAILED(rc)) return rc;
4360
4361 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4362
4363 if (Global::IsOnlineOrTransient(mData->mMachineState))
4364 return setError(VBOX_E_INVALID_VM_STATE,
4365 tr("Invalid machine state: %s"),
4366 Global::stringifyMachineState(mData->mMachineState));
4367
4368 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4369 aName,
4370 aControllerPort,
4371 aDevice);
4372 if (!pAttach)
4373 return setError(VBOX_E_OBJECT_NOT_FOUND,
4374 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4375 aDevice, aControllerPort, aName.c_str());
4376
4377
4378 i_setModified(IsModified_Storage);
4379 mMediumAttachments.backup();
4380
4381 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4382
4383 if (pAttach->i_getType() != DeviceType_HardDisk)
4384 return setError(E_INVALIDARG,
4385 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"),
4386 aDevice, aControllerPort, aName.c_str());
4387 pAttach->i_updateNonRotational(!!aNonRotational);
4388
4389 return S_OK;
4390}
4391
4392HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4393 LONG aDevice, BOOL aDiscard)
4394{
4395
4396 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4397 aName.c_str(), aControllerPort, aDevice, aDiscard));
4398
4399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4400
4401 HRESULT rc = i_checkStateDependency(MutableStateDep);
4402 if (FAILED(rc)) return rc;
4403
4404 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4405
4406 if (Global::IsOnlineOrTransient(mData->mMachineState))
4407 return setError(VBOX_E_INVALID_VM_STATE,
4408 tr("Invalid machine state: %s"),
4409 Global::stringifyMachineState(mData->mMachineState));
4410
4411 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4412 aName,
4413 aControllerPort,
4414 aDevice);
4415 if (!pAttach)
4416 return setError(VBOX_E_OBJECT_NOT_FOUND,
4417 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4418 aDevice, aControllerPort, aName.c_str());
4419
4420
4421 i_setModified(IsModified_Storage);
4422 mMediumAttachments.backup();
4423
4424 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4425
4426 if (pAttach->i_getType() != DeviceType_HardDisk)
4427 return setError(E_INVALIDARG,
4428 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"),
4429 aDevice, aControllerPort, aName.c_str());
4430 pAttach->i_updateDiscard(!!aDiscard);
4431
4432 return S_OK;
4433}
4434
4435HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4436 LONG aDevice, BOOL aHotPluggable)
4437{
4438 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4439 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4440
4441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4442
4443 HRESULT rc = i_checkStateDependency(MutableStateDep);
4444 if (FAILED(rc)) return rc;
4445
4446 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4447
4448 if (Global::IsOnlineOrTransient(mData->mMachineState))
4449 return setError(VBOX_E_INVALID_VM_STATE,
4450 tr("Invalid machine state: %s"),
4451 Global::stringifyMachineState(mData->mMachineState));
4452
4453 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4454 aName,
4455 aControllerPort,
4456 aDevice);
4457 if (!pAttach)
4458 return setError(VBOX_E_OBJECT_NOT_FOUND,
4459 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4460 aDevice, aControllerPort, aName.c_str());
4461
4462 /* Check for an existing controller. */
4463 ComObjPtr<StorageController> ctl;
4464 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4465 if (FAILED(rc)) return rc;
4466
4467 StorageControllerType_T ctrlType;
4468 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4469 if (FAILED(rc))
4470 return setError(E_FAIL,
4471 tr("Could not get type of controller '%s'"),
4472 aName.c_str());
4473
4474 if (!i_isControllerHotplugCapable(ctrlType))
4475 return setError(VBOX_E_NOT_SUPPORTED,
4476 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4477 aName.c_str());
4478
4479 /* silently ignore attempts to modify the hot-plug status of USB devices */
4480 if (ctrlType == StorageControllerType_USB)
4481 return S_OK;
4482
4483 i_setModified(IsModified_Storage);
4484 mMediumAttachments.backup();
4485
4486 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4487
4488 if (pAttach->i_getType() == DeviceType_Floppy)
4489 return setError(E_INVALIDARG,
4490 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"),
4491 aDevice, aControllerPort, aName.c_str());
4492 pAttach->i_updateHotPluggable(!!aHotPluggable);
4493
4494 return S_OK;
4495}
4496
4497HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4498 LONG aDevice)
4499{
4500 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4501 aName.c_str(), aControllerPort, aDevice));
4502
4503 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4504}
4505
4506HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4507 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4508{
4509 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4510 aName.c_str(), aControllerPort, aDevice));
4511
4512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4513
4514 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4515 if (FAILED(rc)) return rc;
4516
4517 if (Global::IsOnlineOrTransient(mData->mMachineState))
4518 return setError(VBOX_E_INVALID_VM_STATE,
4519 tr("Invalid machine state: %s"),
4520 Global::stringifyMachineState(mData->mMachineState));
4521
4522 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4523 aName,
4524 aControllerPort,
4525 aDevice);
4526 if (!pAttach)
4527 return setError(VBOX_E_OBJECT_NOT_FOUND,
4528 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4529 aDevice, aControllerPort, aName.c_str());
4530
4531
4532 i_setModified(IsModified_Storage);
4533 mMediumAttachments.backup();
4534
4535 IBandwidthGroup *iB = aBandwidthGroup;
4536 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4537 if (aBandwidthGroup && group.isNull())
4538 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4539
4540 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4541
4542 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4543 if (strBandwidthGroupOld.isNotEmpty())
4544 {
4545 /* Get the bandwidth group object and release it - this must not fail. */
4546 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4547 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4548 Assert(SUCCEEDED(rc));
4549
4550 pBandwidthGroupOld->i_release();
4551 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4552 }
4553
4554 if (!group.isNull())
4555 {
4556 group->i_reference();
4557 pAttach->i_updateBandwidthGroup(group->i_getName());
4558 }
4559
4560 return S_OK;
4561}
4562
4563HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4564 LONG aControllerPort,
4565 LONG aDevice,
4566 DeviceType_T aType)
4567{
4568 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4569 aName.c_str(), aControllerPort, aDevice, aType));
4570
4571 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4572}
4573
4574
4575HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4576 LONG aControllerPort,
4577 LONG aDevice,
4578 BOOL aForce)
4579{
4580 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4581 aName.c_str(), aControllerPort, aForce));
4582
4583 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4584}
4585
4586HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4587 LONG aControllerPort,
4588 LONG aDevice,
4589 const ComPtr<IMedium> &aMedium,
4590 BOOL aForce)
4591{
4592 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4593 aName.c_str(), aControllerPort, aDevice, aForce));
4594
4595 // request the host lock first, since might be calling Host methods for getting host drives;
4596 // next, protect the media tree all the while we're in here, as well as our member variables
4597 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4598 this->lockHandle(),
4599 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4600
4601 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4602 if (FAILED(hrc)) return hrc;
4603
4604 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4605 aName,
4606 aControllerPort,
4607 aDevice);
4608 if (pAttach.isNull())
4609 return setError(VBOX_E_OBJECT_NOT_FOUND,
4610 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4611 aDevice, aControllerPort, aName.c_str());
4612
4613 /* Remember previously mounted medium. The medium before taking the
4614 * backup is not necessarily the same thing. */
4615 ComObjPtr<Medium> oldmedium;
4616 oldmedium = pAttach->i_getMedium();
4617
4618 IMedium *iM = aMedium;
4619 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4620 if (aMedium && pMedium.isNull())
4621 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4622
4623 AutoCaller mediumCaller(pMedium);
4624 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4625
4626 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4627 if (pMedium)
4628 {
4629 DeviceType_T mediumType = pAttach->i_getType();
4630 switch (mediumType)
4631 {
4632 case DeviceType_DVD:
4633 case DeviceType_Floppy:
4634 break;
4635
4636 default:
4637 return setError(VBOX_E_INVALID_OBJECT_STATE,
4638 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4639 aControllerPort,
4640 aDevice,
4641 aName.c_str());
4642 }
4643 }
4644
4645 i_setModified(IsModified_Storage);
4646 mMediumAttachments.backup();
4647
4648 {
4649 // The backup operation makes the pAttach reference point to the
4650 // old settings. Re-get the correct reference.
4651 pAttach = i_findAttachment(*mMediumAttachments.data(),
4652 aName,
4653 aControllerPort,
4654 aDevice);
4655 if (!oldmedium.isNull())
4656 oldmedium->i_removeBackReference(mData->mUuid);
4657 if (!pMedium.isNull())
4658 {
4659 pMedium->i_addBackReference(mData->mUuid);
4660
4661 mediumLock.release();
4662 multiLock.release();
4663 i_addMediumToRegistry(pMedium);
4664 multiLock.acquire();
4665 mediumLock.acquire();
4666 }
4667
4668 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4669 pAttach->i_updateMedium(pMedium);
4670 }
4671
4672 i_setModified(IsModified_Storage);
4673
4674 mediumLock.release();
4675 multiLock.release();
4676 HRESULT rc = i_onMediumChange(pAttach, aForce);
4677 multiLock.acquire();
4678 mediumLock.acquire();
4679
4680 /* On error roll back this change only. */
4681 if (FAILED(rc))
4682 {
4683 if (!pMedium.isNull())
4684 pMedium->i_removeBackReference(mData->mUuid);
4685 pAttach = i_findAttachment(*mMediumAttachments.data(),
4686 aName,
4687 aControllerPort,
4688 aDevice);
4689 /* If the attachment is gone in the meantime, bail out. */
4690 if (pAttach.isNull())
4691 return rc;
4692 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4693 if (!oldmedium.isNull())
4694 oldmedium->i_addBackReference(mData->mUuid);
4695 pAttach->i_updateMedium(oldmedium);
4696 }
4697
4698 mediumLock.release();
4699 multiLock.release();
4700
4701 /* Save modified registries, but skip this machine as it's the caller's
4702 * job to save its settings like all other settings changes. */
4703 mParent->i_unmarkRegistryModified(i_getId());
4704 mParent->i_saveModifiedRegistries();
4705
4706 return rc;
4707}
4708HRESULT Machine::getMedium(const com::Utf8Str &aName,
4709 LONG aControllerPort,
4710 LONG aDevice,
4711 ComPtr<IMedium> &aMedium)
4712{
4713 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4714 aName.c_str(), aControllerPort, aDevice));
4715
4716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4717
4718 aMedium = NULL;
4719
4720 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4721 aName,
4722 aControllerPort,
4723 aDevice);
4724 if (pAttach.isNull())
4725 return setError(VBOX_E_OBJECT_NOT_FOUND,
4726 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4727 aDevice, aControllerPort, aName.c_str());
4728
4729 aMedium = pAttach->i_getMedium();
4730
4731 return S_OK;
4732}
4733
4734HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4735{
4736 if (aSlot < RT_ELEMENTS(mSerialPorts))
4737 {
4738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4739 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4740 return S_OK;
4741 }
4742 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4743}
4744
4745HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4746{
4747 if (aSlot < RT_ELEMENTS(mParallelPorts))
4748 {
4749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4750 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4751 return S_OK;
4752 }
4753 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4754}
4755
4756
4757HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4758{
4759 /* Do not assert if slot is out of range, just return the advertised
4760 status. testdriver/vbox.py triggers this in logVmInfo. */
4761 if (aSlot >= mNetworkAdapters.size())
4762 return setError(E_INVALIDARG,
4763 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4764 aSlot, mNetworkAdapters.size());
4765
4766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4767
4768 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4769
4770 return S_OK;
4771}
4772
4773HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4774{
4775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4776
4777 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4778 size_t i = 0;
4779 for (settings::StringsMap::const_iterator
4780 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4781 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4782 ++it, ++i)
4783 aKeys[i] = it->first;
4784
4785 return S_OK;
4786}
4787
4788 /**
4789 * @note Locks this object for reading.
4790 */
4791HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4792 com::Utf8Str &aValue)
4793{
4794 /* start with nothing found */
4795 aValue = "";
4796
4797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4798
4799 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4800 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4801 // found:
4802 aValue = it->second; // source is a Utf8Str
4803
4804 /* return the result to caller (may be empty) */
4805 return S_OK;
4806}
4807
4808 /**
4809 * @note Locks mParent for writing + this object for writing.
4810 */
4811HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4812{
4813 /* Because control characters in aKey have caused problems in the settings
4814 * they are rejected unless the key should be deleted. */
4815 if (!aValue.isEmpty())
4816 {
4817 for (size_t i = 0; i < aKey.length(); ++i)
4818 {
4819 char ch = aKey[i];
4820 if (RTLocCIsCntrl(ch))
4821 return E_INVALIDARG;
4822 }
4823 }
4824
4825 Utf8Str strOldValue; // empty
4826
4827 // locking note: we only hold the read lock briefly to look up the old value,
4828 // then release it and call the onExtraCanChange callbacks. There is a small
4829 // chance of a race insofar as the callback might be called twice if two callers
4830 // change the same key at the same time, but that's a much better solution
4831 // than the deadlock we had here before. The actual changing of the extradata
4832 // is then performed under the write lock and race-free.
4833
4834 // look up the old value first; if nothing has changed then we need not do anything
4835 {
4836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4837
4838 // For snapshots don't even think about allowing changes, extradata
4839 // is global for a machine, so there is nothing snapshot specific.
4840 if (i_isSnapshotMachine())
4841 return setError(VBOX_E_INVALID_VM_STATE,
4842 tr("Cannot set extradata for a snapshot"));
4843
4844 // check if the right IMachine instance is used
4845 if (mData->mRegistered && !i_isSessionMachine())
4846 return setError(VBOX_E_INVALID_VM_STATE,
4847 tr("Cannot set extradata for an immutable machine"));
4848
4849 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4850 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4851 strOldValue = it->second;
4852 }
4853
4854 bool fChanged;
4855 if ((fChanged = (strOldValue != aValue)))
4856 {
4857 // ask for permission from all listeners outside the locks;
4858 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4859 // lock to copy the list of callbacks to invoke
4860 Bstr bstrError;
4861 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4862 {
4863 const char *sep = bstrError.isEmpty() ? "" : ": ";
4864 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4865 return setError(E_ACCESSDENIED,
4866 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4867 aKey.c_str(),
4868 aValue.c_str(),
4869 sep,
4870 bstrError.raw());
4871 }
4872
4873 // data is changing and change not vetoed: then write it out under the lock
4874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4875
4876 if (aValue.isEmpty())
4877 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4878 else
4879 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4880 // creates a new key if needed
4881
4882 bool fNeedsGlobalSaveSettings = false;
4883 // This saving of settings is tricky: there is no "old state" for the
4884 // extradata items at all (unlike all other settings), so the old/new
4885 // settings comparison would give a wrong result!
4886 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4887
4888 if (fNeedsGlobalSaveSettings)
4889 {
4890 // save the global settings; for that we should hold only the VirtualBox lock
4891 alock.release();
4892 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4893 mParent->i_saveSettings();
4894 }
4895 }
4896
4897 // fire notification outside the lock
4898 if (fChanged)
4899 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4900
4901 return S_OK;
4902}
4903
4904HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4905{
4906 aProgress = NULL;
4907 NOREF(aSettingsFilePath);
4908 ReturnComNotImplemented();
4909}
4910
4911HRESULT Machine::saveSettings()
4912{
4913 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4914
4915 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4916 if (FAILED(rc)) return rc;
4917
4918 /* the settings file path may never be null */
4919 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4920
4921 /* save all VM data excluding snapshots */
4922 bool fNeedsGlobalSaveSettings = false;
4923 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4924 mlock.release();
4925
4926 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4927 {
4928 // save the global settings; for that we should hold only the VirtualBox lock
4929 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4930 rc = mParent->i_saveSettings();
4931 }
4932
4933 return rc;
4934}
4935
4936
4937HRESULT Machine::discardSettings()
4938{
4939 /*
4940 * We need to take the machine list lock here as well as the machine one
4941 * or we'll get into trouble should any media stuff require rolling back.
4942 *
4943 * Details:
4944 *
4945 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4946 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4947 * 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]
4948 * 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
4949 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4950 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4951 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4952 * 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
4953 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4954 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4955 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4956 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4957 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4958 * 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]
4959 * 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] (*)
4960 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4961 * 0:005> k
4962 * # Child-SP RetAddr Call Site
4963 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4964 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4965 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4966 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4967 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4968 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4969 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4970 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4971 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4972 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4973 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4974 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4975 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4976 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4977 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4978 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4979 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4980 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4981 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4982 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4983 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4984 *
4985 */
4986 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4990 if (FAILED(rc)) return rc;
4991
4992 /*
4993 * during this rollback, the session will be notified if data has
4994 * been actually changed
4995 */
4996 i_rollback(true /* aNotify */);
4997
4998 return S_OK;
4999}
5000
5001/** @note Locks objects! */
5002HRESULT Machine::unregister(AutoCaller &autoCaller,
5003 CleanupMode_T aCleanupMode,
5004 std::vector<ComPtr<IMedium> > &aMedia)
5005{
5006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5007
5008 Guid id(i_getId());
5009
5010 if (mData->mSession.mState != SessionState_Unlocked)
5011 return setError(VBOX_E_INVALID_OBJECT_STATE,
5012 tr("Cannot unregister the machine '%s' while it is locked"),
5013 mUserData->s.strName.c_str());
5014
5015 // wait for state dependents to drop to zero
5016 i_ensureNoStateDependencies(alock);
5017
5018 if (!mData->mAccessible)
5019 {
5020 // inaccessible machines can only be unregistered; uninitialize ourselves
5021 // here because currently there may be no unregistered that are inaccessible
5022 // (this state combination is not supported). Note releasing the caller and
5023 // leaving the lock before calling uninit()
5024 alock.release();
5025 autoCaller.release();
5026
5027 uninit();
5028
5029 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5030 // calls VirtualBox::i_saveSettings()
5031
5032 return S_OK;
5033 }
5034
5035 HRESULT rc = S_OK;
5036 mData->llFilesToDelete.clear();
5037
5038 if (!mSSData->strStateFilePath.isEmpty())
5039 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5040
5041 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5042 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5043 mData->llFilesToDelete.push_back(strNVRAMFile);
5044
5045 // This list collects the medium objects from all medium attachments
5046 // which we will detach from the machine and its snapshots, in a specific
5047 // order which allows for closing all media without getting "media in use"
5048 // errors, simply by going through the list from the front to the back:
5049 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5050 // and must be closed before the parent media from the snapshots, or closing the parents
5051 // will fail because they still have children);
5052 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5053 // the root ("first") snapshot of the machine.
5054 MediaList llMedia;
5055
5056 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5057 && mMediumAttachments->size()
5058 )
5059 {
5060 // we have media attachments: detach them all and add the Medium objects to our list
5061 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5062 }
5063
5064 if (mData->mFirstSnapshot)
5065 {
5066 // add the media from the medium attachments of the snapshots to
5067 // llMedia as well, after the "main" machine media;
5068 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5069 // snapshot machine, depth first.
5070
5071 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5072 MachineState_T oldState = mData->mMachineState;
5073 mData->mMachineState = MachineState_DeletingSnapshot;
5074
5075 // make a copy of the first snapshot reference so the refcount does not
5076 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5077 // (would hang due to the AutoCaller voodoo)
5078 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5079
5080 // GO!
5081 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5082
5083 mData->mMachineState = oldState;
5084 }
5085
5086 if (FAILED(rc))
5087 {
5088 i_rollbackMedia();
5089 return rc;
5090 }
5091
5092 // commit all the media changes made above
5093 i_commitMedia();
5094
5095 mData->mRegistered = false;
5096
5097 // machine lock no longer needed
5098 alock.release();
5099
5100 /* Make sure that the settings of the current VM are not saved, because
5101 * they are rather crippled at this point to meet the cleanup expectations
5102 * and there's no point destroying the VM config on disk just because. */
5103 mParent->i_unmarkRegistryModified(id);
5104
5105 // return media to caller
5106 aMedia.resize(llMedia.size());
5107 size_t i = 0;
5108 for (MediaList::const_iterator
5109 it = llMedia.begin();
5110 it != llMedia.end();
5111 ++it, ++i)
5112 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5113
5114 mParent->i_unregisterMachine(this, aCleanupMode, id);
5115 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5116
5117 autoCaller.release();
5118 uninit();
5119
5120 return S_OK;
5121}
5122
5123/**
5124 * Task record for deleting a machine config.
5125 */
5126class Machine::DeleteConfigTask
5127 : public Machine::Task
5128{
5129public:
5130 DeleteConfigTask(Machine *m,
5131 Progress *p,
5132 const Utf8Str &t,
5133 const RTCList<ComPtr<IMedium> > &llMediums,
5134 const StringsList &llFilesToDelete)
5135 : Task(m, p, t),
5136 m_llMediums(llMediums),
5137 m_llFilesToDelete(llFilesToDelete)
5138 {}
5139
5140private:
5141 void handler()
5142 {
5143 try
5144 {
5145 m_pMachine->i_deleteConfigHandler(*this);
5146 }
5147 catch (...)
5148 {
5149 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5150 }
5151 }
5152
5153 RTCList<ComPtr<IMedium> > m_llMediums;
5154 StringsList m_llFilesToDelete;
5155
5156 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5157};
5158
5159/**
5160 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5161 * SessionMachine::taskHandler().
5162 *
5163 * @note Locks this object for writing.
5164 *
5165 * @param task
5166 * @return
5167 */
5168void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5169{
5170 LogFlowThisFuncEnter();
5171
5172 AutoCaller autoCaller(this);
5173 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5174 if (FAILED(autoCaller.rc()))
5175 {
5176 /* we might have been uninitialized because the session was accidentally
5177 * closed by the client, so don't assert */
5178 HRESULT rc = setError(E_FAIL,
5179 tr("The session has been accidentally closed"));
5180 task.m_pProgress->i_notifyComplete(rc);
5181 LogFlowThisFuncLeave();
5182 return;
5183 }
5184
5185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5186
5187 HRESULT rc = S_OK;
5188
5189 try
5190 {
5191 ULONG uLogHistoryCount = 3;
5192 ComPtr<ISystemProperties> systemProperties;
5193 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5194 if (FAILED(rc)) throw rc;
5195
5196 if (!systemProperties.isNull())
5197 {
5198 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5199 if (FAILED(rc)) throw rc;
5200 }
5201
5202 MachineState_T oldState = mData->mMachineState;
5203 i_setMachineState(MachineState_SettingUp);
5204 alock.release();
5205 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5206 {
5207 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5208 {
5209 AutoCaller mac(pMedium);
5210 if (FAILED(mac.rc())) throw mac.rc();
5211 Utf8Str strLocation = pMedium->i_getLocationFull();
5212 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5213 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5214 if (FAILED(rc)) throw rc;
5215 }
5216 if (pMedium->i_isMediumFormatFile())
5217 {
5218 ComPtr<IProgress> pProgress2;
5219 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5220 if (FAILED(rc)) throw rc;
5221 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5222 if (FAILED(rc)) throw rc;
5223 }
5224
5225 /* Close the medium, deliberately without checking the return
5226 * code, and without leaving any trace in the error info, as
5227 * a failure here is a very minor issue, which shouldn't happen
5228 * as above we even managed to delete the medium. */
5229 {
5230 ErrorInfoKeeper eik;
5231 pMedium->Close();
5232 }
5233 }
5234 i_setMachineState(oldState);
5235 alock.acquire();
5236
5237 // delete the files pushed on the task list by Machine::Delete()
5238 // (this includes saved states of the machine and snapshots and
5239 // medium storage files from the IMedium list passed in, and the
5240 // machine XML file)
5241 for (StringsList::const_iterator
5242 it = task.m_llFilesToDelete.begin();
5243 it != task.m_llFilesToDelete.end();
5244 ++it)
5245 {
5246 const Utf8Str &strFile = *it;
5247 LogFunc(("Deleting file %s\n", strFile.c_str()));
5248 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5249 if (FAILED(rc)) throw rc;
5250
5251 int vrc = RTFileDelete(strFile.c_str());
5252 if (RT_FAILURE(vrc))
5253 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5254 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5255 }
5256
5257 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5258 if (FAILED(rc)) throw rc;
5259
5260 /* delete the settings only when the file actually exists */
5261 if (mData->pMachineConfigFile->fileExists())
5262 {
5263 /* Delete any backup or uncommitted XML files. Ignore failures.
5264 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5265 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5266 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5267 RTFileDelete(otherXml.c_str());
5268 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5269 RTFileDelete(otherXml.c_str());
5270
5271 /* delete the Logs folder, nothing important should be left
5272 * there (we don't check for errors because the user might have
5273 * some private files there that we don't want to delete) */
5274 Utf8Str logFolder;
5275 getLogFolder(logFolder);
5276 Assert(logFolder.length());
5277 if (RTDirExists(logFolder.c_str()))
5278 {
5279 /* Delete all VBox.log[.N] files from the Logs folder
5280 * (this must be in sync with the rotation logic in
5281 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5282 * files that may have been created by the GUI. */
5283 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5284 RTFileDelete(log.c_str());
5285 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5286 RTFileDelete(log.c_str());
5287 for (ULONG i = uLogHistoryCount; i > 0; i--)
5288 {
5289 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5290 RTFileDelete(log.c_str());
5291 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5292 RTFileDelete(log.c_str());
5293 }
5294 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5295 RTFileDelete(log.c_str());
5296#if defined(RT_OS_WINDOWS)
5297 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5298 RTFileDelete(log.c_str());
5299 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5300 RTFileDelete(log.c_str());
5301#endif
5302
5303 RTDirRemove(logFolder.c_str());
5304 }
5305
5306 /* delete the Snapshots folder, nothing important should be left
5307 * there (we don't check for errors because the user might have
5308 * some private files there that we don't want to delete) */
5309 Utf8Str strFullSnapshotFolder;
5310 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5311 Assert(!strFullSnapshotFolder.isEmpty());
5312 if (RTDirExists(strFullSnapshotFolder.c_str()))
5313 RTDirRemove(strFullSnapshotFolder.c_str());
5314
5315 // delete the directory that contains the settings file, but only
5316 // if it matches the VM name
5317 Utf8Str settingsDir;
5318 if (i_isInOwnDir(&settingsDir))
5319 RTDirRemove(settingsDir.c_str());
5320 }
5321
5322 alock.release();
5323
5324 mParent->i_saveModifiedRegistries();
5325 }
5326 catch (HRESULT aRC) { rc = aRC; }
5327
5328 task.m_pProgress->i_notifyComplete(rc);
5329
5330 LogFlowThisFuncLeave();
5331}
5332
5333HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5334{
5335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5336
5337 HRESULT rc = i_checkStateDependency(MutableStateDep);
5338 if (FAILED(rc)) return rc;
5339
5340 if (mData->mRegistered)
5341 return setError(VBOX_E_INVALID_VM_STATE,
5342 tr("Cannot delete settings of a registered machine"));
5343
5344 // collect files to delete
5345 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5346 // machine config file
5347 if (mData->pMachineConfigFile->fileExists())
5348 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5349 // backup of machine config file
5350 Utf8Str strTmp(mData->m_strConfigFileFull);
5351 strTmp.append("-prev");
5352 if (RTFileExists(strTmp.c_str()))
5353 llFilesToDelete.push_back(strTmp);
5354
5355 RTCList<ComPtr<IMedium> > llMediums;
5356 for (size_t i = 0; i < aMedia.size(); ++i)
5357 {
5358 IMedium *pIMedium(aMedia[i]);
5359 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5360 if (pMedium.isNull())
5361 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5362 SafeArray<BSTR> ids;
5363 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5364 if (FAILED(rc)) return rc;
5365 /* At this point the medium should not have any back references
5366 * anymore. If it has it is attached to another VM and *must* not
5367 * deleted. */
5368 if (ids.size() < 1)
5369 llMediums.append(pMedium);
5370 }
5371
5372 ComObjPtr<Progress> pProgress;
5373 pProgress.createObject();
5374 rc = pProgress->init(i_getVirtualBox(),
5375 static_cast<IMachine*>(this) /* aInitiator */,
5376 tr("Deleting files"),
5377 true /* fCancellable */,
5378 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5379 tr("Collecting file inventory"));
5380 if (FAILED(rc))
5381 return rc;
5382
5383 /* create and start the task on a separate thread (note that it will not
5384 * start working until we release alock) */
5385 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5386 rc = pTask->createThread();
5387 pTask = NULL;
5388 if (FAILED(rc))
5389 return rc;
5390
5391 pProgress.queryInterfaceTo(aProgress.asOutParam());
5392
5393 LogFlowFuncLeave();
5394
5395 return S_OK;
5396}
5397
5398HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5399{
5400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 ComObjPtr<Snapshot> pSnapshot;
5403 HRESULT rc;
5404
5405 if (aNameOrId.isEmpty())
5406 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5407 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5408 else
5409 {
5410 Guid uuid(aNameOrId);
5411 if (uuid.isValid())
5412 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5413 else
5414 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5415 }
5416 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5417
5418 return rc;
5419}
5420
5421HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5422 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5423{
5424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5425
5426 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5427 if (FAILED(rc)) return rc;
5428
5429 ComObjPtr<SharedFolder> sharedFolder;
5430 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5431 if (SUCCEEDED(rc))
5432 return setError(VBOX_E_OBJECT_IN_USE,
5433 tr("Shared folder named '%s' already exists"),
5434 aName.c_str());
5435
5436 sharedFolder.createObject();
5437 rc = sharedFolder->init(i_getMachine(),
5438 aName,
5439 aHostPath,
5440 !!aWritable,
5441 !!aAutomount,
5442 aAutoMountPoint,
5443 true /* fFailOnError */);
5444 if (FAILED(rc)) return rc;
5445
5446 i_setModified(IsModified_SharedFolders);
5447 mHWData.backup();
5448 mHWData->mSharedFolders.push_back(sharedFolder);
5449
5450 /* inform the direct session if any */
5451 alock.release();
5452 i_onSharedFolderChange();
5453
5454 return S_OK;
5455}
5456
5457HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5458{
5459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5460
5461 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5462 if (FAILED(rc)) return rc;
5463
5464 ComObjPtr<SharedFolder> sharedFolder;
5465 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5466 if (FAILED(rc)) return rc;
5467
5468 i_setModified(IsModified_SharedFolders);
5469 mHWData.backup();
5470 mHWData->mSharedFolders.remove(sharedFolder);
5471
5472 /* inform the direct session if any */
5473 alock.release();
5474 i_onSharedFolderChange();
5475
5476 return S_OK;
5477}
5478
5479HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5480{
5481 /* start with No */
5482 *aCanShow = FALSE;
5483
5484 ComPtr<IInternalSessionControl> directControl;
5485 {
5486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5487
5488 if (mData->mSession.mState != SessionState_Locked)
5489 return setError(VBOX_E_INVALID_VM_STATE,
5490 tr("Machine is not locked for session (session state: %s)"),
5491 Global::stringifySessionState(mData->mSession.mState));
5492
5493 if (mData->mSession.mLockType == LockType_VM)
5494 directControl = mData->mSession.mDirectControl;
5495 }
5496
5497 /* ignore calls made after #OnSessionEnd() is called */
5498 if (!directControl)
5499 return S_OK;
5500
5501 LONG64 dummy;
5502 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5503}
5504
5505HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5506{
5507 ComPtr<IInternalSessionControl> directControl;
5508 {
5509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5510
5511 if (mData->mSession.mState != SessionState_Locked)
5512 return setError(E_FAIL,
5513 tr("Machine is not locked for session (session state: %s)"),
5514 Global::stringifySessionState(mData->mSession.mState));
5515
5516 if (mData->mSession.mLockType == LockType_VM)
5517 directControl = mData->mSession.mDirectControl;
5518 }
5519
5520 /* ignore calls made after #OnSessionEnd() is called */
5521 if (!directControl)
5522 return S_OK;
5523
5524 BOOL dummy;
5525 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5526}
5527
5528#ifdef VBOX_WITH_GUEST_PROPS
5529/**
5530 * Look up a guest property in VBoxSVC's internal structures.
5531 */
5532HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5533 com::Utf8Str &aValue,
5534 LONG64 *aTimestamp,
5535 com::Utf8Str &aFlags) const
5536{
5537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5538
5539 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5540 if (it != mHWData->mGuestProperties.end())
5541 {
5542 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5543 aValue = it->second.strValue;
5544 *aTimestamp = it->second.mTimestamp;
5545 GuestPropWriteFlags(it->second.mFlags, szFlags);
5546 aFlags = Utf8Str(szFlags);
5547 }
5548
5549 return S_OK;
5550}
5551
5552/**
5553 * Query the VM that a guest property belongs to for the property.
5554 * @returns E_ACCESSDENIED if the VM process is not available or not
5555 * currently handling queries and the lookup should then be done in
5556 * VBoxSVC.
5557 */
5558HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5559 com::Utf8Str &aValue,
5560 LONG64 *aTimestamp,
5561 com::Utf8Str &aFlags) const
5562{
5563 HRESULT rc = S_OK;
5564 Bstr bstrValue;
5565 Bstr bstrFlags;
5566
5567 ComPtr<IInternalSessionControl> directControl;
5568 {
5569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5570 if (mData->mSession.mLockType == LockType_VM)
5571 directControl = mData->mSession.mDirectControl;
5572 }
5573
5574 /* ignore calls made after #OnSessionEnd() is called */
5575 if (!directControl)
5576 rc = E_ACCESSDENIED;
5577 else
5578 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5579 0 /* accessMode */,
5580 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5581
5582 aValue = bstrValue;
5583 aFlags = bstrFlags;
5584
5585 return rc;
5586}
5587#endif // VBOX_WITH_GUEST_PROPS
5588
5589HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5590 com::Utf8Str &aValue,
5591 LONG64 *aTimestamp,
5592 com::Utf8Str &aFlags)
5593{
5594#ifndef VBOX_WITH_GUEST_PROPS
5595 ReturnComNotImplemented();
5596#else // VBOX_WITH_GUEST_PROPS
5597
5598 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5599
5600 if (rc == E_ACCESSDENIED)
5601 /* The VM is not running or the service is not (yet) accessible */
5602 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5603 return rc;
5604#endif // VBOX_WITH_GUEST_PROPS
5605}
5606
5607HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5608{
5609 LONG64 dummyTimestamp;
5610 com::Utf8Str dummyFlags;
5611 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5612 return rc;
5613
5614}
5615HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5616{
5617 com::Utf8Str dummyFlags;
5618 com::Utf8Str dummyValue;
5619 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5620 return rc;
5621}
5622
5623#ifdef VBOX_WITH_GUEST_PROPS
5624/**
5625 * Set a guest property in VBoxSVC's internal structures.
5626 */
5627HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5628 const com::Utf8Str &aFlags, bool fDelete)
5629{
5630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5631 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5632 if (FAILED(rc)) return rc;
5633
5634 try
5635 {
5636 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5637 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5638 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5639
5640 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5641 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5642
5643 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5644 if (it == mHWData->mGuestProperties.end())
5645 {
5646 if (!fDelete)
5647 {
5648 i_setModified(IsModified_MachineData);
5649 mHWData.backupEx();
5650
5651 RTTIMESPEC time;
5652 HWData::GuestProperty prop;
5653 prop.strValue = aValue;
5654 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5655 prop.mFlags = fFlags;
5656 mHWData->mGuestProperties[aName] = prop;
5657 }
5658 }
5659 else
5660 {
5661 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5662 {
5663 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5664 }
5665 else
5666 {
5667 i_setModified(IsModified_MachineData);
5668 mHWData.backupEx();
5669
5670 /* The backupEx() operation invalidates our iterator,
5671 * so get a new one. */
5672 it = mHWData->mGuestProperties.find(aName);
5673 Assert(it != mHWData->mGuestProperties.end());
5674
5675 if (!fDelete)
5676 {
5677 RTTIMESPEC time;
5678 it->second.strValue = aValue;
5679 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5680 it->second.mFlags = fFlags;
5681 }
5682 else
5683 mHWData->mGuestProperties.erase(it);
5684 }
5685 }
5686
5687 if (SUCCEEDED(rc))
5688 {
5689 alock.release();
5690
5691 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5692 }
5693 }
5694 catch (std::bad_alloc &)
5695 {
5696 rc = E_OUTOFMEMORY;
5697 }
5698
5699 return rc;
5700}
5701
5702/**
5703 * Set a property on the VM that that property belongs to.
5704 * @returns E_ACCESSDENIED if the VM process is not available or not
5705 * currently handling queries and the setting should then be done in
5706 * VBoxSVC.
5707 */
5708HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5709 const com::Utf8Str &aFlags, bool fDelete)
5710{
5711 HRESULT rc;
5712
5713 try
5714 {
5715 ComPtr<IInternalSessionControl> directControl;
5716 {
5717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5718 if (mData->mSession.mLockType == LockType_VM)
5719 directControl = mData->mSession.mDirectControl;
5720 }
5721
5722 Bstr dummy1; /* will not be changed (setter) */
5723 Bstr dummy2; /* will not be changed (setter) */
5724 LONG64 dummy64;
5725 if (!directControl)
5726 rc = E_ACCESSDENIED;
5727 else
5728 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5729 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5730 fDelete ? 2 : 1 /* accessMode */,
5731 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5732 }
5733 catch (std::bad_alloc &)
5734 {
5735 rc = E_OUTOFMEMORY;
5736 }
5737
5738 return rc;
5739}
5740#endif // VBOX_WITH_GUEST_PROPS
5741
5742HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5743 const com::Utf8Str &aFlags)
5744{
5745#ifndef VBOX_WITH_GUEST_PROPS
5746 ReturnComNotImplemented();
5747#else // VBOX_WITH_GUEST_PROPS
5748 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5749 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5750
5751 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5752 if (rc == E_ACCESSDENIED)
5753 /* The VM is not running or the service is not (yet) accessible */
5754 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5755 return rc;
5756#endif // VBOX_WITH_GUEST_PROPS
5757}
5758
5759HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5760{
5761 return setGuestProperty(aProperty, aValue, "");
5762}
5763
5764HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5765{
5766#ifndef VBOX_WITH_GUEST_PROPS
5767 ReturnComNotImplemented();
5768#else // VBOX_WITH_GUEST_PROPS
5769 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5770 if (rc == E_ACCESSDENIED)
5771 /* The VM is not running or the service is not (yet) accessible */
5772 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5773 return rc;
5774#endif // VBOX_WITH_GUEST_PROPS
5775}
5776
5777#ifdef VBOX_WITH_GUEST_PROPS
5778/**
5779 * Enumerate the guest properties in VBoxSVC's internal structures.
5780 */
5781HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5782 std::vector<com::Utf8Str> &aNames,
5783 std::vector<com::Utf8Str> &aValues,
5784 std::vector<LONG64> &aTimestamps,
5785 std::vector<com::Utf8Str> &aFlags)
5786{
5787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5788 Utf8Str strPatterns(aPatterns);
5789
5790 /*
5791 * Look for matching patterns and build up a list.
5792 */
5793 HWData::GuestPropertyMap propMap;
5794 for (HWData::GuestPropertyMap::const_iterator
5795 it = mHWData->mGuestProperties.begin();
5796 it != mHWData->mGuestProperties.end();
5797 ++it)
5798 {
5799 if ( strPatterns.isEmpty()
5800 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5801 RTSTR_MAX,
5802 it->first.c_str(),
5803 RTSTR_MAX,
5804 NULL)
5805 )
5806 propMap.insert(*it);
5807 }
5808
5809 alock.release();
5810
5811 /*
5812 * And build up the arrays for returning the property information.
5813 */
5814 size_t cEntries = propMap.size();
5815
5816 aNames.resize(cEntries);
5817 aValues.resize(cEntries);
5818 aTimestamps.resize(cEntries);
5819 aFlags.resize(cEntries);
5820
5821 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5822 size_t i = 0;
5823 for (HWData::GuestPropertyMap::const_iterator
5824 it = propMap.begin();
5825 it != propMap.end();
5826 ++it, ++i)
5827 {
5828 aNames[i] = it->first;
5829 aValues[i] = it->second.strValue;
5830 aTimestamps[i] = it->second.mTimestamp;
5831 GuestPropWriteFlags(it->second.mFlags, szFlags);
5832 aFlags[i] = Utf8Str(szFlags);
5833
5834 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5835 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
5836 }
5837
5838 return S_OK;
5839}
5840
5841/**
5842 * Enumerate the properties managed by a VM.
5843 * @returns E_ACCESSDENIED if the VM process is not available or not
5844 * currently handling queries and the setting should then be done in
5845 * VBoxSVC.
5846 */
5847HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5848 std::vector<com::Utf8Str> &aNames,
5849 std::vector<com::Utf8Str> &aValues,
5850 std::vector<LONG64> &aTimestamps,
5851 std::vector<com::Utf8Str> &aFlags)
5852{
5853 HRESULT rc;
5854 ComPtr<IInternalSessionControl> directControl;
5855 {
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857 if (mData->mSession.mLockType == LockType_VM)
5858 directControl = mData->mSession.mDirectControl;
5859 }
5860
5861 com::SafeArray<BSTR> bNames;
5862 com::SafeArray<BSTR> bValues;
5863 com::SafeArray<LONG64> bTimestamps;
5864 com::SafeArray<BSTR> bFlags;
5865
5866 if (!directControl)
5867 rc = E_ACCESSDENIED;
5868 else
5869 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5870 ComSafeArrayAsOutParam(bNames),
5871 ComSafeArrayAsOutParam(bValues),
5872 ComSafeArrayAsOutParam(bTimestamps),
5873 ComSafeArrayAsOutParam(bFlags));
5874 size_t i;
5875 aNames.resize(bNames.size());
5876 for (i = 0; i < bNames.size(); ++i)
5877 aNames[i] = Utf8Str(bNames[i]);
5878 aValues.resize(bValues.size());
5879 for (i = 0; i < bValues.size(); ++i)
5880 aValues[i] = Utf8Str(bValues[i]);
5881 aTimestamps.resize(bTimestamps.size());
5882 for (i = 0; i < bTimestamps.size(); ++i)
5883 aTimestamps[i] = bTimestamps[i];
5884 aFlags.resize(bFlags.size());
5885 for (i = 0; i < bFlags.size(); ++i)
5886 aFlags[i] = Utf8Str(bFlags[i]);
5887
5888 return rc;
5889}
5890#endif // VBOX_WITH_GUEST_PROPS
5891HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5892 std::vector<com::Utf8Str> &aNames,
5893 std::vector<com::Utf8Str> &aValues,
5894 std::vector<LONG64> &aTimestamps,
5895 std::vector<com::Utf8Str> &aFlags)
5896{
5897#ifndef VBOX_WITH_GUEST_PROPS
5898 ReturnComNotImplemented();
5899#else // VBOX_WITH_GUEST_PROPS
5900
5901 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5902
5903 if (rc == E_ACCESSDENIED)
5904 /* The VM is not running or the service is not (yet) accessible */
5905 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5906 return rc;
5907#endif // VBOX_WITH_GUEST_PROPS
5908}
5909
5910HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5911 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5912{
5913 MediumAttachmentList atts;
5914
5915 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5916 if (FAILED(rc)) return rc;
5917
5918 aMediumAttachments.resize(atts.size());
5919 size_t i = 0;
5920 for (MediumAttachmentList::const_iterator
5921 it = atts.begin();
5922 it != atts.end();
5923 ++it, ++i)
5924 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5925
5926 return S_OK;
5927}
5928
5929HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5930 LONG aControllerPort,
5931 LONG aDevice,
5932 ComPtr<IMediumAttachment> &aAttachment)
5933{
5934 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5935 aName.c_str(), aControllerPort, aDevice));
5936
5937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5938
5939 aAttachment = NULL;
5940
5941 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5942 aName,
5943 aControllerPort,
5944 aDevice);
5945 if (pAttach.isNull())
5946 return setError(VBOX_E_OBJECT_NOT_FOUND,
5947 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5948 aDevice, aControllerPort, aName.c_str());
5949
5950 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5951
5952 return S_OK;
5953}
5954
5955
5956HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5957 StorageBus_T aConnectionType,
5958 ComPtr<IStorageController> &aController)
5959{
5960 if ( (aConnectionType <= StorageBus_Null)
5961 || (aConnectionType > StorageBus_VirtioSCSI))
5962 return setError(E_INVALIDARG,
5963 tr("Invalid connection type: %d"),
5964 aConnectionType);
5965
5966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5967
5968 HRESULT rc = i_checkStateDependency(MutableStateDep);
5969 if (FAILED(rc)) return rc;
5970
5971 /* try to find one with the name first. */
5972 ComObjPtr<StorageController> ctrl;
5973
5974 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5975 if (SUCCEEDED(rc))
5976 return setError(VBOX_E_OBJECT_IN_USE,
5977 tr("Storage controller named '%s' already exists"),
5978 aName.c_str());
5979
5980 ctrl.createObject();
5981
5982 /* get a new instance number for the storage controller */
5983 ULONG ulInstance = 0;
5984 bool fBootable = true;
5985 for (StorageControllerList::const_iterator
5986 it = mStorageControllers->begin();
5987 it != mStorageControllers->end();
5988 ++it)
5989 {
5990 if ((*it)->i_getStorageBus() == aConnectionType)
5991 {
5992 ULONG ulCurInst = (*it)->i_getInstance();
5993
5994 if (ulCurInst >= ulInstance)
5995 ulInstance = ulCurInst + 1;
5996
5997 /* Only one controller of each type can be marked as bootable. */
5998 if ((*it)->i_getBootable())
5999 fBootable = false;
6000 }
6001 }
6002
6003 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6004 if (FAILED(rc)) return rc;
6005
6006 i_setModified(IsModified_Storage);
6007 mStorageControllers.backup();
6008 mStorageControllers->push_back(ctrl);
6009
6010 ctrl.queryInterfaceTo(aController.asOutParam());
6011
6012 /* inform the direct session if any */
6013 alock.release();
6014 i_onStorageControllerChange(i_getId(), aName);
6015
6016 return S_OK;
6017}
6018
6019HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6020 ComPtr<IStorageController> &aStorageController)
6021{
6022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6023
6024 ComObjPtr<StorageController> ctrl;
6025
6026 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6027 if (SUCCEEDED(rc))
6028 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6029
6030 return rc;
6031}
6032
6033HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6034 ULONG aInstance,
6035 ComPtr<IStorageController> &aStorageController)
6036{
6037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 for (StorageControllerList::const_iterator
6040 it = mStorageControllers->begin();
6041 it != mStorageControllers->end();
6042 ++it)
6043 {
6044 if ( (*it)->i_getStorageBus() == aConnectionType
6045 && (*it)->i_getInstance() == aInstance)
6046 {
6047 (*it).queryInterfaceTo(aStorageController.asOutParam());
6048 return S_OK;
6049 }
6050 }
6051
6052 return setError(VBOX_E_OBJECT_NOT_FOUND,
6053 tr("Could not find a storage controller with instance number '%lu'"),
6054 aInstance);
6055}
6056
6057HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6058{
6059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6060
6061 HRESULT rc = i_checkStateDependency(MutableStateDep);
6062 if (FAILED(rc)) return rc;
6063
6064 ComObjPtr<StorageController> ctrl;
6065
6066 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6067 if (SUCCEEDED(rc))
6068 {
6069 /* Ensure that only one controller of each type is marked as bootable. */
6070 if (aBootable == TRUE)
6071 {
6072 for (StorageControllerList::const_iterator
6073 it = mStorageControllers->begin();
6074 it != mStorageControllers->end();
6075 ++it)
6076 {
6077 ComObjPtr<StorageController> aCtrl = (*it);
6078
6079 if ( (aCtrl->i_getName() != aName)
6080 && aCtrl->i_getBootable() == TRUE
6081 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6082 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6083 {
6084 aCtrl->i_setBootable(FALSE);
6085 break;
6086 }
6087 }
6088 }
6089
6090 if (SUCCEEDED(rc))
6091 {
6092 ctrl->i_setBootable(aBootable);
6093 i_setModified(IsModified_Storage);
6094 }
6095 }
6096
6097 if (SUCCEEDED(rc))
6098 {
6099 /* inform the direct session if any */
6100 alock.release();
6101 i_onStorageControllerChange(i_getId(), aName);
6102 }
6103
6104 return rc;
6105}
6106
6107HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6108{
6109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6110
6111 HRESULT rc = i_checkStateDependency(MutableStateDep);
6112 if (FAILED(rc)) return rc;
6113
6114 ComObjPtr<StorageController> ctrl;
6115 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6116 if (FAILED(rc)) return rc;
6117
6118 MediumAttachmentList llDetachedAttachments;
6119 {
6120 /* find all attached devices to the appropriate storage controller and detach them all */
6121 // make a temporary list because detachDevice invalidates iterators into
6122 // mMediumAttachments
6123 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6124
6125 for (MediumAttachmentList::const_iterator
6126 it = llAttachments2.begin();
6127 it != llAttachments2.end();
6128 ++it)
6129 {
6130 MediumAttachment *pAttachTemp = *it;
6131
6132 AutoCaller localAutoCaller(pAttachTemp);
6133 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6134
6135 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6136
6137 if (pAttachTemp->i_getControllerName() == aName)
6138 {
6139 llDetachedAttachments.push_back(pAttachTemp);
6140 rc = i_detachDevice(pAttachTemp, alock, NULL);
6141 if (FAILED(rc)) return rc;
6142 }
6143 }
6144 }
6145
6146 /* send event about detached devices before removing parent controller */
6147 for (MediumAttachmentList::const_iterator
6148 it = llDetachedAttachments.begin();
6149 it != llDetachedAttachments.end();
6150 ++it)
6151 {
6152 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6153 }
6154
6155 /* We can remove it now. */
6156 i_setModified(IsModified_Storage);
6157 mStorageControllers.backup();
6158
6159 ctrl->i_unshare();
6160
6161 mStorageControllers->remove(ctrl);
6162
6163 /* inform the direct session if any */
6164 alock.release();
6165 i_onStorageControllerChange(i_getId(), aName);
6166
6167 return S_OK;
6168}
6169
6170HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6171 ComPtr<IUSBController> &aController)
6172{
6173 if ( (aType <= USBControllerType_Null)
6174 || (aType >= USBControllerType_Last))
6175 return setError(E_INVALIDARG,
6176 tr("Invalid USB controller type: %d"),
6177 aType);
6178
6179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6180
6181 HRESULT rc = i_checkStateDependency(MutableStateDep);
6182 if (FAILED(rc)) return rc;
6183
6184 /* try to find one with the same type first. */
6185 ComObjPtr<USBController> ctrl;
6186
6187 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6188 if (SUCCEEDED(rc))
6189 return setError(VBOX_E_OBJECT_IN_USE,
6190 tr("USB controller named '%s' already exists"),
6191 aName.c_str());
6192
6193 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6194 ULONG maxInstances;
6195 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6196 if (FAILED(rc))
6197 return rc;
6198
6199 ULONG cInstances = i_getUSBControllerCountByType(aType);
6200 if (cInstances >= maxInstances)
6201 return setError(E_INVALIDARG,
6202 tr("Too many USB controllers of this type"));
6203
6204 ctrl.createObject();
6205
6206 rc = ctrl->init(this, aName, aType);
6207 if (FAILED(rc)) return rc;
6208
6209 i_setModified(IsModified_USB);
6210 mUSBControllers.backup();
6211 mUSBControllers->push_back(ctrl);
6212
6213 ctrl.queryInterfaceTo(aController.asOutParam());
6214
6215 /* inform the direct session if any */
6216 alock.release();
6217 i_onUSBControllerChange();
6218
6219 return S_OK;
6220}
6221
6222HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6223{
6224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 ComObjPtr<USBController> ctrl;
6227
6228 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6229 if (SUCCEEDED(rc))
6230 ctrl.queryInterfaceTo(aController.asOutParam());
6231
6232 return rc;
6233}
6234
6235HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6236 ULONG *aControllers)
6237{
6238 if ( (aType <= USBControllerType_Null)
6239 || (aType >= USBControllerType_Last))
6240 return setError(E_INVALIDARG,
6241 tr("Invalid USB controller type: %d"),
6242 aType);
6243
6244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 ComObjPtr<USBController> ctrl;
6247
6248 *aControllers = i_getUSBControllerCountByType(aType);
6249
6250 return S_OK;
6251}
6252
6253HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6254{
6255
6256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6257
6258 HRESULT rc = i_checkStateDependency(MutableStateDep);
6259 if (FAILED(rc)) return rc;
6260
6261 ComObjPtr<USBController> ctrl;
6262 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6263 if (FAILED(rc)) return rc;
6264
6265 i_setModified(IsModified_USB);
6266 mUSBControllers.backup();
6267
6268 ctrl->i_unshare();
6269
6270 mUSBControllers->remove(ctrl);
6271
6272 /* inform the direct session if any */
6273 alock.release();
6274 i_onUSBControllerChange();
6275
6276 return S_OK;
6277}
6278
6279HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6280 ULONG *aOriginX,
6281 ULONG *aOriginY,
6282 ULONG *aWidth,
6283 ULONG *aHeight,
6284 BOOL *aEnabled)
6285{
6286 uint32_t u32OriginX= 0;
6287 uint32_t u32OriginY= 0;
6288 uint32_t u32Width = 0;
6289 uint32_t u32Height = 0;
6290 uint16_t u16Flags = 0;
6291
6292 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6293 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6294 if (RT_FAILURE(vrc))
6295 {
6296#ifdef RT_OS_WINDOWS
6297 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6298 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6299 * So just assign fEnable to TRUE again.
6300 * The right fix would be to change GUI API wrappers to make sure that parameters
6301 * are changed only if API succeeds.
6302 */
6303 *aEnabled = TRUE;
6304#endif
6305 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6306 tr("Saved guest size is not available (%Rrc)"),
6307 vrc);
6308 }
6309
6310 *aOriginX = u32OriginX;
6311 *aOriginY = u32OriginY;
6312 *aWidth = u32Width;
6313 *aHeight = u32Height;
6314 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6315
6316 return S_OK;
6317}
6318
6319HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6320 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6321{
6322 if (aScreenId != 0)
6323 return E_NOTIMPL;
6324
6325 if ( aBitmapFormat != BitmapFormat_BGR0
6326 && aBitmapFormat != BitmapFormat_BGRA
6327 && aBitmapFormat != BitmapFormat_RGBA
6328 && aBitmapFormat != BitmapFormat_PNG)
6329 return setError(E_NOTIMPL,
6330 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6331
6332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6333
6334 uint8_t *pu8Data = NULL;
6335 uint32_t cbData = 0;
6336 uint32_t u32Width = 0;
6337 uint32_t u32Height = 0;
6338
6339 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6340
6341 if (RT_FAILURE(vrc))
6342 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6343 tr("Saved thumbnail data is not available (%Rrc)"),
6344 vrc);
6345
6346 HRESULT hr = S_OK;
6347
6348 *aWidth = u32Width;
6349 *aHeight = u32Height;
6350
6351 if (cbData > 0)
6352 {
6353 /* Convert pixels to the format expected by the API caller. */
6354 if (aBitmapFormat == BitmapFormat_BGR0)
6355 {
6356 /* [0] B, [1] G, [2] R, [3] 0. */
6357 aData.resize(cbData);
6358 memcpy(&aData.front(), pu8Data, cbData);
6359 }
6360 else if (aBitmapFormat == BitmapFormat_BGRA)
6361 {
6362 /* [0] B, [1] G, [2] R, [3] A. */
6363 aData.resize(cbData);
6364 for (uint32_t i = 0; i < cbData; i += 4)
6365 {
6366 aData[i] = pu8Data[i];
6367 aData[i + 1] = pu8Data[i + 1];
6368 aData[i + 2] = pu8Data[i + 2];
6369 aData[i + 3] = 0xff;
6370 }
6371 }
6372 else if (aBitmapFormat == BitmapFormat_RGBA)
6373 {
6374 /* [0] R, [1] G, [2] B, [3] A. */
6375 aData.resize(cbData);
6376 for (uint32_t i = 0; i < cbData; i += 4)
6377 {
6378 aData[i] = pu8Data[i + 2];
6379 aData[i + 1] = pu8Data[i + 1];
6380 aData[i + 2] = pu8Data[i];
6381 aData[i + 3] = 0xff;
6382 }
6383 }
6384 else if (aBitmapFormat == BitmapFormat_PNG)
6385 {
6386 uint8_t *pu8PNG = NULL;
6387 uint32_t cbPNG = 0;
6388 uint32_t cxPNG = 0;
6389 uint32_t cyPNG = 0;
6390
6391 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6392
6393 if (RT_SUCCESS(vrc))
6394 {
6395 aData.resize(cbPNG);
6396 if (cbPNG)
6397 memcpy(&aData.front(), pu8PNG, cbPNG);
6398 }
6399 else
6400 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6401 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6402 vrc);
6403
6404 RTMemFree(pu8PNG);
6405 }
6406 }
6407
6408 freeSavedDisplayScreenshot(pu8Data);
6409
6410 return hr;
6411}
6412
6413HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6414 ULONG *aWidth,
6415 ULONG *aHeight,
6416 std::vector<BitmapFormat_T> &aBitmapFormats)
6417{
6418 if (aScreenId != 0)
6419 return E_NOTIMPL;
6420
6421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6422
6423 uint8_t *pu8Data = NULL;
6424 uint32_t cbData = 0;
6425 uint32_t u32Width = 0;
6426 uint32_t u32Height = 0;
6427
6428 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6429
6430 if (RT_FAILURE(vrc))
6431 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6432 tr("Saved screenshot data is not available (%Rrc)"),
6433 vrc);
6434
6435 *aWidth = u32Width;
6436 *aHeight = u32Height;
6437 aBitmapFormats.resize(1);
6438 aBitmapFormats[0] = BitmapFormat_PNG;
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return S_OK;
6443}
6444
6445HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6446 BitmapFormat_T aBitmapFormat,
6447 ULONG *aWidth,
6448 ULONG *aHeight,
6449 std::vector<BYTE> &aData)
6450{
6451 if (aScreenId != 0)
6452 return E_NOTIMPL;
6453
6454 if (aBitmapFormat != BitmapFormat_PNG)
6455 return E_NOTIMPL;
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 uint8_t *pu8Data = NULL;
6460 uint32_t cbData = 0;
6461 uint32_t u32Width = 0;
6462 uint32_t u32Height = 0;
6463
6464 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6465
6466 if (RT_FAILURE(vrc))
6467 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6468 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6469 vrc);
6470
6471 *aWidth = u32Width;
6472 *aHeight = u32Height;
6473
6474 aData.resize(cbData);
6475 if (cbData)
6476 memcpy(&aData.front(), pu8Data, cbData);
6477
6478 freeSavedDisplayScreenshot(pu8Data);
6479
6480 return S_OK;
6481}
6482
6483HRESULT Machine::hotPlugCPU(ULONG aCpu)
6484{
6485 HRESULT rc = S_OK;
6486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6487
6488 if (!mHWData->mCPUHotPlugEnabled)
6489 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6490
6491 if (aCpu >= mHWData->mCPUCount)
6492 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6493
6494 if (mHWData->mCPUAttached[aCpu])
6495 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6496
6497 rc = i_checkStateDependency(MutableOrRunningStateDep);
6498 if (FAILED(rc)) return rc;
6499
6500 alock.release();
6501 rc = i_onCPUChange(aCpu, false);
6502 alock.acquire();
6503 if (FAILED(rc)) return rc;
6504
6505 i_setModified(IsModified_MachineData);
6506 mHWData.backup();
6507 mHWData->mCPUAttached[aCpu] = true;
6508
6509 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6510 if (Global::IsOnline(mData->mMachineState))
6511 i_saveSettings(NULL, alock);
6512
6513 return S_OK;
6514}
6515
6516HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6517{
6518 HRESULT rc = S_OK;
6519
6520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6521
6522 if (!mHWData->mCPUHotPlugEnabled)
6523 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6524
6525 if (aCpu >= SchemaDefs::MaxCPUCount)
6526 return setError(E_INVALIDARG,
6527 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6528 SchemaDefs::MaxCPUCount);
6529
6530 if (!mHWData->mCPUAttached[aCpu])
6531 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6532
6533 /* CPU 0 can't be detached */
6534 if (aCpu == 0)
6535 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6536
6537 rc = i_checkStateDependency(MutableOrRunningStateDep);
6538 if (FAILED(rc)) return rc;
6539
6540 alock.release();
6541 rc = i_onCPUChange(aCpu, true);
6542 alock.acquire();
6543 if (FAILED(rc)) return rc;
6544
6545 i_setModified(IsModified_MachineData);
6546 mHWData.backup();
6547 mHWData->mCPUAttached[aCpu] = false;
6548
6549 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6550 if (Global::IsOnline(mData->mMachineState))
6551 i_saveSettings(NULL, alock);
6552
6553 return S_OK;
6554}
6555
6556HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6557{
6558 *aAttached = false;
6559
6560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6561
6562 /* If hotplug is enabled the CPU is always enabled. */
6563 if (!mHWData->mCPUHotPlugEnabled)
6564 {
6565 if (aCpu < mHWData->mCPUCount)
6566 *aAttached = true;
6567 }
6568 else
6569 {
6570 if (aCpu < SchemaDefs::MaxCPUCount)
6571 *aAttached = mHWData->mCPUAttached[aCpu];
6572 }
6573
6574 return S_OK;
6575}
6576
6577HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6578{
6579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6580
6581 Utf8Str log = i_getLogFilename(aIdx);
6582 if (!RTFileExists(log.c_str()))
6583 log.setNull();
6584 aFilename = log;
6585
6586 return S_OK;
6587}
6588
6589HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6590{
6591 if (aSize < 0)
6592 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6593
6594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6595
6596 HRESULT rc = S_OK;
6597 Utf8Str log = i_getLogFilename(aIdx);
6598
6599 /* do not unnecessarily hold the lock while doing something which does
6600 * not need the lock and potentially takes a long time. */
6601 alock.release();
6602
6603 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6604 * keeps the SOAP reply size under 1M for the webservice (we're using
6605 * base64 encoded strings for binary data for years now, avoiding the
6606 * expansion of each byte array element to approx. 25 bytes of XML. */
6607 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6608 aData.resize(cbData);
6609
6610 RTFILE LogFile;
6611 int vrc = RTFileOpen(&LogFile, log.c_str(),
6612 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6613 if (RT_SUCCESS(vrc))
6614 {
6615 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6616 if (RT_SUCCESS(vrc))
6617 aData.resize(cbData);
6618 else
6619 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6620 tr("Could not read log file '%s' (%Rrc)"),
6621 log.c_str(), vrc);
6622 RTFileClose(LogFile);
6623 }
6624 else
6625 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6626 tr("Could not open log file '%s' (%Rrc)"),
6627 log.c_str(), vrc);
6628
6629 if (FAILED(rc))
6630 aData.resize(0);
6631
6632 return rc;
6633}
6634
6635
6636/**
6637 * Currently this method doesn't attach device to the running VM,
6638 * just makes sure it's plugged on next VM start.
6639 */
6640HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6641{
6642 // lock scope
6643 {
6644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6645
6646 HRESULT rc = i_checkStateDependency(MutableStateDep);
6647 if (FAILED(rc)) return rc;
6648
6649 ChipsetType_T aChipset = ChipsetType_PIIX3;
6650 COMGETTER(ChipsetType)(&aChipset);
6651
6652 if (aChipset != ChipsetType_ICH9)
6653 {
6654 return setError(E_INVALIDARG,
6655 tr("Host PCI attachment only supported with ICH9 chipset"));
6656 }
6657
6658 // check if device with this host PCI address already attached
6659 for (HWData::PCIDeviceAssignmentList::const_iterator
6660 it = mHWData->mPCIDeviceAssignments.begin();
6661 it != mHWData->mPCIDeviceAssignments.end();
6662 ++it)
6663 {
6664 LONG iHostAddress = -1;
6665 ComPtr<PCIDeviceAttachment> pAttach;
6666 pAttach = *it;
6667 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6668 if (iHostAddress == aHostAddress)
6669 return setError(E_INVALIDARG,
6670 tr("Device with host PCI address already attached to this VM"));
6671 }
6672
6673 ComObjPtr<PCIDeviceAttachment> pda;
6674 char name[32];
6675
6676 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6677 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6678 pda.createObject();
6679 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6680 i_setModified(IsModified_MachineData);
6681 mHWData.backup();
6682 mHWData->mPCIDeviceAssignments.push_back(pda);
6683 }
6684
6685 return S_OK;
6686}
6687
6688/**
6689 * Currently this method doesn't detach device from the running VM,
6690 * just makes sure it's not plugged on next VM start.
6691 */
6692HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6693{
6694 ComObjPtr<PCIDeviceAttachment> pAttach;
6695 bool fRemoved = false;
6696 HRESULT rc;
6697
6698 // lock scope
6699 {
6700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6701
6702 rc = i_checkStateDependency(MutableStateDep);
6703 if (FAILED(rc)) return rc;
6704
6705 for (HWData::PCIDeviceAssignmentList::const_iterator
6706 it = mHWData->mPCIDeviceAssignments.begin();
6707 it != mHWData->mPCIDeviceAssignments.end();
6708 ++it)
6709 {
6710 LONG iHostAddress = -1;
6711 pAttach = *it;
6712 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6713 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6714 {
6715 i_setModified(IsModified_MachineData);
6716 mHWData.backup();
6717 mHWData->mPCIDeviceAssignments.remove(pAttach);
6718 fRemoved = true;
6719 break;
6720 }
6721 }
6722 }
6723
6724
6725 /* Fire event outside of the lock */
6726 if (fRemoved)
6727 {
6728 Assert(!pAttach.isNull());
6729 ComPtr<IEventSource> es;
6730 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6731 Assert(SUCCEEDED(rc));
6732 Bstr mid;
6733 rc = this->COMGETTER(Id)(mid.asOutParam());
6734 Assert(SUCCEEDED(rc));
6735 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6736 }
6737
6738 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6739 tr("No host PCI device %08x attached"),
6740 aHostAddress
6741 );
6742}
6743
6744HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6745{
6746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6747
6748 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6749 size_t i = 0;
6750 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6751 it = mHWData->mPCIDeviceAssignments.begin();
6752 it != mHWData->mPCIDeviceAssignments.end();
6753 ++it, ++i)
6754 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6755
6756 return S_OK;
6757}
6758
6759HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6760{
6761 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6762
6763 return S_OK;
6764}
6765
6766HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6767{
6768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6769
6770 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6771
6772 return S_OK;
6773}
6774
6775HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6776{
6777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6778 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6779 if (SUCCEEDED(hrc))
6780 {
6781 hrc = mHWData.backupEx();
6782 if (SUCCEEDED(hrc))
6783 {
6784 i_setModified(IsModified_MachineData);
6785 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6786 }
6787 }
6788 return hrc;
6789}
6790
6791HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6792{
6793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6794 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6795 return S_OK;
6796}
6797
6798HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6799{
6800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6802 if (SUCCEEDED(hrc))
6803 {
6804 hrc = mHWData.backupEx();
6805 if (SUCCEEDED(hrc))
6806 {
6807 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6808 if (SUCCEEDED(hrc))
6809 i_setModified(IsModified_MachineData);
6810 }
6811 }
6812 return hrc;
6813}
6814
6815HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6816{
6817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6818
6819 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6820
6821 return S_OK;
6822}
6823
6824HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6825{
6826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6827 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6828 if (SUCCEEDED(hrc))
6829 {
6830 hrc = mHWData.backupEx();
6831 if (SUCCEEDED(hrc))
6832 {
6833 i_setModified(IsModified_MachineData);
6834 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6835 }
6836 }
6837 return hrc;
6838}
6839
6840HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6841{
6842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6843
6844 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6845
6846 return S_OK;
6847}
6848
6849HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6850{
6851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6852
6853 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6854 if ( SUCCEEDED(hrc)
6855 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6856 {
6857 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6858 int vrc;
6859
6860 if (aAutostartEnabled)
6861 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6862 else
6863 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6864
6865 if (RT_SUCCESS(vrc))
6866 {
6867 hrc = mHWData.backupEx();
6868 if (SUCCEEDED(hrc))
6869 {
6870 i_setModified(IsModified_MachineData);
6871 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6872 }
6873 }
6874 else if (vrc == VERR_NOT_SUPPORTED)
6875 hrc = setError(VBOX_E_NOT_SUPPORTED,
6876 tr("The VM autostart feature is not supported on this platform"));
6877 else if (vrc == VERR_PATH_NOT_FOUND)
6878 hrc = setError(E_FAIL,
6879 tr("The path to the autostart database is not set"));
6880 else
6881 hrc = setError(E_UNEXPECTED,
6882 aAutostartEnabled ?
6883 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6884 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6885 mUserData->s.strName.c_str(), vrc);
6886 }
6887 return hrc;
6888}
6889
6890HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6891{
6892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6893
6894 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6895
6896 return S_OK;
6897}
6898
6899HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6900{
6901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6902 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6903 if (SUCCEEDED(hrc))
6904 {
6905 hrc = mHWData.backupEx();
6906 if (SUCCEEDED(hrc))
6907 {
6908 i_setModified(IsModified_MachineData);
6909 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6910 }
6911 }
6912 return hrc;
6913}
6914
6915HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6916{
6917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6918
6919 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6920
6921 return S_OK;
6922}
6923
6924HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6925{
6926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6927 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6928 if ( SUCCEEDED(hrc)
6929 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6930 {
6931 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6932 int vrc;
6933
6934 if (aAutostopType != AutostopType_Disabled)
6935 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6936 else
6937 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6938
6939 if (RT_SUCCESS(vrc))
6940 {
6941 hrc = mHWData.backupEx();
6942 if (SUCCEEDED(hrc))
6943 {
6944 i_setModified(IsModified_MachineData);
6945 mHWData->mAutostart.enmAutostopType = aAutostopType;
6946 }
6947 }
6948 else if (vrc == VERR_NOT_SUPPORTED)
6949 hrc = setError(VBOX_E_NOT_SUPPORTED,
6950 tr("The VM autostop feature is not supported on this platform"));
6951 else if (vrc == VERR_PATH_NOT_FOUND)
6952 hrc = setError(E_FAIL,
6953 tr("The path to the autostart database is not set"));
6954 else
6955 hrc = setError(E_UNEXPECTED,
6956 aAutostopType != AutostopType_Disabled ?
6957 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6958 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6959 mUserData->s.strName.c_str(), vrc);
6960 }
6961 return hrc;
6962}
6963
6964HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6965{
6966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6967
6968 aDefaultFrontend = mHWData->mDefaultFrontend;
6969
6970 return S_OK;
6971}
6972
6973HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6974{
6975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6976 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6977 if (SUCCEEDED(hrc))
6978 {
6979 hrc = mHWData.backupEx();
6980 if (SUCCEEDED(hrc))
6981 {
6982 i_setModified(IsModified_MachineData);
6983 mHWData->mDefaultFrontend = aDefaultFrontend;
6984 }
6985 }
6986 return hrc;
6987}
6988
6989HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6990{
6991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6992 size_t cbIcon = mUserData->s.ovIcon.size();
6993 aIcon.resize(cbIcon);
6994 if (cbIcon)
6995 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6996 return S_OK;
6997}
6998
6999HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7000{
7001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7002 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7003 if (SUCCEEDED(hrc))
7004 {
7005 i_setModified(IsModified_MachineData);
7006 mUserData.backup();
7007 size_t cbIcon = aIcon.size();
7008 mUserData->s.ovIcon.resize(cbIcon);
7009 if (cbIcon)
7010 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7011 }
7012 return hrc;
7013}
7014
7015HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7016{
7017#ifdef VBOX_WITH_USB
7018 *aUSBProxyAvailable = true;
7019#else
7020 *aUSBProxyAvailable = false;
7021#endif
7022 return S_OK;
7023}
7024
7025HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7026{
7027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7028
7029 *aVMProcessPriority = mUserData->s.enmVMPriority;
7030
7031 return S_OK;
7032}
7033
7034HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7035{
7036 RT_NOREF(aVMProcessPriority);
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7039 if (SUCCEEDED(hrc))
7040 {
7041 hrc = mUserData.backupEx();
7042 if (SUCCEEDED(hrc))
7043 {
7044 i_setModified(IsModified_MachineData);
7045 mUserData->s.enmVMPriority = aVMProcessPriority;
7046 }
7047 }
7048 alock.release();
7049 if (SUCCEEDED(hrc))
7050 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7051 return hrc;
7052}
7053
7054HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7055 ComPtr<IProgress> &aProgress)
7056{
7057 ComObjPtr<Progress> pP;
7058 Progress *ppP = pP;
7059 IProgress *iP = static_cast<IProgress *>(ppP);
7060 IProgress **pProgress = &iP;
7061
7062 IMachine *pTarget = aTarget;
7063
7064 /* Convert the options. */
7065 RTCList<CloneOptions_T> optList;
7066 if (aOptions.size())
7067 for (size_t i = 0; i < aOptions.size(); ++i)
7068 optList.append(aOptions[i]);
7069
7070 if (optList.contains(CloneOptions_Link))
7071 {
7072 if (!i_isSnapshotMachine())
7073 return setError(E_INVALIDARG,
7074 tr("Linked clone can only be created from a snapshot"));
7075 if (aMode != CloneMode_MachineState)
7076 return setError(E_INVALIDARG,
7077 tr("Linked clone can only be created for a single machine state"));
7078 }
7079 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7080
7081 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7082
7083 HRESULT rc = pWorker->start(pProgress);
7084
7085 pP = static_cast<Progress *>(*pProgress);
7086 pP.queryInterfaceTo(aProgress.asOutParam());
7087
7088 return rc;
7089
7090}
7091
7092HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7093 const com::Utf8Str &aType,
7094 ComPtr<IProgress> &aProgress)
7095{
7096 LogFlowThisFuncEnter();
7097
7098 ComObjPtr<Progress> ptrProgress;
7099 HRESULT hrc = ptrProgress.createObject();
7100 if (SUCCEEDED(hrc))
7101 {
7102 com::Utf8Str strDefaultPath;
7103 if (aTargetPath.isEmpty())
7104 i_calculateFullPath(".", strDefaultPath);
7105
7106 /* Initialize our worker task */
7107 MachineMoveVM *pTask = NULL;
7108 try
7109 {
7110 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7111 }
7112 catch (std::bad_alloc &)
7113 {
7114 return E_OUTOFMEMORY;
7115 }
7116
7117 hrc = pTask->init();//no exceptions are thrown
7118
7119 if (SUCCEEDED(hrc))
7120 {
7121 hrc = pTask->createThread();
7122 pTask = NULL; /* Consumed by createThread(). */
7123 if (SUCCEEDED(hrc))
7124 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7125 else
7126 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7127 }
7128 else
7129 delete pTask;
7130 }
7131
7132 LogFlowThisFuncLeave();
7133 return hrc;
7134
7135}
7136
7137HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7138{
7139 NOREF(aProgress);
7140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7141
7142 // This check should always fail.
7143 HRESULT rc = i_checkStateDependency(MutableStateDep);
7144 if (FAILED(rc)) return rc;
7145
7146 AssertFailedReturn(E_NOTIMPL);
7147}
7148
7149HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7150{
7151 NOREF(aSavedStateFile);
7152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7153
7154 // This check should always fail.
7155 HRESULT rc = i_checkStateDependency(MutableStateDep);
7156 if (FAILED(rc)) return rc;
7157
7158 AssertFailedReturn(E_NOTIMPL);
7159}
7160
7161HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7162{
7163 NOREF(aFRemoveFile);
7164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7165
7166 // This check should always fail.
7167 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7168 if (FAILED(rc)) return rc;
7169
7170 AssertFailedReturn(E_NOTIMPL);
7171}
7172
7173// public methods for internal purposes
7174/////////////////////////////////////////////////////////////////////////////
7175
7176/**
7177 * Adds the given IsModified_* flag to the dirty flags of the machine.
7178 * This must be called either during i_loadSettings or under the machine write lock.
7179 * @param fl Flag
7180 * @param fAllowStateModification If state modifications are allowed.
7181 */
7182void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7183{
7184 mData->flModifications |= fl;
7185 if (fAllowStateModification && i_isStateModificationAllowed())
7186 mData->mCurrentStateModified = true;
7187}
7188
7189/**
7190 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7191 * care of the write locking.
7192 *
7193 * @param fModification The flag to add.
7194 * @param fAllowStateModification If state modifications are allowed.
7195 */
7196void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7197{
7198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7199 i_setModified(fModification, fAllowStateModification);
7200}
7201
7202/**
7203 * Saves the registry entry of this machine to the given configuration node.
7204 *
7205 * @param data Machine registry data.
7206 *
7207 * @note locks this object for reading.
7208 */
7209HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7210{
7211 AutoLimitedCaller autoCaller(this);
7212 AssertComRCReturnRC(autoCaller.rc());
7213
7214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7215
7216 data.uuid = mData->mUuid;
7217 data.strSettingsFile = mData->m_strConfigFile;
7218
7219 return S_OK;
7220}
7221
7222/**
7223 * Calculates the absolute path of the given path taking the directory of the
7224 * machine settings file as the current directory.
7225 *
7226 * @param strPath Path to calculate the absolute path for.
7227 * @param aResult Where to put the result (used only on success, can be the
7228 * same Utf8Str instance as passed in @a aPath).
7229 * @return IPRT result.
7230 *
7231 * @note Locks this object for reading.
7232 */
7233int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7234{
7235 AutoCaller autoCaller(this);
7236 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7237
7238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7239
7240 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7241
7242 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7243
7244 strSettingsDir.stripFilename();
7245 char szFolder[RTPATH_MAX];
7246 size_t cbFolder = sizeof(szFolder);
7247 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7248 if (RT_SUCCESS(vrc))
7249 aResult = szFolder;
7250
7251 return vrc;
7252}
7253
7254/**
7255 * Copies strSource to strTarget, making it relative to the machine folder
7256 * if it is a subdirectory thereof, or simply copying it otherwise.
7257 *
7258 * @param strSource Path to evaluate and copy.
7259 * @param strTarget Buffer to receive target path.
7260 *
7261 * @note Locks this object for reading.
7262 */
7263void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7264 Utf8Str &strTarget)
7265{
7266 AutoCaller autoCaller(this);
7267 AssertComRCReturn(autoCaller.rc(), (void)0);
7268
7269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7270
7271 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7272 // use strTarget as a temporary buffer to hold the machine settings dir
7273 strTarget = mData->m_strConfigFileFull;
7274 strTarget.stripFilename();
7275 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7276 {
7277 // is relative: then append what's left
7278 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7279 // for empty paths (only possible for subdirs) use "." to avoid
7280 // triggering default settings for not present config attributes.
7281 if (strTarget.isEmpty())
7282 strTarget = ".";
7283 }
7284 else
7285 // is not relative: then overwrite
7286 strTarget = strSource;
7287}
7288
7289/**
7290 * Returns the full path to the machine's log folder in the
7291 * \a aLogFolder argument.
7292 */
7293void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7294{
7295 AutoCaller autoCaller(this);
7296 AssertComRCReturnVoid(autoCaller.rc());
7297
7298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7299
7300 char szTmp[RTPATH_MAX];
7301 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7302 if (RT_SUCCESS(vrc))
7303 {
7304 if (szTmp[0] && !mUserData.isNull())
7305 {
7306 char szTmp2[RTPATH_MAX];
7307 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7308 if (RT_SUCCESS(vrc))
7309 aLogFolder.printf("%s%c%s",
7310 szTmp2,
7311 RTPATH_DELIMITER,
7312 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7313 }
7314 else
7315 vrc = VERR_PATH_IS_RELATIVE;
7316 }
7317
7318 if (RT_FAILURE(vrc))
7319 {
7320 // fallback if VBOX_USER_LOGHOME is not set or invalid
7321 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7322 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7323 aLogFolder.append(RTPATH_DELIMITER);
7324 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7325 }
7326}
7327
7328/**
7329 * Returns the full path to the machine's log file for an given index.
7330 */
7331Utf8Str Machine::i_getLogFilename(ULONG idx)
7332{
7333 Utf8Str logFolder;
7334 getLogFolder(logFolder);
7335 Assert(logFolder.length());
7336
7337 Utf8Str log;
7338 if (idx == 0)
7339 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7340#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7341 else if (idx == 1)
7342 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7343 else
7344 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7345#else
7346 else
7347 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7348#endif
7349 return log;
7350}
7351
7352/**
7353 * Returns the full path to the machine's hardened log file.
7354 */
7355Utf8Str Machine::i_getHardeningLogFilename(void)
7356{
7357 Utf8Str strFilename;
7358 getLogFolder(strFilename);
7359 Assert(strFilename.length());
7360 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7361 return strFilename;
7362}
7363
7364/**
7365 * Returns the default NVRAM filename based on the location of the VM config.
7366 * Note that this is a relative path.
7367 */
7368Utf8Str Machine::i_getDefaultNVRAMFilename()
7369{
7370 AutoCaller autoCaller(this);
7371 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7372
7373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7374
7375 if (i_isSnapshotMachine())
7376 return Utf8Str::Empty;
7377
7378 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7379 strNVRAMFilePath.stripPath();
7380 strNVRAMFilePath.stripSuffix();
7381 strNVRAMFilePath += ".nvram";
7382
7383 return strNVRAMFilePath;
7384}
7385
7386/**
7387 * Returns the NVRAM filename for a new snapshot. This intentionally works
7388 * similarly to the saved state file naming. Note that this is usually
7389 * a relative path, unless the snapshot folder is absolute.
7390 */
7391Utf8Str Machine::i_getSnapshotNVRAMFilename()
7392{
7393 AutoCaller autoCaller(this);
7394 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7395
7396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7397
7398 RTTIMESPEC ts;
7399 RTTimeNow(&ts);
7400 RTTIME time;
7401 RTTimeExplode(&time, &ts);
7402
7403 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7404 strNVRAMFilePath += RTPATH_DELIMITER;
7405 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7406 time.i32Year, time.u8Month, time.u8MonthDay,
7407 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7408
7409 return strNVRAMFilePath;
7410}
7411
7412/**
7413 * Returns the version of the settings file.
7414 */
7415SettingsVersion_T Machine::i_getSettingsVersion(void)
7416{
7417 AutoCaller autoCaller(this);
7418 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7419
7420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7421
7422 return mData->pMachineConfigFile->getSettingsVersion();
7423}
7424
7425/**
7426 * Composes a unique saved state filename based on the current system time. The filename is
7427 * granular to the second so this will work so long as no more than one snapshot is taken on
7428 * a machine per second.
7429 *
7430 * Before version 4.1, we used this formula for saved state files:
7431 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7432 * which no longer works because saved state files can now be shared between the saved state of the
7433 * "saved" machine and an online snapshot, and the following would cause problems:
7434 * 1) save machine
7435 * 2) create online snapshot from that machine state --> reusing saved state file
7436 * 3) save machine again --> filename would be reused, breaking the online snapshot
7437 *
7438 * So instead we now use a timestamp.
7439 *
7440 * @param strStateFilePath
7441 */
7442
7443void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7444{
7445 AutoCaller autoCaller(this);
7446 AssertComRCReturnVoid(autoCaller.rc());
7447
7448 {
7449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7450 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7451 }
7452
7453 RTTIMESPEC ts;
7454 RTTimeNow(&ts);
7455 RTTIME time;
7456 RTTimeExplode(&time, &ts);
7457
7458 strStateFilePath += RTPATH_DELIMITER;
7459 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7460 time.i32Year, time.u8Month, time.u8MonthDay,
7461 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7462}
7463
7464/**
7465 * Returns whether at least one USB controller is present for the VM.
7466 */
7467bool Machine::i_isUSBControllerPresent()
7468{
7469 AutoCaller autoCaller(this);
7470 AssertComRCReturn(autoCaller.rc(), false);
7471
7472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7473
7474 return (mUSBControllers->size() > 0);
7475}
7476
7477
7478/**
7479 * @note Locks this object for writing, calls the client process
7480 * (inside the lock).
7481 */
7482HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7483 const Utf8Str &strFrontend,
7484 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7485 ProgressProxy *aProgress)
7486{
7487 LogFlowThisFuncEnter();
7488
7489 AssertReturn(aControl, E_FAIL);
7490 AssertReturn(aProgress, E_FAIL);
7491 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7492
7493 AutoCaller autoCaller(this);
7494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7495
7496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7497
7498 if (!mData->mRegistered)
7499 return setError(E_UNEXPECTED,
7500 tr("The machine '%s' is not registered"),
7501 mUserData->s.strName.c_str());
7502
7503 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7504
7505 /* The process started when launching a VM with separate UI/VM processes is always
7506 * the UI process, i.e. needs special handling as it won't claim the session. */
7507 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7508
7509 if (fSeparate)
7510 {
7511 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7512 return setError(VBOX_E_INVALID_OBJECT_STATE,
7513 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7514 mUserData->s.strName.c_str());
7515 }
7516 else
7517 {
7518 if ( mData->mSession.mState == SessionState_Locked
7519 || mData->mSession.mState == SessionState_Spawning
7520 || mData->mSession.mState == SessionState_Unlocking)
7521 return setError(VBOX_E_INVALID_OBJECT_STATE,
7522 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7523 mUserData->s.strName.c_str());
7524
7525 /* may not be busy */
7526 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7527 }
7528
7529 /* Hardening logging */
7530#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7531 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7532 {
7533 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7534 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7535 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7536 {
7537 Utf8Str strStartupLogDir = strHardeningLogFile;
7538 strStartupLogDir.stripFilename();
7539 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7540 file without stripping the file. */
7541 }
7542 strSupHardeningLogArg.append(strHardeningLogFile);
7543
7544 /* Remove legacy log filename to avoid confusion. */
7545 Utf8Str strOldStartupLogFile;
7546 getLogFolder(strOldStartupLogFile);
7547 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7548 RTFileDelete(strOldStartupLogFile.c_str());
7549 }
7550#else
7551 Utf8Str strSupHardeningLogArg;
7552#endif
7553
7554 Utf8Str strAppOverride;
7555#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7556 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7557#endif
7558
7559 bool fUseVBoxSDS = false;
7560 Utf8Str strCanonicalName;
7561 if (false)
7562 { }
7563#ifdef VBOX_WITH_QTGUI
7564 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7565 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7566 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7567 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7568 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7569 {
7570 strCanonicalName = "GUI/Qt";
7571 fUseVBoxSDS = true;
7572 }
7573#endif
7574#ifdef VBOX_WITH_VBOXSDL
7575 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7576 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7577 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7578 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7579 {
7580 strCanonicalName = "GUI/SDL";
7581 fUseVBoxSDS = true;
7582 }
7583#endif
7584#ifdef VBOX_WITH_HEADLESS
7585 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7586 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7587 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7588 {
7589 strCanonicalName = "headless";
7590 }
7591#endif
7592 else
7593 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7594
7595 Utf8Str idStr = mData->mUuid.toString();
7596 Utf8Str const &strMachineName = mUserData->s.strName;
7597 RTPROCESS pid = NIL_RTPROCESS;
7598
7599#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7600 RT_NOREF(fUseVBoxSDS);
7601#else
7602 DWORD idCallerSession = ~(DWORD)0;
7603 if (fUseVBoxSDS)
7604 {
7605 /*
7606 * The VBoxSDS should be used for process launching the VM with
7607 * GUI only if the caller and the VBoxSDS are in different Windows
7608 * sessions and the caller in the interactive one.
7609 */
7610 fUseVBoxSDS = false;
7611
7612 /* Get windows session of the current process. The process token used
7613 due to several reasons:
7614 1. The token is absent for the current thread except someone set it
7615 for us.
7616 2. Needs to get the id of the session where the process is started.
7617 We only need to do this once, though. */
7618 static DWORD s_idCurrentSession = ~(DWORD)0;
7619 DWORD idCurrentSession = s_idCurrentSession;
7620 if (idCurrentSession == ~(DWORD)0)
7621 {
7622 HANDLE hCurrentProcessToken = NULL;
7623 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7624 {
7625 DWORD cbIgn = 0;
7626 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7627 s_idCurrentSession = idCurrentSession;
7628 else
7629 {
7630 idCurrentSession = ~(DWORD)0;
7631 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7632 }
7633 CloseHandle(hCurrentProcessToken);
7634 }
7635 else
7636 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7637 }
7638
7639 /* get the caller's session */
7640 HRESULT hrc = CoImpersonateClient();
7641 if (SUCCEEDED(hrc))
7642 {
7643 HANDLE hCallerThreadToken;
7644 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7645 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7646 &hCallerThreadToken))
7647 {
7648 SetLastError(NO_ERROR);
7649 DWORD cbIgn = 0;
7650 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7651 {
7652 /* Only need to use SDS if the session ID differs: */
7653 if (idCurrentSession != idCallerSession)
7654 {
7655 fUseVBoxSDS = false;
7656
7657 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7658 DWORD cbTokenGroups = 0;
7659 PTOKEN_GROUPS pTokenGroups = NULL;
7660 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7661 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7662 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7663 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7664 {
7665 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7666 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7667 PSID pInteractiveSid = NULL;
7668 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7669 {
7670 /* Iterate over the groups looking for the interactive SID: */
7671 fUseVBoxSDS = false;
7672 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7673 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7674 {
7675 fUseVBoxSDS = true;
7676 break;
7677 }
7678 FreeSid(pInteractiveSid);
7679 }
7680 }
7681 else
7682 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7683 RTMemTmpFree(pTokenGroups);
7684 }
7685 }
7686 else
7687 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7688 CloseHandle(hCallerThreadToken);
7689 }
7690 else
7691 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7692 CoRevertToSelf();
7693 }
7694 else
7695 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7696 }
7697 if (fUseVBoxSDS)
7698 {
7699 /* connect to VBoxSDS */
7700 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7701 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7702 if (FAILED(rc))
7703 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7704 strMachineName.c_str());
7705
7706 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7707 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7708 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7709 service to access the files. */
7710 rc = CoSetProxyBlanket(pVBoxSDS,
7711 RPC_C_AUTHN_DEFAULT,
7712 RPC_C_AUTHZ_DEFAULT,
7713 COLE_DEFAULT_PRINCIPAL,
7714 RPC_C_AUTHN_LEVEL_DEFAULT,
7715 RPC_C_IMP_LEVEL_IMPERSONATE,
7716 NULL,
7717 EOAC_DEFAULT);
7718 if (FAILED(rc))
7719 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7720
7721 size_t const cEnvVars = aEnvironmentChanges.size();
7722 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7723 for (size_t i = 0; i < cEnvVars; i++)
7724 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7725
7726 ULONG uPid = 0;
7727 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7728 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7729 idCallerSession, &uPid);
7730 if (FAILED(rc))
7731 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7732 pid = (RTPROCESS)uPid;
7733 }
7734 else
7735#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7736 {
7737 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7738 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7739 if (RT_FAILURE(vrc))
7740 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7741 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7742 }
7743
7744 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7745 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7746
7747 if (!fSeparate)
7748 {
7749 /*
7750 * Note that we don't release the lock here before calling the client,
7751 * because it doesn't need to call us back if called with a NULL argument.
7752 * Releasing the lock here is dangerous because we didn't prepare the
7753 * launch data yet, but the client we've just started may happen to be
7754 * too fast and call LockMachine() that will fail (because of PID, etc.),
7755 * so that the Machine will never get out of the Spawning session state.
7756 */
7757
7758 /* inform the session that it will be a remote one */
7759 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7760#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7761 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7762#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7763 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7764#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7765 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7766
7767 if (FAILED(rc))
7768 {
7769 /* restore the session state */
7770 mData->mSession.mState = SessionState_Unlocked;
7771 alock.release();
7772 mParent->i_addProcessToReap(pid);
7773 /* The failure may occur w/o any error info (from RPC), so provide one */
7774 return setError(VBOX_E_VM_ERROR,
7775 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7776 }
7777
7778 /* attach launch data to the machine */
7779 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7780 mData->mSession.mRemoteControls.push_back(aControl);
7781 mData->mSession.mProgress = aProgress;
7782 mData->mSession.mPID = pid;
7783 mData->mSession.mState = SessionState_Spawning;
7784 Assert(strCanonicalName.isNotEmpty());
7785 mData->mSession.mName = strCanonicalName;
7786 }
7787 else
7788 {
7789 /* For separate UI process we declare the launch as completed instantly, as the
7790 * actual headless VM start may or may not come. No point in remembering anything
7791 * yet, as what matters for us is when the headless VM gets started. */
7792 aProgress->i_notifyComplete(S_OK);
7793 }
7794
7795 alock.release();
7796 mParent->i_addProcessToReap(pid);
7797
7798 LogFlowThisFuncLeave();
7799 return S_OK;
7800}
7801
7802/**
7803 * Returns @c true if the given session machine instance has an open direct
7804 * session (and optionally also for direct sessions which are closing) and
7805 * returns the session control machine instance if so.
7806 *
7807 * Note that when the method returns @c false, the arguments remain unchanged.
7808 *
7809 * @param aMachine Session machine object.
7810 * @param aControl Direct session control object (optional).
7811 * @param aRequireVM If true then only allow VM sessions.
7812 * @param aAllowClosing If true then additionally a session which is currently
7813 * being closed will also be allowed.
7814 *
7815 * @note locks this object for reading.
7816 */
7817bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7818 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7819 bool aRequireVM /*= false*/,
7820 bool aAllowClosing /*= false*/)
7821{
7822 AutoLimitedCaller autoCaller(this);
7823 AssertComRCReturn(autoCaller.rc(), false);
7824
7825 /* just return false for inaccessible machines */
7826 if (getObjectState().getState() != ObjectState::Ready)
7827 return false;
7828
7829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7830
7831 if ( ( mData->mSession.mState == SessionState_Locked
7832 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7833 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7834 )
7835 {
7836 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7837
7838 aMachine = mData->mSession.mMachine;
7839
7840 if (aControl != NULL)
7841 *aControl = mData->mSession.mDirectControl;
7842
7843 return true;
7844 }
7845
7846 return false;
7847}
7848
7849/**
7850 * Returns @c true if the given machine has an spawning direct session.
7851 *
7852 * @note locks this object for reading.
7853 */
7854bool Machine::i_isSessionSpawning()
7855{
7856 AutoLimitedCaller autoCaller(this);
7857 AssertComRCReturn(autoCaller.rc(), false);
7858
7859 /* just return false for inaccessible machines */
7860 if (getObjectState().getState() != ObjectState::Ready)
7861 return false;
7862
7863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7864
7865 if (mData->mSession.mState == SessionState_Spawning)
7866 return true;
7867
7868 return false;
7869}
7870
7871/**
7872 * Called from the client watcher thread to check for unexpected client process
7873 * death during Session_Spawning state (e.g. before it successfully opened a
7874 * direct session).
7875 *
7876 * On Win32 and on OS/2, this method is called only when we've got the
7877 * direct client's process termination notification, so it always returns @c
7878 * true.
7879 *
7880 * On other platforms, this method returns @c true if the client process is
7881 * terminated and @c false if it's still alive.
7882 *
7883 * @note Locks this object for writing.
7884 */
7885bool Machine::i_checkForSpawnFailure()
7886{
7887 AutoCaller autoCaller(this);
7888 if (!autoCaller.isOk())
7889 {
7890 /* nothing to do */
7891 LogFlowThisFunc(("Already uninitialized!\n"));
7892 return true;
7893 }
7894
7895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7896
7897 if (mData->mSession.mState != SessionState_Spawning)
7898 {
7899 /* nothing to do */
7900 LogFlowThisFunc(("Not spawning any more!\n"));
7901 return true;
7902 }
7903
7904 HRESULT rc = S_OK;
7905
7906 /* PID not yet initialized, skip check. */
7907 if (mData->mSession.mPID == NIL_RTPROCESS)
7908 return false;
7909
7910 RTPROCSTATUS status;
7911 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7912
7913 if (vrc != VERR_PROCESS_RUNNING)
7914 {
7915 Utf8Str strExtraInfo;
7916
7917#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7918 /* If the startup logfile exists and is of non-zero length, tell the
7919 user to look there for more details to encourage them to attach it
7920 when reporting startup issues. */
7921 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7922 uint64_t cbStartupLogFile = 0;
7923 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7924 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7925 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7926#endif
7927
7928 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7929 rc = setError(E_FAIL,
7930 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7931 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7932 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7933 rc = setError(E_FAIL,
7934 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7935 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7936 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7937 rc = setError(E_FAIL,
7938 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7939 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7940 else
7941 rc = setErrorBoth(E_FAIL, vrc,
7942 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7943 i_getName().c_str(), vrc, strExtraInfo.c_str());
7944 }
7945
7946 if (FAILED(rc))
7947 {
7948 /* Close the remote session, remove the remote control from the list
7949 * and reset session state to Closed (@note keep the code in sync with
7950 * the relevant part in LockMachine()). */
7951
7952 Assert(mData->mSession.mRemoteControls.size() == 1);
7953 if (mData->mSession.mRemoteControls.size() == 1)
7954 {
7955 ErrorInfoKeeper eik;
7956 mData->mSession.mRemoteControls.front()->Uninitialize();
7957 }
7958
7959 mData->mSession.mRemoteControls.clear();
7960 mData->mSession.mState = SessionState_Unlocked;
7961
7962 /* finalize the progress after setting the state */
7963 if (!mData->mSession.mProgress.isNull())
7964 {
7965 mData->mSession.mProgress->notifyComplete(rc);
7966 mData->mSession.mProgress.setNull();
7967 }
7968
7969 mData->mSession.mPID = NIL_RTPROCESS;
7970
7971 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7972 return true;
7973 }
7974
7975 return false;
7976}
7977
7978/**
7979 * Checks whether the machine can be registered. If so, commits and saves
7980 * all settings.
7981 *
7982 * @note Must be called from mParent's write lock. Locks this object and
7983 * children for writing.
7984 */
7985HRESULT Machine::i_prepareRegister()
7986{
7987 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7988
7989 AutoLimitedCaller autoCaller(this);
7990 AssertComRCReturnRC(autoCaller.rc());
7991
7992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7993
7994 /* wait for state dependents to drop to zero */
7995 i_ensureNoStateDependencies(alock);
7996
7997 if (!mData->mAccessible)
7998 return setError(VBOX_E_INVALID_OBJECT_STATE,
7999 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8000 mUserData->s.strName.c_str(),
8001 mData->mUuid.toString().c_str());
8002
8003 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8004
8005 if (mData->mRegistered)
8006 return setError(VBOX_E_INVALID_OBJECT_STATE,
8007 tr("The machine '%s' with UUID {%s} is already registered"),
8008 mUserData->s.strName.c_str(),
8009 mData->mUuid.toString().c_str());
8010
8011 HRESULT rc = S_OK;
8012
8013 // Ensure the settings are saved. If we are going to be registered and
8014 // no config file exists yet, create it by calling i_saveSettings() too.
8015 if ( (mData->flModifications)
8016 || (!mData->pMachineConfigFile->fileExists())
8017 )
8018 {
8019 rc = i_saveSettings(NULL, alock);
8020 // no need to check whether VirtualBox.xml needs saving too since
8021 // we can't have a machine XML file rename pending
8022 if (FAILED(rc)) return rc;
8023 }
8024
8025 /* more config checking goes here */
8026
8027 if (SUCCEEDED(rc))
8028 {
8029 /* we may have had implicit modifications we want to fix on success */
8030 i_commit();
8031
8032 mData->mRegistered = true;
8033 }
8034 else
8035 {
8036 /* we may have had implicit modifications we want to cancel on failure*/
8037 i_rollback(false /* aNotify */);
8038 }
8039
8040 return rc;
8041}
8042
8043/**
8044 * Increases the number of objects dependent on the machine state or on the
8045 * registered state. Guarantees that these two states will not change at least
8046 * until #i_releaseStateDependency() is called.
8047 *
8048 * Depending on the @a aDepType value, additional state checks may be made.
8049 * These checks will set extended error info on failure. See
8050 * #i_checkStateDependency() for more info.
8051 *
8052 * If this method returns a failure, the dependency is not added and the caller
8053 * is not allowed to rely on any particular machine state or registration state
8054 * value and may return the failed result code to the upper level.
8055 *
8056 * @param aDepType Dependency type to add.
8057 * @param aState Current machine state (NULL if not interested).
8058 * @param aRegistered Current registered state (NULL if not interested).
8059 *
8060 * @note Locks this object for writing.
8061 */
8062HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8063 MachineState_T *aState /* = NULL */,
8064 BOOL *aRegistered /* = NULL */)
8065{
8066 AutoCaller autoCaller(this);
8067 AssertComRCReturnRC(autoCaller.rc());
8068
8069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8070
8071 HRESULT rc = i_checkStateDependency(aDepType);
8072 if (FAILED(rc)) return rc;
8073
8074 {
8075 if (mData->mMachineStateChangePending != 0)
8076 {
8077 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8078 * drop to zero so don't add more. It may make sense to wait a bit
8079 * and retry before reporting an error (since the pending state
8080 * transition should be really quick) but let's just assert for
8081 * now to see if it ever happens on practice. */
8082
8083 AssertFailed();
8084
8085 return setError(E_ACCESSDENIED,
8086 tr("Machine state change is in progress. Please retry the operation later."));
8087 }
8088
8089 ++mData->mMachineStateDeps;
8090 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8091 }
8092
8093 if (aState)
8094 *aState = mData->mMachineState;
8095 if (aRegistered)
8096 *aRegistered = mData->mRegistered;
8097
8098 return S_OK;
8099}
8100
8101/**
8102 * Decreases the number of objects dependent on the machine state.
8103 * Must always complete the #i_addStateDependency() call after the state
8104 * dependency is no more necessary.
8105 */
8106void Machine::i_releaseStateDependency()
8107{
8108 AutoCaller autoCaller(this);
8109 AssertComRCReturnVoid(autoCaller.rc());
8110
8111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8112
8113 /* releaseStateDependency() w/o addStateDependency()? */
8114 AssertReturnVoid(mData->mMachineStateDeps != 0);
8115 -- mData->mMachineStateDeps;
8116
8117 if (mData->mMachineStateDeps == 0)
8118 {
8119 /* inform i_ensureNoStateDependencies() that there are no more deps */
8120 if (mData->mMachineStateChangePending != 0)
8121 {
8122 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8123 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8124 }
8125 }
8126}
8127
8128Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8129{
8130 /* start with nothing found */
8131 Utf8Str strResult("");
8132
8133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8134
8135 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8136 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8137 // found:
8138 strResult = it->second; // source is a Utf8Str
8139
8140 return strResult;
8141}
8142
8143// protected methods
8144/////////////////////////////////////////////////////////////////////////////
8145
8146/**
8147 * Performs machine state checks based on the @a aDepType value. If a check
8148 * fails, this method will set extended error info, otherwise it will return
8149 * S_OK. It is supposed, that on failure, the caller will immediately return
8150 * the return value of this method to the upper level.
8151 *
8152 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8153 *
8154 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8155 * current state of this machine object allows to change settings of the
8156 * machine (i.e. the machine is not registered, or registered but not running
8157 * and not saved). It is useful to call this method from Machine setters
8158 * before performing any change.
8159 *
8160 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8161 * as for MutableStateDep except that if the machine is saved, S_OK is also
8162 * returned. This is useful in setters which allow changing machine
8163 * properties when it is in the saved state.
8164 *
8165 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8166 * if the current state of this machine object allows to change runtime
8167 * changeable settings of the machine (i.e. the machine is not registered, or
8168 * registered but either running or not running and not saved). It is useful
8169 * to call this method from Machine setters before performing any changes to
8170 * runtime changeable settings.
8171 *
8172 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8173 * the same as for MutableOrRunningStateDep except that if the machine is
8174 * saved, S_OK is also returned. This is useful in setters which allow
8175 * changing runtime and saved state changeable machine properties.
8176 *
8177 * @param aDepType Dependency type to check.
8178 *
8179 * @note Non Machine based classes should use #i_addStateDependency() and
8180 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8181 * template.
8182 *
8183 * @note This method must be called from under this object's read or write
8184 * lock.
8185 */
8186HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8187{
8188 switch (aDepType)
8189 {
8190 case AnyStateDep:
8191 {
8192 break;
8193 }
8194 case MutableStateDep:
8195 {
8196 if ( mData->mRegistered
8197 && ( !i_isSessionMachine()
8198 || ( mData->mMachineState != MachineState_Aborted
8199 && mData->mMachineState != MachineState_Teleported
8200 && mData->mMachineState != MachineState_PoweredOff
8201 )
8202 )
8203 )
8204 return setError(VBOX_E_INVALID_VM_STATE,
8205 tr("The machine is not mutable (state is %s)"),
8206 Global::stringifyMachineState(mData->mMachineState));
8207 break;
8208 }
8209 case MutableOrSavedStateDep:
8210 {
8211 if ( mData->mRegistered
8212 && ( !i_isSessionMachine()
8213 || ( mData->mMachineState != MachineState_Aborted
8214 && mData->mMachineState != MachineState_Teleported
8215 && mData->mMachineState != MachineState_Saved
8216 && mData->mMachineState != MachineState_AbortedSaved
8217 && mData->mMachineState != MachineState_PoweredOff
8218 )
8219 )
8220 )
8221 return setError(VBOX_E_INVALID_VM_STATE,
8222 tr("The machine is not mutable or saved (state is %s)"),
8223 Global::stringifyMachineState(mData->mMachineState));
8224 break;
8225 }
8226 case MutableOrRunningStateDep:
8227 {
8228 if ( mData->mRegistered
8229 && ( !i_isSessionMachine()
8230 || ( mData->mMachineState != MachineState_Aborted
8231 && mData->mMachineState != MachineState_Teleported
8232 && mData->mMachineState != MachineState_PoweredOff
8233 && !Global::IsOnline(mData->mMachineState)
8234 )
8235 )
8236 )
8237 return setError(VBOX_E_INVALID_VM_STATE,
8238 tr("The machine is not mutable or running (state is %s)"),
8239 Global::stringifyMachineState(mData->mMachineState));
8240 break;
8241 }
8242 case MutableOrSavedOrRunningStateDep:
8243 {
8244 if ( mData->mRegistered
8245 && ( !i_isSessionMachine()
8246 || ( mData->mMachineState != MachineState_Aborted
8247 && mData->mMachineState != MachineState_Teleported
8248 && mData->mMachineState != MachineState_Saved
8249 && mData->mMachineState != MachineState_AbortedSaved
8250 && mData->mMachineState != MachineState_PoweredOff
8251 && !Global::IsOnline(mData->mMachineState)
8252 )
8253 )
8254 )
8255 return setError(VBOX_E_INVALID_VM_STATE,
8256 tr("The machine is not mutable, saved or running (state is %s)"),
8257 Global::stringifyMachineState(mData->mMachineState));
8258 break;
8259 }
8260 }
8261
8262 return S_OK;
8263}
8264
8265/**
8266 * Helper to initialize all associated child objects and allocate data
8267 * structures.
8268 *
8269 * This method must be called as a part of the object's initialization procedure
8270 * (usually done in the #init() method).
8271 *
8272 * @note Must be called only from #init() or from #i_registeredInit().
8273 */
8274HRESULT Machine::initDataAndChildObjects()
8275{
8276 AutoCaller autoCaller(this);
8277 AssertComRCReturnRC(autoCaller.rc());
8278 AssertReturn( getObjectState().getState() == ObjectState::InInit
8279 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8280
8281 AssertReturn(!mData->mAccessible, E_FAIL);
8282
8283 /* allocate data structures */
8284 mSSData.allocate();
8285 mUserData.allocate();
8286 mHWData.allocate();
8287 mMediumAttachments.allocate();
8288 mStorageControllers.allocate();
8289 mUSBControllers.allocate();
8290
8291 /* initialize mOSTypeId */
8292 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8293
8294/** @todo r=bird: init() methods never fails, right? Why don't we make them
8295 * return void then! */
8296
8297 /* create associated BIOS settings object */
8298 unconst(mBIOSSettings).createObject();
8299 mBIOSSettings->init(this);
8300
8301 /* create associated trusted platform module object */
8302 unconst(mTrustedPlatformModule).createObject();
8303 mTrustedPlatformModule->init(this);
8304
8305 /* create associated NVRAM store object */
8306 unconst(mNvramStore).createObject();
8307 mNvramStore->init(this);
8308
8309 /* create associated record settings object */
8310 unconst(mRecordingSettings).createObject();
8311 mRecordingSettings->init(this);
8312
8313 /* create the graphics adapter object (always present) */
8314 unconst(mGraphicsAdapter).createObject();
8315 mGraphicsAdapter->init(this);
8316
8317 /* create an associated VRDE object (default is disabled) */
8318 unconst(mVRDEServer).createObject();
8319 mVRDEServer->init(this);
8320
8321 /* create associated serial port objects */
8322 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8323 {
8324 unconst(mSerialPorts[slot]).createObject();
8325 mSerialPorts[slot]->init(this, slot);
8326 }
8327
8328 /* create associated parallel port objects */
8329 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8330 {
8331 unconst(mParallelPorts[slot]).createObject();
8332 mParallelPorts[slot]->init(this, slot);
8333 }
8334
8335 /* create the audio adapter object (always present, default is disabled) */
8336 unconst(mAudioAdapter).createObject();
8337 mAudioAdapter->init(this);
8338
8339 /* create the USB device filters object (always present) */
8340 unconst(mUSBDeviceFilters).createObject();
8341 mUSBDeviceFilters->init(this);
8342
8343 /* create associated network adapter objects */
8344 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8346 {
8347 unconst(mNetworkAdapters[slot]).createObject();
8348 mNetworkAdapters[slot]->init(this, slot);
8349 }
8350
8351 /* create the bandwidth control */
8352 unconst(mBandwidthControl).createObject();
8353 mBandwidthControl->init(this);
8354
8355 return S_OK;
8356}
8357
8358/**
8359 * Helper to uninitialize all associated child objects and to free all data
8360 * structures.
8361 *
8362 * This method must be called as a part of the object's uninitialization
8363 * procedure (usually done in the #uninit() method).
8364 *
8365 * @note Must be called only from #uninit() or from #i_registeredInit().
8366 */
8367void Machine::uninitDataAndChildObjects()
8368{
8369 AutoCaller autoCaller(this);
8370 AssertComRCReturnVoid(autoCaller.rc());
8371 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8372 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8373 || getObjectState().getState() == ObjectState::InUninit
8374 || getObjectState().getState() == ObjectState::Limited);
8375
8376 /* tell all our other child objects we've been uninitialized */
8377 if (mBandwidthControl)
8378 {
8379 mBandwidthControl->uninit();
8380 unconst(mBandwidthControl).setNull();
8381 }
8382
8383 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8384 {
8385 if (mNetworkAdapters[slot])
8386 {
8387 mNetworkAdapters[slot]->uninit();
8388 unconst(mNetworkAdapters[slot]).setNull();
8389 }
8390 }
8391
8392 if (mUSBDeviceFilters)
8393 {
8394 mUSBDeviceFilters->uninit();
8395 unconst(mUSBDeviceFilters).setNull();
8396 }
8397
8398 if (mAudioAdapter)
8399 {
8400 mAudioAdapter->uninit();
8401 unconst(mAudioAdapter).setNull();
8402 }
8403
8404 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8405 {
8406 if (mParallelPorts[slot])
8407 {
8408 mParallelPorts[slot]->uninit();
8409 unconst(mParallelPorts[slot]).setNull();
8410 }
8411 }
8412
8413 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8414 {
8415 if (mSerialPorts[slot])
8416 {
8417 mSerialPorts[slot]->uninit();
8418 unconst(mSerialPorts[slot]).setNull();
8419 }
8420 }
8421
8422 if (mVRDEServer)
8423 {
8424 mVRDEServer->uninit();
8425 unconst(mVRDEServer).setNull();
8426 }
8427
8428 if (mGraphicsAdapter)
8429 {
8430 mGraphicsAdapter->uninit();
8431 unconst(mGraphicsAdapter).setNull();
8432 }
8433
8434 if (mBIOSSettings)
8435 {
8436 mBIOSSettings->uninit();
8437 unconst(mBIOSSettings).setNull();
8438 }
8439
8440 if (mTrustedPlatformModule)
8441 {
8442 mTrustedPlatformModule->uninit();
8443 unconst(mTrustedPlatformModule).setNull();
8444 }
8445
8446 if (mNvramStore)
8447 {
8448 mNvramStore->uninit();
8449 unconst(mNvramStore).setNull();
8450 }
8451
8452 if (mRecordingSettings)
8453 {
8454 mRecordingSettings->uninit();
8455 unconst(mRecordingSettings).setNull();
8456 }
8457
8458 /* Deassociate media (only when a real Machine or a SnapshotMachine
8459 * instance is uninitialized; SessionMachine instances refer to real
8460 * Machine media). This is necessary for a clean re-initialization of
8461 * the VM after successfully re-checking the accessibility state. Note
8462 * that in case of normal Machine or SnapshotMachine uninitialization (as
8463 * a result of unregistering or deleting the snapshot), outdated media
8464 * attachments will already be uninitialized and deleted, so this
8465 * code will not affect them. */
8466 if ( !mMediumAttachments.isNull()
8467 && !i_isSessionMachine()
8468 )
8469 {
8470 for (MediumAttachmentList::const_iterator
8471 it = mMediumAttachments->begin();
8472 it != mMediumAttachments->end();
8473 ++it)
8474 {
8475 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8476 if (pMedium.isNull())
8477 continue;
8478 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8479 AssertComRC(rc);
8480 }
8481 }
8482
8483 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8484 {
8485 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8486 if (mData->mFirstSnapshot)
8487 {
8488 // Snapshots tree is protected by machine write lock.
8489 // Otherwise we assert in Snapshot::uninit()
8490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8491 mData->mFirstSnapshot->uninit();
8492 mData->mFirstSnapshot.setNull();
8493 }
8494
8495 mData->mCurrentSnapshot.setNull();
8496 }
8497
8498 /* free data structures (the essential mData structure is not freed here
8499 * since it may be still in use) */
8500 mMediumAttachments.free();
8501 mStorageControllers.free();
8502 mUSBControllers.free();
8503 mHWData.free();
8504 mUserData.free();
8505 mSSData.free();
8506}
8507
8508/**
8509 * Returns a pointer to the Machine object for this machine that acts like a
8510 * parent for complex machine data objects such as shared folders, etc.
8511 *
8512 * For primary Machine objects and for SnapshotMachine objects, returns this
8513 * object's pointer itself. For SessionMachine objects, returns the peer
8514 * (primary) machine pointer.
8515 */
8516Machine *Machine::i_getMachine()
8517{
8518 if (i_isSessionMachine())
8519 return (Machine*)mPeer;
8520 return this;
8521}
8522
8523/**
8524 * Makes sure that there are no machine state dependents. If necessary, waits
8525 * for the number of dependents to drop to zero.
8526 *
8527 * Make sure this method is called from under this object's write lock to
8528 * guarantee that no new dependents may be added when this method returns
8529 * control to the caller.
8530 *
8531 * @note Receives a lock to this object for writing. The lock will be released
8532 * while waiting (if necessary).
8533 *
8534 * @warning To be used only in methods that change the machine state!
8535 */
8536void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8537{
8538 AssertReturnVoid(isWriteLockOnCurrentThread());
8539
8540 /* Wait for all state dependents if necessary */
8541 if (mData->mMachineStateDeps != 0)
8542 {
8543 /* lazy semaphore creation */
8544 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8545 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8546
8547 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8548 mData->mMachineStateDeps));
8549
8550 ++mData->mMachineStateChangePending;
8551
8552 /* reset the semaphore before waiting, the last dependent will signal
8553 * it */
8554 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8555
8556 alock.release();
8557
8558 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8559
8560 alock.acquire();
8561
8562 -- mData->mMachineStateChangePending;
8563 }
8564}
8565
8566/**
8567 * Changes the machine state and informs callbacks.
8568 *
8569 * This method is not intended to fail so it either returns S_OK or asserts (and
8570 * returns a failure).
8571 *
8572 * @note Locks this object for writing.
8573 */
8574HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8575{
8576 LogFlowThisFuncEnter();
8577 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8578 Assert(aMachineState != MachineState_Null);
8579
8580 AutoCaller autoCaller(this);
8581 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8582
8583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8584
8585 /* wait for state dependents to drop to zero */
8586 i_ensureNoStateDependencies(alock);
8587
8588 MachineState_T const enmOldState = mData->mMachineState;
8589 if (enmOldState != aMachineState)
8590 {
8591 mData->mMachineState = aMachineState;
8592 RTTimeNow(&mData->mLastStateChange);
8593
8594#ifdef VBOX_WITH_DTRACE_R3_MAIN
8595 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8596#endif
8597 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8598 }
8599
8600 LogFlowThisFuncLeave();
8601 return S_OK;
8602}
8603
8604/**
8605 * Searches for a shared folder with the given logical name
8606 * in the collection of shared folders.
8607 *
8608 * @param aName logical name of the shared folder
8609 * @param aSharedFolder where to return the found object
8610 * @param aSetError whether to set the error info if the folder is
8611 * not found
8612 * @return
8613 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8614 *
8615 * @note
8616 * must be called from under the object's lock!
8617 */
8618HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8619 ComObjPtr<SharedFolder> &aSharedFolder,
8620 bool aSetError /* = false */)
8621{
8622 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8623 for (HWData::SharedFolderList::const_iterator
8624 it = mHWData->mSharedFolders.begin();
8625 it != mHWData->mSharedFolders.end();
8626 ++it)
8627 {
8628 SharedFolder *pSF = *it;
8629 AutoCaller autoCaller(pSF);
8630 if (pSF->i_getName() == aName)
8631 {
8632 aSharedFolder = pSF;
8633 rc = S_OK;
8634 break;
8635 }
8636 }
8637
8638 if (aSetError && FAILED(rc))
8639 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8640
8641 return rc;
8642}
8643
8644/**
8645 * Initializes all machine instance data from the given settings structures
8646 * from XML. The exception is the machine UUID which needs special handling
8647 * depending on the caller's use case, so the caller needs to set that herself.
8648 *
8649 * This gets called in several contexts during machine initialization:
8650 *
8651 * -- When machine XML exists on disk already and needs to be loaded into memory,
8652 * for example, from #i_registeredInit() to load all registered machines on
8653 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8654 * attached to the machine should be part of some media registry already.
8655 *
8656 * -- During OVF import, when a machine config has been constructed from an
8657 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8658 * ensure that the media listed as attachments in the config (which have
8659 * been imported from the OVF) receive the correct registry ID.
8660 *
8661 * -- During VM cloning.
8662 *
8663 * @param config Machine settings from XML.
8664 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8665 * for each attached medium in the config.
8666 * @return
8667 */
8668HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8669 const Guid *puuidRegistry)
8670{
8671 // copy name, description, OS type, teleporter, UTC etc.
8672 mUserData->s = config.machineUserData;
8673
8674 // look up the object by Id to check it is valid
8675 ComObjPtr<GuestOSType> pGuestOSType;
8676 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8677 if (!pGuestOSType.isNull())
8678 mUserData->s.strOsType = pGuestOSType->i_id();
8679
8680 // stateFile (optional)
8681 if (config.strStateFile.isEmpty())
8682 mSSData->strStateFilePath.setNull();
8683 else
8684 {
8685 Utf8Str stateFilePathFull(config.strStateFile);
8686 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8687 if (RT_FAILURE(vrc))
8688 return setErrorBoth(E_FAIL, vrc,
8689 tr("Invalid saved state file path '%s' (%Rrc)"),
8690 config.strStateFile.c_str(),
8691 vrc);
8692 mSSData->strStateFilePath = stateFilePathFull;
8693 }
8694
8695 // snapshot folder needs special processing so set it again
8696 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8697 if (FAILED(rc)) return rc;
8698
8699 /* Copy the extra data items (config may or may not be the same as
8700 * mData->pMachineConfigFile) if necessary. When loading the XML files
8701 * from disk they are the same, but not for OVF import. */
8702 if (mData->pMachineConfigFile != &config)
8703 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8704
8705 /* currentStateModified (optional, default is true) */
8706 mData->mCurrentStateModified = config.fCurrentStateModified;
8707
8708 mData->mLastStateChange = config.timeLastStateChange;
8709
8710 /*
8711 * note: all mUserData members must be assigned prior this point because
8712 * we need to commit changes in order to let mUserData be shared by all
8713 * snapshot machine instances.
8714 */
8715 mUserData.commitCopy();
8716
8717 // machine registry, if present (must be loaded before snapshots)
8718 if (config.canHaveOwnMediaRegistry())
8719 {
8720 // determine machine folder
8721 Utf8Str strMachineFolder = i_getSettingsFileFull();
8722 strMachineFolder.stripFilename();
8723 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8724 config.mediaRegistry,
8725 strMachineFolder);
8726 if (FAILED(rc)) return rc;
8727 }
8728
8729 /* Snapshot node (optional) */
8730 size_t cRootSnapshots;
8731 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8732 {
8733 // there must be only one root snapshot
8734 Assert(cRootSnapshots == 1);
8735 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8736
8737 rc = i_loadSnapshot(snap,
8738 config.uuidCurrentSnapshot);
8739 if (FAILED(rc)) return rc;
8740 }
8741
8742 // hardware data
8743 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8744 if (FAILED(rc)) return rc;
8745
8746 /*
8747 * NOTE: the assignment below must be the last thing to do,
8748 * otherwise it will be not possible to change the settings
8749 * somewhere in the code above because all setters will be
8750 * blocked by i_checkStateDependency(MutableStateDep).
8751 */
8752
8753 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8754 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8755 {
8756 /* no need to use i_setMachineState() during init() */
8757 mData->mMachineState = MachineState_AbortedSaved;
8758 }
8759 else if (config.fAborted)
8760 {
8761 mSSData->strStateFilePath.setNull();
8762
8763 /* no need to use i_setMachineState() during init() */
8764 mData->mMachineState = MachineState_Aborted;
8765 }
8766 else if (!mSSData->strStateFilePath.isEmpty())
8767 {
8768 /* no need to use i_setMachineState() during init() */
8769 mData->mMachineState = MachineState_Saved;
8770 }
8771
8772 // after loading settings, we are no longer different from the XML on disk
8773 mData->flModifications = 0;
8774
8775 return S_OK;
8776}
8777
8778/**
8779 * Loads all snapshots starting from the given settings.
8780 *
8781 * @param data snapshot settings.
8782 * @param aCurSnapshotId Current snapshot ID from the settings file.
8783 */
8784HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8785 const Guid &aCurSnapshotId)
8786{
8787 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8788 AssertReturn(!i_isSessionMachine(), E_FAIL);
8789
8790 HRESULT rc = S_OK;
8791
8792 std::list<const settings::Snapshot *> llSettingsTodo;
8793 llSettingsTodo.push_back(&data);
8794 std::list<Snapshot *> llParentsTodo;
8795 llParentsTodo.push_back(NULL);
8796
8797 while (llSettingsTodo.size() > 0)
8798 {
8799 const settings::Snapshot *current = llSettingsTodo.front();
8800 llSettingsTodo.pop_front();
8801 Snapshot *pParent = llParentsTodo.front();
8802 llParentsTodo.pop_front();
8803
8804 Utf8Str strStateFile;
8805 if (!current->strStateFile.isEmpty())
8806 {
8807 /* optional */
8808 strStateFile = current->strStateFile;
8809 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8810 if (RT_FAILURE(vrc))
8811 {
8812 setErrorBoth(E_FAIL, vrc,
8813 tr("Invalid saved state file path '%s' (%Rrc)"),
8814 strStateFile.c_str(), vrc);
8815 }
8816 }
8817
8818 /* create a snapshot machine object */
8819 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8820 pSnapshotMachine.createObject();
8821 rc = pSnapshotMachine->initFromSettings(this,
8822 current->hardware,
8823 &current->debugging,
8824 &current->autostart,
8825 current->uuid.ref(),
8826 strStateFile);
8827 if (FAILED(rc)) break;
8828
8829 /* create a snapshot object */
8830 ComObjPtr<Snapshot> pSnapshot;
8831 pSnapshot.createObject();
8832 /* initialize the snapshot */
8833 rc = pSnapshot->init(mParent, // VirtualBox object
8834 current->uuid,
8835 current->strName,
8836 current->strDescription,
8837 current->timestamp,
8838 pSnapshotMachine,
8839 pParent);
8840 if (FAILED(rc)) break;
8841
8842 /* memorize the first snapshot if necessary */
8843 if (!mData->mFirstSnapshot)
8844 {
8845 Assert(pParent == NULL);
8846 mData->mFirstSnapshot = pSnapshot;
8847 }
8848
8849 /* memorize the current snapshot when appropriate */
8850 if ( !mData->mCurrentSnapshot
8851 && pSnapshot->i_getId() == aCurSnapshotId
8852 )
8853 mData->mCurrentSnapshot = pSnapshot;
8854
8855 /* create all children */
8856 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
8857 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
8858 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
8859 {
8860 llSettingsTodo.push_back(&*it);
8861 llParentsTodo.push_back(pSnapshot);
8862 }
8863 }
8864
8865 return rc;
8866}
8867
8868/**
8869 * Loads settings into mHWData.
8870 *
8871 * @param puuidRegistry Registry ID.
8872 * @param puuidSnapshot Snapshot ID
8873 * @param data Reference to the hardware settings.
8874 * @param pDbg Pointer to the debugging settings.
8875 * @param pAutostart Pointer to the autostart settings.
8876 */
8877HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8878 const Guid *puuidSnapshot,
8879 const settings::Hardware &data,
8880 const settings::Debugging *pDbg,
8881 const settings::Autostart *pAutostart)
8882{
8883 AssertReturn(!i_isSessionMachine(), E_FAIL);
8884
8885 HRESULT rc = S_OK;
8886
8887 try
8888 {
8889 ComObjPtr<GuestOSType> pGuestOSType;
8890 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8891
8892 /* The hardware version attribute (optional). */
8893 mHWData->mHWVersion = data.strVersion;
8894 mHWData->mHardwareUUID = data.uuid;
8895
8896 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8897 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8898 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8899 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8900 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8901 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8902 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8903 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8904 mHWData->mPAEEnabled = data.fPAE;
8905 mHWData->mLongMode = data.enmLongMode;
8906 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8907 mHWData->mAPIC = data.fAPIC;
8908 mHWData->mX2APIC = data.fX2APIC;
8909 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8910 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8911 mHWData->mSpecCtrl = data.fSpecCtrl;
8912 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8913 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8914 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8915 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8916 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8917 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8918 mHWData->mCPUCount = data.cCPUs;
8919 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8920 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8921 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8922 mHWData->mCpuProfile = data.strCpuProfile;
8923
8924 // cpu
8925 if (mHWData->mCPUHotPlugEnabled)
8926 {
8927 for (settings::CpuList::const_iterator
8928 it = data.llCpus.begin();
8929 it != data.llCpus.end();
8930 ++it)
8931 {
8932 const settings::Cpu &cpu = *it;
8933
8934 mHWData->mCPUAttached[cpu.ulId] = true;
8935 }
8936 }
8937
8938 // cpuid leafs
8939 for (settings::CpuIdLeafsList::const_iterator
8940 it = data.llCpuIdLeafs.begin();
8941 it != data.llCpuIdLeafs.end();
8942 ++it)
8943 {
8944 const settings::CpuIdLeaf &rLeaf= *it;
8945 if ( rLeaf.idx < UINT32_C(0x20)
8946 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8947 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8948 mHWData->mCpuIdLeafList.push_back(rLeaf);
8949 /* else: just ignore */
8950 }
8951
8952 mHWData->mMemorySize = data.ulMemorySizeMB;
8953 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8954
8955 // boot order
8956 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8957 {
8958 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8959 if (it == data.mapBootOrder.end())
8960 mHWData->mBootOrder[i] = DeviceType_Null;
8961 else
8962 mHWData->mBootOrder[i] = it->second;
8963 }
8964
8965 mHWData->mFirmwareType = data.firmwareType;
8966 mHWData->mPointingHIDType = data.pointingHIDType;
8967 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8968 mHWData->mChipsetType = data.chipsetType;
8969 mHWData->mIommuType = data.iommuType;
8970 mHWData->mParavirtProvider = data.paravirtProvider;
8971 mHWData->mParavirtDebug = data.strParavirtDebug;
8972 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8973 mHWData->mHPETEnabled = data.fHPETEnabled;
8974
8975 /* GraphicsAdapter */
8976 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8977 if (FAILED(rc)) return rc;
8978
8979 /* VRDEServer */
8980 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8981 if (FAILED(rc)) return rc;
8982
8983 /* BIOS */
8984 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8985 if (FAILED(rc)) return rc;
8986
8987 /* Trusted Platform Module */
8988 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8989 if (FAILED(rc)) return rc;
8990
8991 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8992 if (FAILED(rc)) return rc;
8993
8994 /* Recording settings */
8995 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8996 if (FAILED(rc)) return rc;
8997
8998 // Bandwidth control (must come before network adapters)
8999 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9000 if (FAILED(rc)) return rc;
9001
9002 /* USB controllers */
9003 for (settings::USBControllerList::const_iterator
9004 it = data.usbSettings.llUSBControllers.begin();
9005 it != data.usbSettings.llUSBControllers.end();
9006 ++it)
9007 {
9008 const settings::USBController &settingsCtrl = *it;
9009 ComObjPtr<USBController> newCtrl;
9010
9011 newCtrl.createObject();
9012 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9013 mUSBControllers->push_back(newCtrl);
9014 }
9015
9016 /* USB device filters */
9017 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9018 if (FAILED(rc)) return rc;
9019
9020 // network adapters (establish array size first and apply defaults, to
9021 // ensure reading the same settings as we saved, since the list skips
9022 // adapters having defaults)
9023 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9024 size_t oldCount = mNetworkAdapters.size();
9025 if (newCount > oldCount)
9026 {
9027 mNetworkAdapters.resize(newCount);
9028 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9029 {
9030 unconst(mNetworkAdapters[slot]).createObject();
9031 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9032 }
9033 }
9034 else if (newCount < oldCount)
9035 mNetworkAdapters.resize(newCount);
9036 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9037 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9038 for (settings::NetworkAdaptersList::const_iterator
9039 it = data.llNetworkAdapters.begin();
9040 it != data.llNetworkAdapters.end();
9041 ++it)
9042 {
9043 const settings::NetworkAdapter &nic = *it;
9044
9045 /* slot uniqueness is guaranteed by XML Schema */
9046 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9047 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9048 if (FAILED(rc)) return rc;
9049 }
9050
9051 // serial ports (establish defaults first, to ensure reading the same
9052 // settings as we saved, since the list skips ports having defaults)
9053 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9054 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9055 for (settings::SerialPortsList::const_iterator
9056 it = data.llSerialPorts.begin();
9057 it != data.llSerialPorts.end();
9058 ++it)
9059 {
9060 const settings::SerialPort &s = *it;
9061
9062 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9063 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9064 if (FAILED(rc)) return rc;
9065 }
9066
9067 // parallel ports (establish defaults first, to ensure reading the same
9068 // settings as we saved, since the list skips ports having defaults)
9069 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9070 mParallelPorts[i]->i_applyDefaults();
9071 for (settings::ParallelPortsList::const_iterator
9072 it = data.llParallelPorts.begin();
9073 it != data.llParallelPorts.end();
9074 ++it)
9075 {
9076 const settings::ParallelPort &p = *it;
9077
9078 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9079 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9080 if (FAILED(rc)) return rc;
9081 }
9082
9083 /* AudioAdapter */
9084 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9085 if (FAILED(rc)) return rc;
9086
9087 /* storage controllers */
9088 rc = i_loadStorageControllers(data.storage,
9089 puuidRegistry,
9090 puuidSnapshot);
9091 if (FAILED(rc)) return rc;
9092
9093 /* Shared folders */
9094 for (settings::SharedFoldersList::const_iterator
9095 it = data.llSharedFolders.begin();
9096 it != data.llSharedFolders.end();
9097 ++it)
9098 {
9099 const settings::SharedFolder &sf = *it;
9100
9101 ComObjPtr<SharedFolder> sharedFolder;
9102 /* Check for double entries. Not allowed! */
9103 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9104 if (SUCCEEDED(rc))
9105 return setError(VBOX_E_OBJECT_IN_USE,
9106 tr("Shared folder named '%s' already exists"),
9107 sf.strName.c_str());
9108
9109 /* Create the new shared folder. Don't break on error. This will be
9110 * reported when the machine starts. */
9111 sharedFolder.createObject();
9112 rc = sharedFolder->init(i_getMachine(),
9113 sf.strName,
9114 sf.strHostPath,
9115 RT_BOOL(sf.fWritable),
9116 RT_BOOL(sf.fAutoMount),
9117 sf.strAutoMountPoint,
9118 false /* fFailOnError */);
9119 if (FAILED(rc)) return rc;
9120 mHWData->mSharedFolders.push_back(sharedFolder);
9121 }
9122
9123 // Clipboard
9124 mHWData->mClipboardMode = data.clipboardMode;
9125 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9126
9127 // drag'n'drop
9128 mHWData->mDnDMode = data.dndMode;
9129
9130 // guest settings
9131 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9132
9133 // IO settings
9134 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9135 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9136
9137 // Host PCI devices
9138 for (settings::HostPCIDeviceAttachmentList::const_iterator
9139 it = data.pciAttachments.begin();
9140 it != data.pciAttachments.end();
9141 ++it)
9142 {
9143 const settings::HostPCIDeviceAttachment &hpda = *it;
9144 ComObjPtr<PCIDeviceAttachment> pda;
9145
9146 pda.createObject();
9147 pda->i_loadSettings(this, hpda);
9148 mHWData->mPCIDeviceAssignments.push_back(pda);
9149 }
9150
9151 /*
9152 * (The following isn't really real hardware, but it lives in HWData
9153 * for reasons of convenience.)
9154 */
9155
9156#ifdef VBOX_WITH_GUEST_PROPS
9157 /* Guest properties (optional) */
9158
9159 /* Only load transient guest properties for configs which have saved
9160 * state, because there shouldn't be any for powered off VMs. The same
9161 * logic applies for snapshots, as offline snapshots shouldn't have
9162 * any such properties. They confuse the code in various places.
9163 * Note: can't rely on the machine state, as it isn't set yet. */
9164 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9165 /* apologies for the hacky unconst() usage, but this needs hacking
9166 * actually inconsistent settings into consistency, otherwise there
9167 * will be some corner cases where the inconsistency survives
9168 * surprisingly long without getting fixed, especially for snapshots
9169 * as there are no config changes. */
9170 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9171 for (settings::GuestPropertiesList::iterator
9172 it = llGuestProperties.begin();
9173 it != llGuestProperties.end();
9174 /*nothing*/)
9175 {
9176 const settings::GuestProperty &prop = *it;
9177 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9178 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9179 if ( fSkipTransientGuestProperties
9180 && ( fFlags & GUEST_PROP_F_TRANSIENT
9181 || fFlags & GUEST_PROP_F_TRANSRESET))
9182 {
9183 it = llGuestProperties.erase(it);
9184 continue;
9185 }
9186 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9187 mHWData->mGuestProperties[prop.strName] = property;
9188 ++it;
9189 }
9190#endif /* VBOX_WITH_GUEST_PROPS defined */
9191
9192 rc = i_loadDebugging(pDbg);
9193 if (FAILED(rc))
9194 return rc;
9195
9196 mHWData->mAutostart = *pAutostart;
9197
9198 /* default frontend */
9199 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9200 }
9201 catch (std::bad_alloc &)
9202 {
9203 return E_OUTOFMEMORY;
9204 }
9205
9206 AssertComRC(rc);
9207 return rc;
9208}
9209
9210/**
9211 * Called from i_loadHardware() to load the debugging settings of the
9212 * machine.
9213 *
9214 * @param pDbg Pointer to the settings.
9215 */
9216HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9217{
9218 mHWData->mDebugging = *pDbg;
9219 /* no more processing currently required, this will probably change. */
9220 return S_OK;
9221}
9222
9223/**
9224 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9225 *
9226 * @param data storage settings.
9227 * @param puuidRegistry media registry ID to set media to or NULL;
9228 * see Machine::i_loadMachineDataFromSettings()
9229 * @param puuidSnapshot snapshot ID
9230 * @return
9231 */
9232HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9233 const Guid *puuidRegistry,
9234 const Guid *puuidSnapshot)
9235{
9236 AssertReturn(!i_isSessionMachine(), E_FAIL);
9237
9238 HRESULT rc = S_OK;
9239
9240 for (settings::StorageControllersList::const_iterator
9241 it = data.llStorageControllers.begin();
9242 it != data.llStorageControllers.end();
9243 ++it)
9244 {
9245 const settings::StorageController &ctlData = *it;
9246
9247 ComObjPtr<StorageController> pCtl;
9248 /* Try to find one with the name first. */
9249 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9250 if (SUCCEEDED(rc))
9251 return setError(VBOX_E_OBJECT_IN_USE,
9252 tr("Storage controller named '%s' already exists"),
9253 ctlData.strName.c_str());
9254
9255 pCtl.createObject();
9256 rc = pCtl->init(this,
9257 ctlData.strName,
9258 ctlData.storageBus,
9259 ctlData.ulInstance,
9260 ctlData.fBootable);
9261 if (FAILED(rc)) return rc;
9262
9263 mStorageControllers->push_back(pCtl);
9264
9265 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9266 if (FAILED(rc)) return rc;
9267
9268 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9269 if (FAILED(rc)) return rc;
9270
9271 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9272 if (FAILED(rc)) return rc;
9273
9274 /* Load the attached devices now. */
9275 rc = i_loadStorageDevices(pCtl,
9276 ctlData,
9277 puuidRegistry,
9278 puuidSnapshot);
9279 if (FAILED(rc)) return rc;
9280 }
9281
9282 return S_OK;
9283}
9284
9285/**
9286 * Called from i_loadStorageControllers for a controller's devices.
9287 *
9288 * @param aStorageController
9289 * @param data
9290 * @param puuidRegistry media registry ID to set media to or NULL; see
9291 * Machine::i_loadMachineDataFromSettings()
9292 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9293 * @return
9294 */
9295HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9296 const settings::StorageController &data,
9297 const Guid *puuidRegistry,
9298 const Guid *puuidSnapshot)
9299{
9300 HRESULT rc = S_OK;
9301
9302 /* paranoia: detect duplicate attachments */
9303 for (settings::AttachedDevicesList::const_iterator
9304 it = data.llAttachedDevices.begin();
9305 it != data.llAttachedDevices.end();
9306 ++it)
9307 {
9308 const settings::AttachedDevice &ad = *it;
9309
9310 for (settings::AttachedDevicesList::const_iterator it2 = it;
9311 it2 != data.llAttachedDevices.end();
9312 ++it2)
9313 {
9314 if (it == it2)
9315 continue;
9316
9317 const settings::AttachedDevice &ad2 = *it2;
9318
9319 if ( ad.lPort == ad2.lPort
9320 && ad.lDevice == ad2.lDevice)
9321 {
9322 return setError(E_FAIL,
9323 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9324 aStorageController->i_getName().c_str(),
9325 ad.lPort,
9326 ad.lDevice,
9327 mUserData->s.strName.c_str());
9328 }
9329 }
9330 }
9331
9332 for (settings::AttachedDevicesList::const_iterator
9333 it = data.llAttachedDevices.begin();
9334 it != data.llAttachedDevices.end();
9335 ++it)
9336 {
9337 const settings::AttachedDevice &dev = *it;
9338 ComObjPtr<Medium> medium;
9339
9340 switch (dev.deviceType)
9341 {
9342 case DeviceType_Floppy:
9343 case DeviceType_DVD:
9344 if (dev.strHostDriveSrc.isNotEmpty())
9345 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9346 false /* fRefresh */, medium);
9347 else
9348 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9349 dev.uuid,
9350 false /* fRefresh */,
9351 false /* aSetError */,
9352 medium);
9353 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9354 // This is not an error. The host drive or UUID might have vanished, so just go
9355 // ahead without this removeable medium attachment
9356 rc = S_OK;
9357 break;
9358
9359 case DeviceType_HardDisk:
9360 {
9361 /* find a hard disk by UUID */
9362 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9363 if (FAILED(rc))
9364 {
9365 if (i_isSnapshotMachine())
9366 {
9367 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9368 // so the user knows that the bad disk is in a snapshot somewhere
9369 com::ErrorInfo info;
9370 return setError(E_FAIL,
9371 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9372 puuidSnapshot->raw(),
9373 info.getText().raw());
9374 }
9375 else
9376 return rc;
9377 }
9378
9379 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9380
9381 if (medium->i_getType() == MediumType_Immutable)
9382 {
9383 if (i_isSnapshotMachine())
9384 return setError(E_FAIL,
9385 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9386 "of the virtual machine '%s' ('%s')"),
9387 medium->i_getLocationFull().c_str(),
9388 dev.uuid.raw(),
9389 puuidSnapshot->raw(),
9390 mUserData->s.strName.c_str(),
9391 mData->m_strConfigFileFull.c_str());
9392
9393 return setError(E_FAIL,
9394 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9395 medium->i_getLocationFull().c_str(),
9396 dev.uuid.raw(),
9397 mUserData->s.strName.c_str(),
9398 mData->m_strConfigFileFull.c_str());
9399 }
9400
9401 if (medium->i_getType() == MediumType_MultiAttach)
9402 {
9403 if (i_isSnapshotMachine())
9404 return setError(E_FAIL,
9405 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9406 "of the virtual machine '%s' ('%s')"),
9407 medium->i_getLocationFull().c_str(),
9408 dev.uuid.raw(),
9409 puuidSnapshot->raw(),
9410 mUserData->s.strName.c_str(),
9411 mData->m_strConfigFileFull.c_str());
9412
9413 return setError(E_FAIL,
9414 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9415 medium->i_getLocationFull().c_str(),
9416 dev.uuid.raw(),
9417 mUserData->s.strName.c_str(),
9418 mData->m_strConfigFileFull.c_str());
9419 }
9420
9421 if ( !i_isSnapshotMachine()
9422 && medium->i_getChildren().size() != 0
9423 )
9424 return setError(E_FAIL,
9425 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9426 "because it has %d differencing child hard disks"),
9427 medium->i_getLocationFull().c_str(),
9428 dev.uuid.raw(),
9429 mUserData->s.strName.c_str(),
9430 mData->m_strConfigFileFull.c_str(),
9431 medium->i_getChildren().size());
9432
9433 if (i_findAttachment(*mMediumAttachments.data(),
9434 medium))
9435 return setError(E_FAIL,
9436 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9437 medium->i_getLocationFull().c_str(),
9438 dev.uuid.raw(),
9439 mUserData->s.strName.c_str(),
9440 mData->m_strConfigFileFull.c_str());
9441
9442 break;
9443 }
9444
9445 default:
9446 return setError(E_FAIL,
9447 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9448 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9449 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9450 }
9451
9452 if (FAILED(rc))
9453 break;
9454
9455 /* Bandwidth groups are loaded at this point. */
9456 ComObjPtr<BandwidthGroup> pBwGroup;
9457
9458 if (!dev.strBwGroup.isEmpty())
9459 {
9460 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9461 if (FAILED(rc))
9462 return setError(E_FAIL,
9463 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9464 medium->i_getLocationFull().c_str(),
9465 dev.strBwGroup.c_str(),
9466 mUserData->s.strName.c_str(),
9467 mData->m_strConfigFileFull.c_str());
9468 pBwGroup->i_reference();
9469 }
9470
9471 const Utf8Str controllerName = aStorageController->i_getName();
9472 ComObjPtr<MediumAttachment> pAttachment;
9473 pAttachment.createObject();
9474 rc = pAttachment->init(this,
9475 medium,
9476 controllerName,
9477 dev.lPort,
9478 dev.lDevice,
9479 dev.deviceType,
9480 false,
9481 dev.fPassThrough,
9482 dev.fTempEject,
9483 dev.fNonRotational,
9484 dev.fDiscard,
9485 dev.fHotPluggable,
9486 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9487 if (FAILED(rc)) break;
9488
9489 /* associate the medium with this machine and snapshot */
9490 if (!medium.isNull())
9491 {
9492 AutoCaller medCaller(medium);
9493 if (FAILED(medCaller.rc())) return medCaller.rc();
9494 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9495
9496 if (i_isSnapshotMachine())
9497 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9498 else
9499 rc = medium->i_addBackReference(mData->mUuid);
9500 /* If the medium->addBackReference fails it sets an appropriate
9501 * error message, so no need to do any guesswork here. */
9502
9503 if (puuidRegistry)
9504 // caller wants registry ID to be set on all attached media (OVF import case)
9505 medium->i_addRegistry(*puuidRegistry);
9506 }
9507
9508 if (FAILED(rc))
9509 break;
9510
9511 /* back up mMediumAttachments to let registeredInit() properly rollback
9512 * on failure (= limited accessibility) */
9513 i_setModified(IsModified_Storage);
9514 mMediumAttachments.backup();
9515 mMediumAttachments->push_back(pAttachment);
9516 }
9517
9518 return rc;
9519}
9520
9521/**
9522 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9523 *
9524 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9525 * @param aSnapshot where to return the found snapshot
9526 * @param aSetError true to set extended error info on failure
9527 */
9528HRESULT Machine::i_findSnapshotById(const Guid &aId,
9529 ComObjPtr<Snapshot> &aSnapshot,
9530 bool aSetError /* = false */)
9531{
9532 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9533
9534 if (!mData->mFirstSnapshot)
9535 {
9536 if (aSetError)
9537 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9538 return E_FAIL;
9539 }
9540
9541 if (aId.isZero())
9542 aSnapshot = mData->mFirstSnapshot;
9543 else
9544 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9545
9546 if (!aSnapshot)
9547 {
9548 if (aSetError)
9549 return setError(E_FAIL,
9550 tr("Could not find a snapshot with UUID {%s}"),
9551 aId.toString().c_str());
9552 return E_FAIL;
9553 }
9554
9555 return S_OK;
9556}
9557
9558/**
9559 * Returns the snapshot with the given name or fails of no such snapshot.
9560 *
9561 * @param strName snapshot name to find
9562 * @param aSnapshot where to return the found snapshot
9563 * @param aSetError true to set extended error info on failure
9564 */
9565HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9566 ComObjPtr<Snapshot> &aSnapshot,
9567 bool aSetError /* = false */)
9568{
9569 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9570
9571 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9572
9573 if (!mData->mFirstSnapshot)
9574 {
9575 if (aSetError)
9576 return setError(VBOX_E_OBJECT_NOT_FOUND,
9577 tr("This machine does not have any snapshots"));
9578 return VBOX_E_OBJECT_NOT_FOUND;
9579 }
9580
9581 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9582
9583 if (!aSnapshot)
9584 {
9585 if (aSetError)
9586 return setError(VBOX_E_OBJECT_NOT_FOUND,
9587 tr("Could not find a snapshot named '%s'"), strName.c_str());
9588 return VBOX_E_OBJECT_NOT_FOUND;
9589 }
9590
9591 return S_OK;
9592}
9593
9594/**
9595 * Returns a storage controller object with the given name.
9596 *
9597 * @param aName storage controller name to find
9598 * @param aStorageController where to return the found storage controller
9599 * @param aSetError true to set extended error info on failure
9600 */
9601HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9602 ComObjPtr<StorageController> &aStorageController,
9603 bool aSetError /* = false */)
9604{
9605 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9606
9607 for (StorageControllerList::const_iterator
9608 it = mStorageControllers->begin();
9609 it != mStorageControllers->end();
9610 ++it)
9611 {
9612 if ((*it)->i_getName() == aName)
9613 {
9614 aStorageController = (*it);
9615 return S_OK;
9616 }
9617 }
9618
9619 if (aSetError)
9620 return setError(VBOX_E_OBJECT_NOT_FOUND,
9621 tr("Could not find a storage controller named '%s'"),
9622 aName.c_str());
9623 return VBOX_E_OBJECT_NOT_FOUND;
9624}
9625
9626/**
9627 * Returns a USB controller object with the given name.
9628 *
9629 * @param aName USB controller name to find
9630 * @param aUSBController where to return the found USB controller
9631 * @param aSetError true to set extended error info on failure
9632 */
9633HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9634 ComObjPtr<USBController> &aUSBController,
9635 bool aSetError /* = false */)
9636{
9637 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9638
9639 for (USBControllerList::const_iterator
9640 it = mUSBControllers->begin();
9641 it != mUSBControllers->end();
9642 ++it)
9643 {
9644 if ((*it)->i_getName() == aName)
9645 {
9646 aUSBController = (*it);
9647 return S_OK;
9648 }
9649 }
9650
9651 if (aSetError)
9652 return setError(VBOX_E_OBJECT_NOT_FOUND,
9653 tr("Could not find a storage controller named '%s'"),
9654 aName.c_str());
9655 return VBOX_E_OBJECT_NOT_FOUND;
9656}
9657
9658/**
9659 * Returns the number of USB controller instance of the given type.
9660 *
9661 * @param enmType USB controller type.
9662 */
9663ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9664{
9665 ULONG cCtrls = 0;
9666
9667 for (USBControllerList::const_iterator
9668 it = mUSBControllers->begin();
9669 it != mUSBControllers->end();
9670 ++it)
9671 {
9672 if ((*it)->i_getControllerType() == enmType)
9673 cCtrls++;
9674 }
9675
9676 return cCtrls;
9677}
9678
9679HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9680 MediumAttachmentList &atts)
9681{
9682 AutoCaller autoCaller(this);
9683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9684
9685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9686
9687 for (MediumAttachmentList::const_iterator
9688 it = mMediumAttachments->begin();
9689 it != mMediumAttachments->end();
9690 ++it)
9691 {
9692 const ComObjPtr<MediumAttachment> &pAtt = *it;
9693 // should never happen, but deal with NULL pointers in the list.
9694 AssertContinue(!pAtt.isNull());
9695
9696 // getControllerName() needs caller+read lock
9697 AutoCaller autoAttCaller(pAtt);
9698 if (FAILED(autoAttCaller.rc()))
9699 {
9700 atts.clear();
9701 return autoAttCaller.rc();
9702 }
9703 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9704
9705 if (pAtt->i_getControllerName() == aName)
9706 atts.push_back(pAtt);
9707 }
9708
9709 return S_OK;
9710}
9711
9712
9713/**
9714 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9715 * file if the machine name was changed and about creating a new settings file
9716 * if this is a new machine.
9717 *
9718 * @note Must be never called directly but only from #saveSettings().
9719 */
9720HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9721 bool *pfSettingsFileIsNew)
9722{
9723 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9724
9725 HRESULT rc = S_OK;
9726
9727 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9728 /// @todo need to handle primary group change, too
9729
9730 /* attempt to rename the settings file if machine name is changed */
9731 if ( mUserData->s.fNameSync
9732 && mUserData.isBackedUp()
9733 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9734 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9735 )
9736 {
9737 bool dirRenamed = false;
9738 bool fileRenamed = false;
9739
9740 Utf8Str configFile, newConfigFile;
9741 Utf8Str configFilePrev, newConfigFilePrev;
9742 Utf8Str NVRAMFile, newNVRAMFile;
9743 Utf8Str configDir, newConfigDir;
9744
9745 do
9746 {
9747 int vrc = VINF_SUCCESS;
9748
9749 Utf8Str name = mUserData.backedUpData()->s.strName;
9750 Utf8Str newName = mUserData->s.strName;
9751 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9752 if (group == "/")
9753 group.setNull();
9754 Utf8Str newGroup = mUserData->s.llGroups.front();
9755 if (newGroup == "/")
9756 newGroup.setNull();
9757
9758 configFile = mData->m_strConfigFileFull;
9759
9760 /* first, rename the directory if it matches the group and machine name */
9761 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9762 /** @todo hack, make somehow use of ComposeMachineFilename */
9763 if (mUserData->s.fDirectoryIncludesUUID)
9764 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9765 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9766 /** @todo hack, make somehow use of ComposeMachineFilename */
9767 if (mUserData->s.fDirectoryIncludesUUID)
9768 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9769 configDir = configFile;
9770 configDir.stripFilename();
9771 newConfigDir = configDir;
9772 if ( configDir.length() >= groupPlusName.length()
9773 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9774 groupPlusName.c_str()))
9775 {
9776 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9777 Utf8Str newConfigBaseDir(newConfigDir);
9778 newConfigDir.append(newGroupPlusName);
9779 /* consistency: use \ if appropriate on the platform */
9780 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9781 /* new dir and old dir cannot be equal here because of 'if'
9782 * above and because name != newName */
9783 Assert(configDir != newConfigDir);
9784 if (!fSettingsFileIsNew)
9785 {
9786 /* perform real rename only if the machine is not new */
9787 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9788 if ( vrc == VERR_FILE_NOT_FOUND
9789 || vrc == VERR_PATH_NOT_FOUND)
9790 {
9791 /* create the parent directory, then retry renaming */
9792 Utf8Str parent(newConfigDir);
9793 parent.stripFilename();
9794 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9795 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9796 }
9797 if (RT_FAILURE(vrc))
9798 {
9799 rc = setErrorBoth(E_FAIL, vrc,
9800 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9801 configDir.c_str(),
9802 newConfigDir.c_str(),
9803 vrc);
9804 break;
9805 }
9806 /* delete subdirectories which are no longer needed */
9807 Utf8Str dir(configDir);
9808 dir.stripFilename();
9809 while (dir != newConfigBaseDir && dir != ".")
9810 {
9811 vrc = RTDirRemove(dir.c_str());
9812 if (RT_FAILURE(vrc))
9813 break;
9814 dir.stripFilename();
9815 }
9816 dirRenamed = true;
9817 }
9818 }
9819
9820 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9821
9822 /* then try to rename the settings file itself */
9823 if (newConfigFile != configFile)
9824 {
9825 /* get the path to old settings file in renamed directory */
9826 Assert(mData->m_strConfigFileFull == configFile);
9827 configFile.printf("%s%c%s",
9828 newConfigDir.c_str(),
9829 RTPATH_DELIMITER,
9830 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9831 if (!fSettingsFileIsNew)
9832 {
9833 /* perform real rename only if the machine is not new */
9834 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9835 if (RT_FAILURE(vrc))
9836 {
9837 rc = setErrorBoth(E_FAIL, vrc,
9838 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9839 configFile.c_str(),
9840 newConfigFile.c_str(),
9841 vrc);
9842 break;
9843 }
9844 fileRenamed = true;
9845 configFilePrev = configFile;
9846 configFilePrev += "-prev";
9847 newConfigFilePrev = newConfigFile;
9848 newConfigFilePrev += "-prev";
9849 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9850 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9851 if (NVRAMFile.isNotEmpty())
9852 {
9853 // in the NVRAM file path, replace the old directory with the new directory
9854 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9855 {
9856 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9857 NVRAMFile = newConfigDir + strNVRAMFile;
9858 }
9859 newNVRAMFile = newConfigFile;
9860 newNVRAMFile.stripSuffix();
9861 newNVRAMFile += ".nvram";
9862 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9863 }
9864 }
9865 }
9866
9867 // update m_strConfigFileFull amd mConfigFile
9868 mData->m_strConfigFileFull = newConfigFile;
9869 // compute the relative path too
9870 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9871
9872 // store the old and new so that VirtualBox::i_saveSettings() can update
9873 // the media registry
9874 if ( mData->mRegistered
9875 && (configDir != newConfigDir || configFile != newConfigFile))
9876 {
9877 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9878
9879 if (pfNeedsGlobalSaveSettings)
9880 *pfNeedsGlobalSaveSettings = true;
9881 }
9882
9883 // in the saved state file path, replace the old directory with the new directory
9884 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9885 {
9886 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9887 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9888 }
9889 if (newNVRAMFile.isNotEmpty())
9890 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9891
9892 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9893 if (mData->mFirstSnapshot)
9894 {
9895 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9896 newConfigDir.c_str());
9897 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9898 newConfigDir.c_str());
9899 }
9900 }
9901 while (0);
9902
9903 if (FAILED(rc))
9904 {
9905 /* silently try to rename everything back */
9906 if (fileRenamed)
9907 {
9908 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9909 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9910 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9911 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9912 }
9913 if (dirRenamed)
9914 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9915 }
9916
9917 if (FAILED(rc)) return rc;
9918 }
9919
9920 if (fSettingsFileIsNew)
9921 {
9922 /* create a virgin config file */
9923 int vrc = VINF_SUCCESS;
9924
9925 /* ensure the settings directory exists */
9926 Utf8Str path(mData->m_strConfigFileFull);
9927 path.stripFilename();
9928 if (!RTDirExists(path.c_str()))
9929 {
9930 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9931 if (RT_FAILURE(vrc))
9932 {
9933 return setErrorBoth(E_FAIL, vrc,
9934 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9935 path.c_str(),
9936 vrc);
9937 }
9938 }
9939
9940 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9941 path = mData->m_strConfigFileFull;
9942 RTFILE f = NIL_RTFILE;
9943 vrc = RTFileOpen(&f, path.c_str(),
9944 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9945 if (RT_FAILURE(vrc))
9946 return setErrorBoth(E_FAIL, vrc,
9947 tr("Could not create the settings file '%s' (%Rrc)"),
9948 path.c_str(),
9949 vrc);
9950 RTFileClose(f);
9951 }
9952 if (pfSettingsFileIsNew)
9953 *pfSettingsFileIsNew = fSettingsFileIsNew;
9954
9955 return rc;
9956}
9957
9958/**
9959 * Saves and commits machine data, user data and hardware data.
9960 *
9961 * Note that on failure, the data remains uncommitted.
9962 *
9963 * @a aFlags may combine the following flags:
9964 *
9965 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9966 * Used when saving settings after an operation that makes them 100%
9967 * correspond to the settings from the current snapshot.
9968 * - SaveS_Force: settings will be saved without doing a deep compare of the
9969 * settings structures. This is used when this is called because snapshots
9970 * have changed to avoid the overhead of the deep compare.
9971 *
9972 * @note Must be called from under this object's write lock. Locks children for
9973 * writing.
9974 *
9975 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9976 * initialized to false and that will be set to true by this function if
9977 * the caller must invoke VirtualBox::i_saveSettings() because the global
9978 * settings have changed. This will happen if a machine rename has been
9979 * saved and the global machine and media registries will therefore need
9980 * updating.
9981 * @param alock Reference to the lock for this machine object.
9982 * @param aFlags Flags.
9983 */
9984HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9985 AutoWriteLock &alock,
9986 int aFlags /*= 0*/)
9987{
9988 LogFlowThisFuncEnter();
9989
9990 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9991
9992 /* make sure child objects are unable to modify the settings while we are
9993 * saving them */
9994 i_ensureNoStateDependencies(alock);
9995
9996 AssertReturn(!i_isSnapshotMachine(),
9997 E_FAIL);
9998
9999 if (!mData->mAccessible)
10000 return setError(VBOX_E_INVALID_VM_STATE,
10001 tr("The machine is not accessible, so cannot save settings"));
10002
10003 HRESULT rc = S_OK;
10004 bool fNeedsWrite = false;
10005 bool fSettingsFileIsNew = false;
10006
10007 /* First, prepare to save settings. It will care about renaming the
10008 * settings directory and file if the machine name was changed and about
10009 * creating a new settings file if this is a new machine. */
10010 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10011 &fSettingsFileIsNew);
10012 if (FAILED(rc)) return rc;
10013
10014 // keep a pointer to the current settings structures
10015 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10016 settings::MachineConfigFile *pNewConfig = NULL;
10017
10018 try
10019 {
10020 // make a fresh one to have everyone write stuff into
10021 pNewConfig = new settings::MachineConfigFile(NULL);
10022 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10023
10024 // now go and copy all the settings data from COM to the settings structures
10025 // (this calls i_saveSettings() on all the COM objects in the machine)
10026 i_copyMachineDataToSettings(*pNewConfig);
10027
10028 if (aFlags & SaveS_ResetCurStateModified)
10029 {
10030 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10031 mData->mCurrentStateModified = FALSE;
10032 fNeedsWrite = true; // always, no need to compare
10033 }
10034 else if (aFlags & SaveS_Force)
10035 {
10036 fNeedsWrite = true; // always, no need to compare
10037 }
10038 else
10039 {
10040 if (!mData->mCurrentStateModified)
10041 {
10042 // do a deep compare of the settings that we just saved with the settings
10043 // previously stored in the config file; this invokes MachineConfigFile::operator==
10044 // which does a deep compare of all the settings, which is expensive but less expensive
10045 // than writing out XML in vain
10046 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10047
10048 // could still be modified if any settings changed
10049 mData->mCurrentStateModified = fAnySettingsChanged;
10050
10051 fNeedsWrite = fAnySettingsChanged;
10052 }
10053 else
10054 fNeedsWrite = true;
10055 }
10056
10057 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10058
10059 if (fNeedsWrite)
10060 // now spit it all out!
10061 pNewConfig->write(mData->m_strConfigFileFull);
10062
10063 mData->pMachineConfigFile = pNewConfig;
10064 delete pOldConfig;
10065 i_commit();
10066
10067 // after saving settings, we are no longer different from the XML on disk
10068 mData->flModifications = 0;
10069 }
10070 catch (HRESULT err)
10071 {
10072 // we assume that error info is set by the thrower
10073 rc = err;
10074
10075 // delete any newly created settings file
10076 if (fSettingsFileIsNew)
10077 RTFileDelete(mData->m_strConfigFileFull.c_str());
10078
10079 // restore old config
10080 delete pNewConfig;
10081 mData->pMachineConfigFile = pOldConfig;
10082 }
10083 catch (...)
10084 {
10085 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10086 }
10087
10088 if (fNeedsWrite)
10089 {
10090 /* Fire the data change event, even on failure (since we've already
10091 * committed all data). This is done only for SessionMachines because
10092 * mutable Machine instances are always not registered (i.e. private
10093 * to the client process that creates them) and thus don't need to
10094 * inform callbacks. */
10095 if (i_isSessionMachine())
10096 mParent->i_onMachineDataChanged(mData->mUuid);
10097 }
10098
10099 LogFlowThisFunc(("rc=%08X\n", rc));
10100 LogFlowThisFuncLeave();
10101 return rc;
10102}
10103
10104/**
10105 * Implementation for saving the machine settings into the given
10106 * settings::MachineConfigFile instance. This copies machine extradata
10107 * from the previous machine config file in the instance data, if any.
10108 *
10109 * This gets called from two locations:
10110 *
10111 * -- Machine::i_saveSettings(), during the regular XML writing;
10112 *
10113 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10114 * exported to OVF and we write the VirtualBox proprietary XML
10115 * into a <vbox:Machine> tag.
10116 *
10117 * This routine fills all the fields in there, including snapshots, *except*
10118 * for the following:
10119 *
10120 * -- fCurrentStateModified. There is some special logic associated with that.
10121 *
10122 * The caller can then call MachineConfigFile::write() or do something else
10123 * with it.
10124 *
10125 * Caller must hold the machine lock!
10126 *
10127 * This throws XML errors and HRESULT, so the caller must have a catch block!
10128 */
10129void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10130{
10131 // deep copy extradata, being extra careful with self assignment (the STL
10132 // map assignment on Mac OS X clang based Xcode isn't checking)
10133 if (&config != mData->pMachineConfigFile)
10134 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10135
10136 config.uuid = mData->mUuid;
10137
10138 // copy name, description, OS type, teleport, UTC etc.
10139 config.machineUserData = mUserData->s;
10140
10141 if ( mData->mMachineState == MachineState_Saved
10142 || mData->mMachineState == MachineState_AbortedSaved
10143 || mData->mMachineState == MachineState_Restoring
10144 // when doing certain snapshot operations we may or may not have
10145 // a saved state in the current state, so keep everything as is
10146 || ( ( mData->mMachineState == MachineState_Snapshotting
10147 || mData->mMachineState == MachineState_DeletingSnapshot
10148 || mData->mMachineState == MachineState_RestoringSnapshot)
10149 && (!mSSData->strStateFilePath.isEmpty())
10150 )
10151 )
10152 {
10153 Assert(!mSSData->strStateFilePath.isEmpty());
10154 /* try to make the file name relative to the settings file dir */
10155 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10156 }
10157 else
10158 {
10159 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10160 config.strStateFile.setNull();
10161 }
10162
10163 if (mData->mCurrentSnapshot)
10164 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10165 else
10166 config.uuidCurrentSnapshot.clear();
10167
10168 config.timeLastStateChange = mData->mLastStateChange;
10169 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10170 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10171
10172 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10173 if (FAILED(rc)) throw rc;
10174
10175 // save machine's media registry if this is VirtualBox 4.0 or later
10176 if (config.canHaveOwnMediaRegistry())
10177 {
10178 // determine machine folder
10179 Utf8Str strMachineFolder = i_getSettingsFileFull();
10180 strMachineFolder.stripFilename();
10181 mParent->i_saveMediaRegistry(config.mediaRegistry,
10182 i_getId(), // only media with registry ID == machine UUID
10183 strMachineFolder);
10184 // this throws HRESULT
10185 }
10186
10187 // save snapshots
10188 rc = i_saveAllSnapshots(config);
10189 if (FAILED(rc)) throw rc;
10190}
10191
10192/**
10193 * Saves all snapshots of the machine into the given machine config file. Called
10194 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10195 * @param config
10196 * @return
10197 */
10198HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10199{
10200 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10201
10202 HRESULT rc = S_OK;
10203
10204 try
10205 {
10206 config.llFirstSnapshot.clear();
10207
10208 if (mData->mFirstSnapshot)
10209 {
10210 // the settings use a list for "the first snapshot"
10211 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10212
10213 // get reference to the snapshot on the list and work on that
10214 // element straight in the list to avoid excessive copying later
10215 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10216 if (FAILED(rc)) throw rc;
10217 }
10218
10219// if (mType == IsSessionMachine)
10220// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10221
10222 }
10223 catch (HRESULT err)
10224 {
10225 /* we assume that error info is set by the thrower */
10226 rc = err;
10227 }
10228 catch (...)
10229 {
10230 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10231 }
10232
10233 return rc;
10234}
10235
10236/**
10237 * Saves the VM hardware configuration. It is assumed that the
10238 * given node is empty.
10239 *
10240 * @param data Reference to the settings object for the hardware config.
10241 * @param pDbg Pointer to the settings object for the debugging config
10242 * which happens to live in mHWData.
10243 * @param pAutostart Pointer to the settings object for the autostart config
10244 * which happens to live in mHWData.
10245 */
10246HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10247 settings::Autostart *pAutostart)
10248{
10249 HRESULT rc = S_OK;
10250
10251 try
10252 {
10253 /* The hardware version attribute (optional).
10254 Automatically upgrade from 1 to current default hardware version
10255 when there is no saved state. (ugly!) */
10256 if ( mHWData->mHWVersion == "1"
10257 && mSSData->strStateFilePath.isEmpty()
10258 )
10259 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10260
10261 data.strVersion = mHWData->mHWVersion;
10262 data.uuid = mHWData->mHardwareUUID;
10263
10264 // CPU
10265 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10266 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10267 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10268 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10269 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10270 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10271 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10272 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10273 data.fPAE = !!mHWData->mPAEEnabled;
10274 data.enmLongMode = mHWData->mLongMode;
10275 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10276 data.fAPIC = !!mHWData->mAPIC;
10277 data.fX2APIC = !!mHWData->mX2APIC;
10278 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10279 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10280 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10281 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10282 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10283 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10284 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10285 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10286 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10287 data.cCPUs = mHWData->mCPUCount;
10288 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10289 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10290 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10291 data.strCpuProfile = mHWData->mCpuProfile;
10292
10293 data.llCpus.clear();
10294 if (data.fCpuHotPlug)
10295 {
10296 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10297 {
10298 if (mHWData->mCPUAttached[idx])
10299 {
10300 settings::Cpu cpu;
10301 cpu.ulId = idx;
10302 data.llCpus.push_back(cpu);
10303 }
10304 }
10305 }
10306
10307 /* Standard and Extended CPUID leafs. */
10308 data.llCpuIdLeafs.clear();
10309 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10310
10311 // memory
10312 data.ulMemorySizeMB = mHWData->mMemorySize;
10313 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10314
10315 // firmware
10316 data.firmwareType = mHWData->mFirmwareType;
10317
10318 // HID
10319 data.pointingHIDType = mHWData->mPointingHIDType;
10320 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10321
10322 // chipset
10323 data.chipsetType = mHWData->mChipsetType;
10324
10325 // iommu
10326 data.iommuType = mHWData->mIommuType;
10327
10328 // paravirt
10329 data.paravirtProvider = mHWData->mParavirtProvider;
10330 data.strParavirtDebug = mHWData->mParavirtDebug;
10331
10332 // emulated USB card reader
10333 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10334
10335 // HPET
10336 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10337
10338 // boot order
10339 data.mapBootOrder.clear();
10340 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10341 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10342
10343 /* VRDEServer settings (optional) */
10344 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10345 if (FAILED(rc)) throw rc;
10346
10347 /* BIOS settings (required) */
10348 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10349 if (FAILED(rc)) throw rc;
10350
10351 /* Trusted Platform Module settings (required) */
10352 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10353 if (FAILED(rc)) throw rc;
10354
10355 /* NVRAM settings (required) */
10356 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10357 if (FAILED(rc)) throw rc;
10358
10359 /* Recording settings (required) */
10360 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10361 if (FAILED(rc)) throw rc;
10362
10363 /* GraphicsAdapter settings (required) */
10364 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10365 if (FAILED(rc)) throw rc;
10366
10367 /* USB Controller (required) */
10368 data.usbSettings.llUSBControllers.clear();
10369 for (USBControllerList::const_iterator
10370 it = mUSBControllers->begin();
10371 it != mUSBControllers->end();
10372 ++it)
10373 {
10374 ComObjPtr<USBController> ctrl = *it;
10375 settings::USBController settingsCtrl;
10376
10377 settingsCtrl.strName = ctrl->i_getName();
10378 settingsCtrl.enmType = ctrl->i_getControllerType();
10379
10380 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10381 }
10382
10383 /* USB device filters (required) */
10384 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10385 if (FAILED(rc)) throw rc;
10386
10387 /* Network adapters (required) */
10388 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10389 data.llNetworkAdapters.clear();
10390 /* Write out only the nominal number of network adapters for this
10391 * chipset type. Since Machine::commit() hasn't been called there
10392 * may be extra NIC settings in the vector. */
10393 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10394 {
10395 settings::NetworkAdapter nic;
10396 nic.ulSlot = (uint32_t)slot;
10397 /* paranoia check... must not be NULL, but must not crash either. */
10398 if (mNetworkAdapters[slot])
10399 {
10400 if (mNetworkAdapters[slot]->i_hasDefaults())
10401 continue;
10402
10403 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10404 if (FAILED(rc)) throw rc;
10405
10406 data.llNetworkAdapters.push_back(nic);
10407 }
10408 }
10409
10410 /* Serial ports */
10411 data.llSerialPorts.clear();
10412 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10413 {
10414 if (mSerialPorts[slot]->i_hasDefaults())
10415 continue;
10416
10417 settings::SerialPort s;
10418 s.ulSlot = slot;
10419 rc = mSerialPorts[slot]->i_saveSettings(s);
10420 if (FAILED(rc)) return rc;
10421
10422 data.llSerialPorts.push_back(s);
10423 }
10424
10425 /* Parallel ports */
10426 data.llParallelPorts.clear();
10427 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10428 {
10429 if (mParallelPorts[slot]->i_hasDefaults())
10430 continue;
10431
10432 settings::ParallelPort p;
10433 p.ulSlot = slot;
10434 rc = mParallelPorts[slot]->i_saveSettings(p);
10435 if (FAILED(rc)) return rc;
10436
10437 data.llParallelPorts.push_back(p);
10438 }
10439
10440 /* Audio adapter */
10441 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10442 if (FAILED(rc)) return rc;
10443
10444 rc = i_saveStorageControllers(data.storage);
10445 if (FAILED(rc)) return rc;
10446
10447 /* Shared folders */
10448 data.llSharedFolders.clear();
10449 for (HWData::SharedFolderList::const_iterator
10450 it = mHWData->mSharedFolders.begin();
10451 it != mHWData->mSharedFolders.end();
10452 ++it)
10453 {
10454 SharedFolder *pSF = *it;
10455 AutoCaller sfCaller(pSF);
10456 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10457 settings::SharedFolder sf;
10458 sf.strName = pSF->i_getName();
10459 sf.strHostPath = pSF->i_getHostPath();
10460 sf.fWritable = !!pSF->i_isWritable();
10461 sf.fAutoMount = !!pSF->i_isAutoMounted();
10462 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10463
10464 data.llSharedFolders.push_back(sf);
10465 }
10466
10467 // clipboard
10468 data.clipboardMode = mHWData->mClipboardMode;
10469 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10470
10471 // drag'n'drop
10472 data.dndMode = mHWData->mDnDMode;
10473
10474 /* Guest */
10475 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10476
10477 // IO settings
10478 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10479 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10480
10481 /* BandwidthControl (required) */
10482 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10483 if (FAILED(rc)) throw rc;
10484
10485 /* Host PCI devices */
10486 data.pciAttachments.clear();
10487 for (HWData::PCIDeviceAssignmentList::const_iterator
10488 it = mHWData->mPCIDeviceAssignments.begin();
10489 it != mHWData->mPCIDeviceAssignments.end();
10490 ++it)
10491 {
10492 ComObjPtr<PCIDeviceAttachment> pda = *it;
10493 settings::HostPCIDeviceAttachment hpda;
10494
10495 rc = pda->i_saveSettings(hpda);
10496 if (FAILED(rc)) throw rc;
10497
10498 data.pciAttachments.push_back(hpda);
10499 }
10500
10501 // guest properties
10502 data.llGuestProperties.clear();
10503#ifdef VBOX_WITH_GUEST_PROPS
10504 for (HWData::GuestPropertyMap::const_iterator
10505 it = mHWData->mGuestProperties.begin();
10506 it != mHWData->mGuestProperties.end();
10507 ++it)
10508 {
10509 HWData::GuestProperty property = it->second;
10510
10511 /* Remove transient guest properties at shutdown unless we
10512 * are saving state. Note that restoring snapshot intentionally
10513 * keeps them, they will be removed if appropriate once the final
10514 * machine state is set (as crashes etc. need to work). */
10515 if ( ( mData->mMachineState == MachineState_PoweredOff
10516 || mData->mMachineState == MachineState_Aborted
10517 || mData->mMachineState == MachineState_Teleported)
10518 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10519 continue;
10520 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10521 prop.strName = it->first;
10522 prop.strValue = property.strValue;
10523 prop.timestamp = (uint64_t)property.mTimestamp;
10524 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10525 GuestPropWriteFlags(property.mFlags, szFlags);
10526 prop.strFlags = szFlags;
10527
10528 data.llGuestProperties.push_back(prop);
10529 }
10530
10531 /* I presume this doesn't require a backup(). */
10532 mData->mGuestPropertiesModified = FALSE;
10533#endif /* VBOX_WITH_GUEST_PROPS defined */
10534
10535 *pDbg = mHWData->mDebugging;
10536 *pAutostart = mHWData->mAutostart;
10537
10538 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10539 }
10540 catch (std::bad_alloc &)
10541 {
10542 return E_OUTOFMEMORY;
10543 }
10544
10545 AssertComRC(rc);
10546 return rc;
10547}
10548
10549/**
10550 * Saves the storage controller configuration.
10551 *
10552 * @param data storage settings.
10553 */
10554HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10555{
10556 data.llStorageControllers.clear();
10557
10558 for (StorageControllerList::const_iterator
10559 it = mStorageControllers->begin();
10560 it != mStorageControllers->end();
10561 ++it)
10562 {
10563 HRESULT rc;
10564 ComObjPtr<StorageController> pCtl = *it;
10565
10566 settings::StorageController ctl;
10567 ctl.strName = pCtl->i_getName();
10568 ctl.controllerType = pCtl->i_getControllerType();
10569 ctl.storageBus = pCtl->i_getStorageBus();
10570 ctl.ulInstance = pCtl->i_getInstance();
10571 ctl.fBootable = pCtl->i_getBootable();
10572
10573 /* Save the port count. */
10574 ULONG portCount;
10575 rc = pCtl->COMGETTER(PortCount)(&portCount);
10576 ComAssertComRCRet(rc, rc);
10577 ctl.ulPortCount = portCount;
10578
10579 /* Save fUseHostIOCache */
10580 BOOL fUseHostIOCache;
10581 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10582 ComAssertComRCRet(rc, rc);
10583 ctl.fUseHostIOCache = !!fUseHostIOCache;
10584
10585 /* save the devices now. */
10586 rc = i_saveStorageDevices(pCtl, ctl);
10587 ComAssertComRCRet(rc, rc);
10588
10589 data.llStorageControllers.push_back(ctl);
10590 }
10591
10592 return S_OK;
10593}
10594
10595/**
10596 * Saves the hard disk configuration.
10597 */
10598HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10599 settings::StorageController &data)
10600{
10601 MediumAttachmentList atts;
10602
10603 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10604 if (FAILED(rc)) return rc;
10605
10606 data.llAttachedDevices.clear();
10607 for (MediumAttachmentList::const_iterator
10608 it = atts.begin();
10609 it != atts.end();
10610 ++it)
10611 {
10612 settings::AttachedDevice dev;
10613 IMediumAttachment *iA = *it;
10614 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10615 Medium *pMedium = pAttach->i_getMedium();
10616
10617 dev.deviceType = pAttach->i_getType();
10618 dev.lPort = pAttach->i_getPort();
10619 dev.lDevice = pAttach->i_getDevice();
10620 dev.fPassThrough = pAttach->i_getPassthrough();
10621 dev.fHotPluggable = pAttach->i_getHotPluggable();
10622 if (pMedium)
10623 {
10624 if (pMedium->i_isHostDrive())
10625 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10626 else
10627 dev.uuid = pMedium->i_getId();
10628 dev.fTempEject = pAttach->i_getTempEject();
10629 dev.fNonRotational = pAttach->i_getNonRotational();
10630 dev.fDiscard = pAttach->i_getDiscard();
10631 }
10632
10633 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10634
10635 data.llAttachedDevices.push_back(dev);
10636 }
10637
10638 return S_OK;
10639}
10640
10641/**
10642 * Saves machine state settings as defined by aFlags
10643 * (SaveSTS_* values).
10644 *
10645 * @param aFlags Combination of SaveSTS_* flags.
10646 *
10647 * @note Locks objects for writing.
10648 */
10649HRESULT Machine::i_saveStateSettings(int aFlags)
10650{
10651 if (aFlags == 0)
10652 return S_OK;
10653
10654 AutoCaller autoCaller(this);
10655 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10656
10657 /* This object's write lock is also necessary to serialize file access
10658 * (prevent concurrent reads and writes) */
10659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10660
10661 HRESULT rc = S_OK;
10662
10663 Assert(mData->pMachineConfigFile);
10664
10665 try
10666 {
10667 if (aFlags & SaveSTS_CurStateModified)
10668 mData->pMachineConfigFile->fCurrentStateModified = true;
10669
10670 if (aFlags & SaveSTS_StateFilePath)
10671 {
10672 if (!mSSData->strStateFilePath.isEmpty())
10673 /* try to make the file name relative to the settings file dir */
10674 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10675 else
10676 mData->pMachineConfigFile->strStateFile.setNull();
10677 }
10678
10679 if (aFlags & SaveSTS_StateTimeStamp)
10680 {
10681 Assert( mData->mMachineState != MachineState_Aborted
10682 || mSSData->strStateFilePath.isEmpty());
10683
10684 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10685
10686 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10687 || mData->mMachineState == MachineState_AbortedSaved);
10688/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10689 }
10690
10691 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10692 }
10693 catch (...)
10694 {
10695 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10696 }
10697
10698 return rc;
10699}
10700
10701/**
10702 * Ensures that the given medium is added to a media registry. If this machine
10703 * was created with 4.0 or later, then the machine registry is used. Otherwise
10704 * the global VirtualBox media registry is used.
10705 *
10706 * Caller must NOT hold machine lock, media tree or any medium locks!
10707 *
10708 * @param pMedium
10709 */
10710void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10711{
10712 /* Paranoia checks: do not hold machine or media tree locks. */
10713 AssertReturnVoid(!isWriteLockOnCurrentThread());
10714 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10715
10716 ComObjPtr<Medium> pBase;
10717 {
10718 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10719 pBase = pMedium->i_getBase();
10720 }
10721
10722 /* Paranoia checks: do not hold medium locks. */
10723 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10724 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10725
10726 // decide which medium registry to use now that the medium is attached:
10727 Guid uuid;
10728 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10729 if (fCanHaveOwnMediaRegistry)
10730 // machine XML is VirtualBox 4.0 or higher:
10731 uuid = i_getId(); // machine UUID
10732 else
10733 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10734
10735 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10736 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10737 if (pMedium->i_addRegistry(uuid))
10738 mParent->i_markRegistryModified(uuid);
10739
10740 /* For more complex hard disk structures it can happen that the base
10741 * medium isn't yet associated with any medium registry. Do that now. */
10742 if (pMedium != pBase)
10743 {
10744 /* Tree lock needed by Medium::addRegistryAll. */
10745 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10746 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
10747 {
10748 treeLock.release();
10749 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10750 treeLock.acquire();
10751 }
10752 if (pBase->i_addRegistryAll(uuid))
10753 {
10754 treeLock.release();
10755 mParent->i_markRegistryModified(uuid);
10756 }
10757 }
10758}
10759
10760/**
10761 * Creates differencing hard disks for all normal hard disks attached to this
10762 * machine and a new set of attachments to refer to created disks.
10763 *
10764 * Used when taking a snapshot or when deleting the current state. Gets called
10765 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10766 *
10767 * This method assumes that mMediumAttachments contains the original hard disk
10768 * attachments it needs to create diffs for. On success, these attachments will
10769 * be replaced with the created diffs.
10770 *
10771 * Attachments with non-normal hard disks are left as is.
10772 *
10773 * If @a aOnline is @c false then the original hard disks that require implicit
10774 * diffs will be locked for reading. Otherwise it is assumed that they are
10775 * already locked for writing (when the VM was started). Note that in the latter
10776 * case it is responsibility of the caller to lock the newly created diffs for
10777 * writing if this method succeeds.
10778 *
10779 * @param aProgress Progress object to run (must contain at least as
10780 * many operations left as the number of hard disks
10781 * attached).
10782 * @param aWeight Weight of this operation.
10783 * @param aOnline Whether the VM was online prior to this operation.
10784 *
10785 * @note The progress object is not marked as completed, neither on success nor
10786 * on failure. This is a responsibility of the caller.
10787 *
10788 * @note Locks this object and the media tree for writing.
10789 */
10790HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10791 ULONG aWeight,
10792 bool aOnline)
10793{
10794 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10795
10796 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10797 AssertReturn(!!pProgressControl, E_INVALIDARG);
10798
10799 AutoCaller autoCaller(this);
10800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10801
10802 AutoMultiWriteLock2 alock(this->lockHandle(),
10803 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10804
10805 /* must be in a protective state because we release the lock below */
10806 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10807 || mData->mMachineState == MachineState_OnlineSnapshotting
10808 || mData->mMachineState == MachineState_LiveSnapshotting
10809 || mData->mMachineState == MachineState_RestoringSnapshot
10810 || mData->mMachineState == MachineState_DeletingSnapshot
10811 , E_FAIL);
10812
10813 HRESULT rc = S_OK;
10814
10815 // use appropriate locked media map (online or offline)
10816 MediumLockListMap lockedMediaOffline;
10817 MediumLockListMap *lockedMediaMap;
10818 if (aOnline)
10819 lockedMediaMap = &mData->mSession.mLockedMedia;
10820 else
10821 lockedMediaMap = &lockedMediaOffline;
10822
10823 try
10824 {
10825 if (!aOnline)
10826 {
10827 /* lock all attached hard disks early to detect "in use"
10828 * situations before creating actual diffs */
10829 for (MediumAttachmentList::const_iterator
10830 it = mMediumAttachments->begin();
10831 it != mMediumAttachments->end();
10832 ++it)
10833 {
10834 MediumAttachment *pAtt = *it;
10835 if (pAtt->i_getType() == DeviceType_HardDisk)
10836 {
10837 Medium *pMedium = pAtt->i_getMedium();
10838 Assert(pMedium);
10839
10840 MediumLockList *pMediumLockList(new MediumLockList());
10841 alock.release();
10842 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10843 NULL /* pToLockWrite */,
10844 false /* fMediumLockWriteAll */,
10845 NULL,
10846 *pMediumLockList);
10847 alock.acquire();
10848 if (FAILED(rc))
10849 {
10850 delete pMediumLockList;
10851 throw rc;
10852 }
10853 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10854 if (FAILED(rc))
10855 {
10856 throw setError(rc,
10857 tr("Collecting locking information for all attached media failed"));
10858 }
10859 }
10860 }
10861
10862 /* Now lock all media. If this fails, nothing is locked. */
10863 alock.release();
10864 rc = lockedMediaMap->Lock();
10865 alock.acquire();
10866 if (FAILED(rc))
10867 {
10868 throw setError(rc,
10869 tr("Locking of attached media failed"));
10870 }
10871 }
10872
10873 /* remember the current list (note that we don't use backup() since
10874 * mMediumAttachments may be already backed up) */
10875 MediumAttachmentList atts = *mMediumAttachments.data();
10876
10877 /* start from scratch */
10878 mMediumAttachments->clear();
10879
10880 /* go through remembered attachments and create diffs for normal hard
10881 * disks and attach them */
10882 for (MediumAttachmentList::const_iterator
10883 it = atts.begin();
10884 it != atts.end();
10885 ++it)
10886 {
10887 MediumAttachment *pAtt = *it;
10888
10889 DeviceType_T devType = pAtt->i_getType();
10890 Medium *pMedium = pAtt->i_getMedium();
10891
10892 if ( devType != DeviceType_HardDisk
10893 || pMedium == NULL
10894 || pMedium->i_getType() != MediumType_Normal)
10895 {
10896 /* copy the attachment as is */
10897
10898 /** @todo the progress object created in SessionMachine::TakeSnaphot
10899 * only expects operations for hard disks. Later other
10900 * device types need to show up in the progress as well. */
10901 if (devType == DeviceType_HardDisk)
10902 {
10903 if (pMedium == NULL)
10904 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10905 aWeight); // weight
10906 else
10907 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10908 pMedium->i_getBase()->i_getName().c_str()).raw(),
10909 aWeight); // weight
10910 }
10911
10912 mMediumAttachments->push_back(pAtt);
10913 continue;
10914 }
10915
10916 /* need a diff */
10917 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10918 pMedium->i_getBase()->i_getName().c_str()).raw(),
10919 aWeight); // weight
10920
10921 Utf8Str strFullSnapshotFolder;
10922 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10923
10924 ComObjPtr<Medium> diff;
10925 diff.createObject();
10926 // store the diff in the same registry as the parent
10927 // (this cannot fail here because we can't create implicit diffs for
10928 // unregistered images)
10929 Guid uuidRegistryParent;
10930 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10931 Assert(fInRegistry); NOREF(fInRegistry);
10932 rc = diff->init(mParent,
10933 pMedium->i_getPreferredDiffFormat(),
10934 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10935 uuidRegistryParent,
10936 DeviceType_HardDisk);
10937 if (FAILED(rc)) throw rc;
10938
10939 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10940 * the push_back? Looks like we're going to release medium with the
10941 * wrong kind of lock (general issue with if we fail anywhere at all)
10942 * and an orphaned VDI in the snapshots folder. */
10943
10944 /* update the appropriate lock list */
10945 MediumLockList *pMediumLockList;
10946 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10947 AssertComRCThrowRC(rc);
10948 if (aOnline)
10949 {
10950 alock.release();
10951 /* The currently attached medium will be read-only, change
10952 * the lock type to read. */
10953 rc = pMediumLockList->Update(pMedium, false);
10954 alock.acquire();
10955 AssertComRCThrowRC(rc);
10956 }
10957
10958 /* release the locks before the potentially lengthy operation */
10959 alock.release();
10960 rc = pMedium->i_createDiffStorage(diff,
10961 pMedium->i_getPreferredDiffVariant(),
10962 pMediumLockList,
10963 NULL /* aProgress */,
10964 true /* aWait */,
10965 false /* aNotify */);
10966 alock.acquire();
10967 if (FAILED(rc)) throw rc;
10968
10969 /* actual lock list update is done in Machine::i_commitMedia */
10970
10971 rc = diff->i_addBackReference(mData->mUuid);
10972 AssertComRCThrowRC(rc);
10973
10974 /* add a new attachment */
10975 ComObjPtr<MediumAttachment> attachment;
10976 attachment.createObject();
10977 rc = attachment->init(this,
10978 diff,
10979 pAtt->i_getControllerName(),
10980 pAtt->i_getPort(),
10981 pAtt->i_getDevice(),
10982 DeviceType_HardDisk,
10983 true /* aImplicit */,
10984 false /* aPassthrough */,
10985 false /* aTempEject */,
10986 pAtt->i_getNonRotational(),
10987 pAtt->i_getDiscard(),
10988 pAtt->i_getHotPluggable(),
10989 pAtt->i_getBandwidthGroup());
10990 if (FAILED(rc)) throw rc;
10991
10992 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10993 AssertComRCThrowRC(rc);
10994 mMediumAttachments->push_back(attachment);
10995 }
10996 }
10997 catch (HRESULT aRC) { rc = aRC; }
10998
10999 /* unlock all hard disks we locked when there is no VM */
11000 if (!aOnline)
11001 {
11002 ErrorInfoKeeper eik;
11003
11004 HRESULT rc1 = lockedMediaMap->Clear();
11005 AssertComRC(rc1);
11006 }
11007
11008 return rc;
11009}
11010
11011/**
11012 * Deletes implicit differencing hard disks created either by
11013 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11014 * mMediumAttachments.
11015 *
11016 * Note that to delete hard disks created by #attachDevice() this method is
11017 * called from #i_rollbackMedia() when the changes are rolled back.
11018 *
11019 * @note Locks this object and the media tree for writing.
11020 */
11021HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11022{
11023 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11024
11025 AutoCaller autoCaller(this);
11026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11027
11028 AutoMultiWriteLock2 alock(this->lockHandle(),
11029 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11030
11031 /* We absolutely must have backed up state. */
11032 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11033
11034 /* Check if there are any implicitly created diff images. */
11035 bool fImplicitDiffs = false;
11036 for (MediumAttachmentList::const_iterator
11037 it = mMediumAttachments->begin();
11038 it != mMediumAttachments->end();
11039 ++it)
11040 {
11041 const ComObjPtr<MediumAttachment> &pAtt = *it;
11042 if (pAtt->i_isImplicit())
11043 {
11044 fImplicitDiffs = true;
11045 break;
11046 }
11047 }
11048 /* If there is nothing to do, leave early. This saves lots of image locking
11049 * effort. It also avoids a MachineStateChanged event without real reason.
11050 * This is important e.g. when loading a VM config, because there should be
11051 * no events. Otherwise API clients can become thoroughly confused for
11052 * inaccessible VMs (the code for loading VM configs uses this method for
11053 * cleanup if the config makes no sense), as they take such events as an
11054 * indication that the VM is alive, and they would force the VM config to
11055 * be reread, leading to an endless loop. */
11056 if (!fImplicitDiffs)
11057 return S_OK;
11058
11059 HRESULT rc = S_OK;
11060 MachineState_T oldState = mData->mMachineState;
11061
11062 /* will release the lock before the potentially lengthy operation,
11063 * so protect with the special state (unless already protected) */
11064 if ( oldState != MachineState_Snapshotting
11065 && oldState != MachineState_OnlineSnapshotting
11066 && oldState != MachineState_LiveSnapshotting
11067 && oldState != MachineState_RestoringSnapshot
11068 && oldState != MachineState_DeletingSnapshot
11069 && oldState != MachineState_DeletingSnapshotOnline
11070 && oldState != MachineState_DeletingSnapshotPaused
11071 )
11072 i_setMachineState(MachineState_SettingUp);
11073
11074 // use appropriate locked media map (online or offline)
11075 MediumLockListMap lockedMediaOffline;
11076 MediumLockListMap *lockedMediaMap;
11077 if (aOnline)
11078 lockedMediaMap = &mData->mSession.mLockedMedia;
11079 else
11080 lockedMediaMap = &lockedMediaOffline;
11081
11082 try
11083 {
11084 if (!aOnline)
11085 {
11086 /* lock all attached hard disks early to detect "in use"
11087 * situations before deleting actual diffs */
11088 for (MediumAttachmentList::const_iterator
11089 it = mMediumAttachments->begin();
11090 it != mMediumAttachments->end();
11091 ++it)
11092 {
11093 MediumAttachment *pAtt = *it;
11094 if (pAtt->i_getType() == DeviceType_HardDisk)
11095 {
11096 Medium *pMedium = pAtt->i_getMedium();
11097 Assert(pMedium);
11098
11099 MediumLockList *pMediumLockList(new MediumLockList());
11100 alock.release();
11101 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11102 NULL /* pToLockWrite */,
11103 false /* fMediumLockWriteAll */,
11104 NULL,
11105 *pMediumLockList);
11106 alock.acquire();
11107
11108 if (FAILED(rc))
11109 {
11110 delete pMediumLockList;
11111 throw rc;
11112 }
11113
11114 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11115 if (FAILED(rc))
11116 throw rc;
11117 }
11118 }
11119
11120 if (FAILED(rc))
11121 throw rc;
11122 } // end of offline
11123
11124 /* Lock lists are now up to date and include implicitly created media */
11125
11126 /* Go through remembered attachments and delete all implicitly created
11127 * diffs and fix up the attachment information */
11128 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11129 MediumAttachmentList implicitAtts;
11130 for (MediumAttachmentList::const_iterator
11131 it = mMediumAttachments->begin();
11132 it != mMediumAttachments->end();
11133 ++it)
11134 {
11135 ComObjPtr<MediumAttachment> pAtt = *it;
11136 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11137 if (pMedium.isNull())
11138 continue;
11139
11140 // Implicit attachments go on the list for deletion and back references are removed.
11141 if (pAtt->i_isImplicit())
11142 {
11143 /* Deassociate and mark for deletion */
11144 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11145 rc = pMedium->i_removeBackReference(mData->mUuid);
11146 if (FAILED(rc))
11147 throw rc;
11148 implicitAtts.push_back(pAtt);
11149 continue;
11150 }
11151
11152 /* Was this medium attached before? */
11153 if (!i_findAttachment(oldAtts, pMedium))
11154 {
11155 /* no: de-associate */
11156 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11157 rc = pMedium->i_removeBackReference(mData->mUuid);
11158 if (FAILED(rc))
11159 throw rc;
11160 continue;
11161 }
11162 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11163 }
11164
11165 /* If there are implicit attachments to delete, throw away the lock
11166 * map contents (which will unlock all media) since the medium
11167 * attachments will be rolled back. Below we need to completely
11168 * recreate the lock map anyway since it is infinitely complex to
11169 * do this incrementally (would need reconstructing each attachment
11170 * change, which would be extremely hairy). */
11171 if (implicitAtts.size() != 0)
11172 {
11173 ErrorInfoKeeper eik;
11174
11175 HRESULT rc1 = lockedMediaMap->Clear();
11176 AssertComRC(rc1);
11177 }
11178
11179 /* rollback hard disk changes */
11180 mMediumAttachments.rollback();
11181
11182 MultiResult mrc(S_OK);
11183
11184 // Delete unused implicit diffs.
11185 if (implicitAtts.size() != 0)
11186 {
11187 alock.release();
11188
11189 for (MediumAttachmentList::const_iterator
11190 it = implicitAtts.begin();
11191 it != implicitAtts.end();
11192 ++it)
11193 {
11194 // Remove medium associated with this attachment.
11195 ComObjPtr<MediumAttachment> pAtt = *it;
11196 Assert(pAtt);
11197 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11198 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11199 Assert(pMedium);
11200
11201 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11202 // continue on delete failure, just collect error messages
11203 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11204 pMedium->i_getLocationFull().c_str() ));
11205 mrc = rc;
11206 }
11207 // Clear the list of deleted implicit attachments now, while not
11208 // holding the lock, as it will ultimately trigger Medium::uninit()
11209 // calls which assume that the media tree lock isn't held.
11210 implicitAtts.clear();
11211
11212 alock.acquire();
11213
11214 /* if there is a VM recreate media lock map as mentioned above,
11215 * otherwise it is a waste of time and we leave things unlocked */
11216 if (aOnline)
11217 {
11218 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11219 /* must never be NULL, but better safe than sorry */
11220 if (!pMachine.isNull())
11221 {
11222 alock.release();
11223 rc = mData->mSession.mMachine->i_lockMedia();
11224 alock.acquire();
11225 if (FAILED(rc))
11226 throw rc;
11227 }
11228 }
11229 }
11230 }
11231 catch (HRESULT aRC) {rc = aRC;}
11232
11233 if (mData->mMachineState == MachineState_SettingUp)
11234 i_setMachineState(oldState);
11235
11236 /* unlock all hard disks we locked when there is no VM */
11237 if (!aOnline)
11238 {
11239 ErrorInfoKeeper eik;
11240
11241 HRESULT rc1 = lockedMediaMap->Clear();
11242 AssertComRC(rc1);
11243 }
11244
11245 return rc;
11246}
11247
11248
11249/**
11250 * Looks through the given list of media attachments for one with the given parameters
11251 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11252 * can be searched as well if needed.
11253 *
11254 * @param ll
11255 * @param aControllerName
11256 * @param aControllerPort
11257 * @param aDevice
11258 * @return
11259 */
11260MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11261 const Utf8Str &aControllerName,
11262 LONG aControllerPort,
11263 LONG aDevice)
11264{
11265 for (MediumAttachmentList::const_iterator
11266 it = ll.begin();
11267 it != ll.end();
11268 ++it)
11269 {
11270 MediumAttachment *pAttach = *it;
11271 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11272 return pAttach;
11273 }
11274
11275 return NULL;
11276}
11277
11278/**
11279 * Looks through the given list of media attachments for one with the given parameters
11280 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11281 * can be searched as well if needed.
11282 *
11283 * @param ll
11284 * @param pMedium
11285 * @return
11286 */
11287MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11288 ComObjPtr<Medium> pMedium)
11289{
11290 for (MediumAttachmentList::const_iterator
11291 it = ll.begin();
11292 it != ll.end();
11293 ++it)
11294 {
11295 MediumAttachment *pAttach = *it;
11296 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11297 if (pMediumThis == pMedium)
11298 return pAttach;
11299 }
11300
11301 return NULL;
11302}
11303
11304/**
11305 * Looks through the given list of media attachments for one with the given parameters
11306 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11307 * can be searched as well if needed.
11308 *
11309 * @param ll
11310 * @param id
11311 * @return
11312 */
11313MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11314 Guid &id)
11315{
11316 for (MediumAttachmentList::const_iterator
11317 it = ll.begin();
11318 it != ll.end();
11319 ++it)
11320 {
11321 MediumAttachment *pAttach = *it;
11322 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11323 if (pMediumThis->i_getId() == id)
11324 return pAttach;
11325 }
11326
11327 return NULL;
11328}
11329
11330/**
11331 * Main implementation for Machine::DetachDevice. This also gets called
11332 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11333 *
11334 * @param pAttach Medium attachment to detach.
11335 * @param writeLock Machine write lock which the caller must have locked once.
11336 * This may be released temporarily in here.
11337 * @param pSnapshot If NULL, then the detachment is for the current machine.
11338 * Otherwise this is for a SnapshotMachine, and this must be
11339 * its snapshot.
11340 * @return
11341 */
11342HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11343 AutoWriteLock &writeLock,
11344 Snapshot *pSnapshot)
11345{
11346 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11347 DeviceType_T mediumType = pAttach->i_getType();
11348
11349 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11350
11351 if (pAttach->i_isImplicit())
11352 {
11353 /* attempt to implicitly delete the implicitly created diff */
11354
11355 /// @todo move the implicit flag from MediumAttachment to Medium
11356 /// and forbid any hard disk operation when it is implicit. Or maybe
11357 /// a special media state for it to make it even more simple.
11358
11359 Assert(mMediumAttachments.isBackedUp());
11360
11361 /* will release the lock before the potentially lengthy operation, so
11362 * protect with the special state */
11363 MachineState_T oldState = mData->mMachineState;
11364 i_setMachineState(MachineState_SettingUp);
11365
11366 writeLock.release();
11367
11368 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11369 true /*aWait*/,
11370 false /*aNotify*/);
11371
11372 writeLock.acquire();
11373
11374 i_setMachineState(oldState);
11375
11376 if (FAILED(rc)) return rc;
11377 }
11378
11379 i_setModified(IsModified_Storage);
11380 mMediumAttachments.backup();
11381 mMediumAttachments->remove(pAttach);
11382
11383 if (!oldmedium.isNull())
11384 {
11385 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11386 if (pSnapshot)
11387 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11388 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11389 else if (mediumType != DeviceType_HardDisk)
11390 oldmedium->i_removeBackReference(mData->mUuid);
11391 }
11392
11393 return S_OK;
11394}
11395
11396/**
11397 * Goes thru all media of the given list and
11398 *
11399 * 1) calls i_detachDevice() on each of them for this machine and
11400 * 2) adds all Medium objects found in the process to the given list,
11401 * depending on cleanupMode.
11402 *
11403 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11404 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11405 * media to the list.
11406 *
11407 * This gets called from Machine::Unregister, both for the actual Machine and
11408 * the SnapshotMachine objects that might be found in the snapshots.
11409 *
11410 * Requires caller and locking. The machine lock must be passed in because it
11411 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11412 *
11413 * @param writeLock Machine lock from top-level caller; this gets passed to
11414 * i_detachDevice.
11415 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11416 * object if called for a SnapshotMachine.
11417 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11418 * added to llMedia; if Full, then all media get added;
11419 * otherwise no media get added.
11420 * @param llMedia Caller's list to receive Medium objects which got detached so
11421 * caller can close() them, depending on cleanupMode.
11422 * @return
11423 */
11424HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11425 Snapshot *pSnapshot,
11426 CleanupMode_T cleanupMode,
11427 MediaList &llMedia)
11428{
11429 Assert(isWriteLockOnCurrentThread());
11430
11431 HRESULT rc;
11432
11433 // make a temporary list because i_detachDevice invalidates iterators into
11434 // mMediumAttachments
11435 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11436
11437 for (MediumAttachmentList::iterator
11438 it = llAttachments2.begin();
11439 it != llAttachments2.end();
11440 ++it)
11441 {
11442 ComObjPtr<MediumAttachment> &pAttach = *it;
11443 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11444
11445 if (!pMedium.isNull())
11446 {
11447 AutoCaller mac(pMedium);
11448 if (FAILED(mac.rc())) return mac.rc();
11449 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11450 DeviceType_T devType = pMedium->i_getDeviceType();
11451 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11452 && devType == DeviceType_HardDisk)
11453 || (cleanupMode == CleanupMode_Full)
11454 )
11455 {
11456 llMedia.push_back(pMedium);
11457 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11458 /* Not allowed to keep this lock as below we need the parent
11459 * medium lock, and the lock order is parent to child. */
11460 lock.release();
11461 /*
11462 * Search for medias which are not attached to any machine, but
11463 * in the chain to an attached disk. Mediums are only consided
11464 * if they are:
11465 * - have only one child
11466 * - no references to any machines
11467 * - are of normal medium type
11468 */
11469 while (!pParent.isNull())
11470 {
11471 AutoCaller mac1(pParent);
11472 if (FAILED(mac1.rc())) return mac1.rc();
11473 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11474 if (pParent->i_getChildren().size() == 1)
11475 {
11476 if ( pParent->i_getMachineBackRefCount() == 0
11477 && pParent->i_getType() == MediumType_Normal
11478 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11479 llMedia.push_back(pParent);
11480 }
11481 else
11482 break;
11483 pParent = pParent->i_getParent();
11484 }
11485 }
11486 }
11487
11488 // real machine: then we need to use the proper method
11489 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11490
11491 if (FAILED(rc))
11492 return rc;
11493 }
11494
11495 return S_OK;
11496}
11497
11498/**
11499 * Perform deferred hard disk detachments.
11500 *
11501 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11502 * changed (not backed up).
11503 *
11504 * If @a aOnline is @c true then this method will also unlock the old hard
11505 * disks for which the new implicit diffs were created and will lock these new
11506 * diffs for writing.
11507 *
11508 * @param aOnline Whether the VM was online prior to this operation.
11509 *
11510 * @note Locks this object for writing!
11511 */
11512void Machine::i_commitMedia(bool aOnline /*= false*/)
11513{
11514 AutoCaller autoCaller(this);
11515 AssertComRCReturnVoid(autoCaller.rc());
11516
11517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11518
11519 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11520
11521 HRESULT rc = S_OK;
11522
11523 /* no attach/detach operations -- nothing to do */
11524 if (!mMediumAttachments.isBackedUp())
11525 return;
11526
11527 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11528 bool fMediaNeedsLocking = false;
11529
11530 /* enumerate new attachments */
11531 for (MediumAttachmentList::const_iterator
11532 it = mMediumAttachments->begin();
11533 it != mMediumAttachments->end();
11534 ++it)
11535 {
11536 MediumAttachment *pAttach = *it;
11537
11538 pAttach->i_commit();
11539
11540 Medium *pMedium = pAttach->i_getMedium();
11541 bool fImplicit = pAttach->i_isImplicit();
11542
11543 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11544 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11545 fImplicit));
11546
11547 /** @todo convert all this Machine-based voodoo to MediumAttachment
11548 * based commit logic. */
11549 if (fImplicit)
11550 {
11551 /* convert implicit attachment to normal */
11552 pAttach->i_setImplicit(false);
11553
11554 if ( aOnline
11555 && pMedium
11556 && pAttach->i_getType() == DeviceType_HardDisk
11557 )
11558 {
11559 /* update the appropriate lock list */
11560 MediumLockList *pMediumLockList;
11561 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11562 AssertComRC(rc);
11563 if (pMediumLockList)
11564 {
11565 /* unlock if there's a need to change the locking */
11566 if (!fMediaNeedsLocking)
11567 {
11568 rc = mData->mSession.mLockedMedia.Unlock();
11569 AssertComRC(rc);
11570 fMediaNeedsLocking = true;
11571 }
11572 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11573 AssertComRC(rc);
11574 rc = pMediumLockList->Append(pMedium, true);
11575 AssertComRC(rc);
11576 }
11577 }
11578
11579 continue;
11580 }
11581
11582 if (pMedium)
11583 {
11584 /* was this medium attached before? */
11585 for (MediumAttachmentList::iterator
11586 oldIt = oldAtts.begin();
11587 oldIt != oldAtts.end();
11588 ++oldIt)
11589 {
11590 MediumAttachment *pOldAttach = *oldIt;
11591 if (pOldAttach->i_getMedium() == pMedium)
11592 {
11593 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11594
11595 /* yes: remove from old to avoid de-association */
11596 oldAtts.erase(oldIt);
11597 break;
11598 }
11599 }
11600 }
11601 }
11602
11603 /* enumerate remaining old attachments and de-associate from the
11604 * current machine state */
11605 for (MediumAttachmentList::const_iterator
11606 it = oldAtts.begin();
11607 it != oldAtts.end();
11608 ++it)
11609 {
11610 MediumAttachment *pAttach = *it;
11611 Medium *pMedium = pAttach->i_getMedium();
11612
11613 /* Detach only hard disks, since DVD/floppy media is detached
11614 * instantly in MountMedium. */
11615 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11616 {
11617 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11618
11619 /* now de-associate from the current machine state */
11620 rc = pMedium->i_removeBackReference(mData->mUuid);
11621 AssertComRC(rc);
11622
11623 if (aOnline)
11624 {
11625 /* unlock since medium is not used anymore */
11626 MediumLockList *pMediumLockList;
11627 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11628 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11629 {
11630 /* this happens for online snapshots, there the attachment
11631 * is changing, but only to a diff image created under
11632 * the old one, so there is no separate lock list */
11633 Assert(!pMediumLockList);
11634 }
11635 else
11636 {
11637 AssertComRC(rc);
11638 if (pMediumLockList)
11639 {
11640 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11641 AssertComRC(rc);
11642 }
11643 }
11644 }
11645 }
11646 }
11647
11648 /* take media locks again so that the locking state is consistent */
11649 if (fMediaNeedsLocking)
11650 {
11651 Assert(aOnline);
11652 rc = mData->mSession.mLockedMedia.Lock();
11653 AssertComRC(rc);
11654 }
11655
11656 /* commit the hard disk changes */
11657 mMediumAttachments.commit();
11658
11659 if (i_isSessionMachine())
11660 {
11661 /*
11662 * Update the parent machine to point to the new owner.
11663 * This is necessary because the stored parent will point to the
11664 * session machine otherwise and cause crashes or errors later
11665 * when the session machine gets invalid.
11666 */
11667 /** @todo Change the MediumAttachment class to behave like any other
11668 * class in this regard by creating peer MediumAttachment
11669 * objects for session machines and share the data with the peer
11670 * machine.
11671 */
11672 for (MediumAttachmentList::const_iterator
11673 it = mMediumAttachments->begin();
11674 it != mMediumAttachments->end();
11675 ++it)
11676 (*it)->i_updateParentMachine(mPeer);
11677
11678 /* attach new data to the primary machine and reshare it */
11679 mPeer->mMediumAttachments.attach(mMediumAttachments);
11680 }
11681
11682 return;
11683}
11684
11685/**
11686 * Perform deferred deletion of implicitly created diffs.
11687 *
11688 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11689 * changed (not backed up).
11690 *
11691 * @note Locks this object for writing!
11692 */
11693void Machine::i_rollbackMedia()
11694{
11695 AutoCaller autoCaller(this);
11696 AssertComRCReturnVoid(autoCaller.rc());
11697
11698 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11699 LogFlowThisFunc(("Entering rollbackMedia\n"));
11700
11701 HRESULT rc = S_OK;
11702
11703 /* no attach/detach operations -- nothing to do */
11704 if (!mMediumAttachments.isBackedUp())
11705 return;
11706
11707 /* enumerate new attachments */
11708 for (MediumAttachmentList::const_iterator
11709 it = mMediumAttachments->begin();
11710 it != mMediumAttachments->end();
11711 ++it)
11712 {
11713 MediumAttachment *pAttach = *it;
11714 /* Fix up the backrefs for DVD/floppy media. */
11715 if (pAttach->i_getType() != DeviceType_HardDisk)
11716 {
11717 Medium *pMedium = pAttach->i_getMedium();
11718 if (pMedium)
11719 {
11720 rc = pMedium->i_removeBackReference(mData->mUuid);
11721 AssertComRC(rc);
11722 }
11723 }
11724
11725 (*it)->i_rollback();
11726
11727 pAttach = *it;
11728 /* Fix up the backrefs for DVD/floppy media. */
11729 if (pAttach->i_getType() != DeviceType_HardDisk)
11730 {
11731 Medium *pMedium = pAttach->i_getMedium();
11732 if (pMedium)
11733 {
11734 rc = pMedium->i_addBackReference(mData->mUuid);
11735 AssertComRC(rc);
11736 }
11737 }
11738 }
11739
11740 /** @todo convert all this Machine-based voodoo to MediumAttachment
11741 * based rollback logic. */
11742 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11743
11744 return;
11745}
11746
11747/**
11748 * Returns true if the settings file is located in the directory named exactly
11749 * as the machine; this means, among other things, that the machine directory
11750 * should be auto-renamed.
11751 *
11752 * @param aSettingsDir if not NULL, the full machine settings file directory
11753 * name will be assigned there.
11754 *
11755 * @note Doesn't lock anything.
11756 * @note Not thread safe (must be called from this object's lock).
11757 */
11758bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11759{
11760 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11761 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11762 if (aSettingsDir)
11763 *aSettingsDir = strMachineDirName;
11764 strMachineDirName.stripPath(); // vmname
11765 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11766 strConfigFileOnly.stripPath() // vmname.vbox
11767 .stripSuffix(); // vmname
11768 /** @todo hack, make somehow use of ComposeMachineFilename */
11769 if (mUserData->s.fDirectoryIncludesUUID)
11770 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11771
11772 AssertReturn(!strMachineDirName.isEmpty(), false);
11773 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11774
11775 return strMachineDirName == strConfigFileOnly;
11776}
11777
11778/**
11779 * Discards all changes to machine settings.
11780 *
11781 * @param aNotify Whether to notify the direct session about changes or not.
11782 *
11783 * @note Locks objects for writing!
11784 */
11785void Machine::i_rollback(bool aNotify)
11786{
11787 AutoCaller autoCaller(this);
11788 AssertComRCReturn(autoCaller.rc(), (void)0);
11789
11790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11791
11792 if (!mStorageControllers.isNull())
11793 {
11794 if (mStorageControllers.isBackedUp())
11795 {
11796 /* unitialize all new devices (absent in the backed up list). */
11797 StorageControllerList *backedList = mStorageControllers.backedUpData();
11798 for (StorageControllerList::const_iterator
11799 it = mStorageControllers->begin();
11800 it != mStorageControllers->end();
11801 ++it)
11802 {
11803 if ( std::find(backedList->begin(), backedList->end(), *it)
11804 == backedList->end()
11805 )
11806 {
11807 (*it)->uninit();
11808 }
11809 }
11810
11811 /* restore the list */
11812 mStorageControllers.rollback();
11813 }
11814
11815 /* rollback any changes to devices after restoring the list */
11816 if (mData->flModifications & IsModified_Storage)
11817 {
11818 for (StorageControllerList::const_iterator
11819 it = mStorageControllers->begin();
11820 it != mStorageControllers->end();
11821 ++it)
11822 {
11823 (*it)->i_rollback();
11824 }
11825 }
11826 }
11827
11828 if (!mUSBControllers.isNull())
11829 {
11830 if (mUSBControllers.isBackedUp())
11831 {
11832 /* unitialize all new devices (absent in the backed up list). */
11833 USBControllerList *backedList = mUSBControllers.backedUpData();
11834 for (USBControllerList::const_iterator
11835 it = mUSBControllers->begin();
11836 it != mUSBControllers->end();
11837 ++it)
11838 {
11839 if ( std::find(backedList->begin(), backedList->end(), *it)
11840 == backedList->end()
11841 )
11842 {
11843 (*it)->uninit();
11844 }
11845 }
11846
11847 /* restore the list */
11848 mUSBControllers.rollback();
11849 }
11850
11851 /* rollback any changes to devices after restoring the list */
11852 if (mData->flModifications & IsModified_USB)
11853 {
11854 for (USBControllerList::const_iterator
11855 it = mUSBControllers->begin();
11856 it != mUSBControllers->end();
11857 ++it)
11858 {
11859 (*it)->i_rollback();
11860 }
11861 }
11862 }
11863
11864 mUserData.rollback();
11865
11866 mHWData.rollback();
11867
11868 if (mData->flModifications & IsModified_Storage)
11869 i_rollbackMedia();
11870
11871 if (mBIOSSettings)
11872 mBIOSSettings->i_rollback();
11873
11874 if (mTrustedPlatformModule)
11875 mTrustedPlatformModule->i_rollback();
11876
11877 if (mNvramStore)
11878 mNvramStore->i_rollback();
11879
11880 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11881 mRecordingSettings->i_rollback();
11882
11883 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11884 mGraphicsAdapter->i_rollback();
11885
11886 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11887 mVRDEServer->i_rollback();
11888
11889 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11890 mAudioAdapter->i_rollback();
11891
11892 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11893 mUSBDeviceFilters->i_rollback();
11894
11895 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11896 mBandwidthControl->i_rollback();
11897
11898 if (!mHWData.isNull())
11899 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11900 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11901 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11902 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11903
11904 if (mData->flModifications & IsModified_NetworkAdapters)
11905 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11906 if ( mNetworkAdapters[slot]
11907 && mNetworkAdapters[slot]->i_isModified())
11908 {
11909 mNetworkAdapters[slot]->i_rollback();
11910 networkAdapters[slot] = mNetworkAdapters[slot];
11911 }
11912
11913 if (mData->flModifications & IsModified_SerialPorts)
11914 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11915 if ( mSerialPorts[slot]
11916 && mSerialPorts[slot]->i_isModified())
11917 {
11918 mSerialPorts[slot]->i_rollback();
11919 serialPorts[slot] = mSerialPorts[slot];
11920 }
11921
11922 if (mData->flModifications & IsModified_ParallelPorts)
11923 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11924 if ( mParallelPorts[slot]
11925 && mParallelPorts[slot]->i_isModified())
11926 {
11927 mParallelPorts[slot]->i_rollback();
11928 parallelPorts[slot] = mParallelPorts[slot];
11929 }
11930
11931 if (aNotify)
11932 {
11933 /* inform the direct session about changes */
11934
11935 ComObjPtr<Machine> that = this;
11936 uint32_t flModifications = mData->flModifications;
11937 alock.release();
11938
11939 if (flModifications & IsModified_SharedFolders)
11940 that->i_onSharedFolderChange();
11941
11942 if (flModifications & IsModified_VRDEServer)
11943 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11944 if (flModifications & IsModified_USB)
11945 that->i_onUSBControllerChange();
11946
11947 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11948 if (networkAdapters[slot])
11949 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11950 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11951 if (serialPorts[slot])
11952 that->i_onSerialPortChange(serialPorts[slot]);
11953 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11954 if (parallelPorts[slot])
11955 that->i_onParallelPortChange(parallelPorts[slot]);
11956
11957 if (flModifications & IsModified_Storage)
11958 {
11959 for (StorageControllerList::const_iterator
11960 it = mStorageControllers->begin();
11961 it != mStorageControllers->end();
11962 ++it)
11963 {
11964 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11965 }
11966 }
11967
11968
11969#if 0
11970 if (flModifications & IsModified_BandwidthControl)
11971 that->onBandwidthControlChange();
11972#endif
11973 }
11974}
11975
11976/**
11977 * Commits all the changes to machine settings.
11978 *
11979 * Note that this operation is supposed to never fail.
11980 *
11981 * @note Locks this object and children for writing.
11982 */
11983void Machine::i_commit()
11984{
11985 AutoCaller autoCaller(this);
11986 AssertComRCReturnVoid(autoCaller.rc());
11987
11988 AutoCaller peerCaller(mPeer);
11989 AssertComRCReturnVoid(peerCaller.rc());
11990
11991 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11992
11993 /*
11994 * use safe commit to ensure Snapshot machines (that share mUserData)
11995 * will still refer to a valid memory location
11996 */
11997 mUserData.commitCopy();
11998
11999 mHWData.commit();
12000
12001 if (mMediumAttachments.isBackedUp())
12002 i_commitMedia(Global::IsOnline(mData->mMachineState));
12003
12004 mBIOSSettings->i_commit();
12005 mTrustedPlatformModule->i_commit();
12006 mNvramStore->i_commit();
12007 mRecordingSettings->i_commit();
12008 mGraphicsAdapter->i_commit();
12009 mVRDEServer->i_commit();
12010 mAudioAdapter->i_commit();
12011 mUSBDeviceFilters->i_commit();
12012 mBandwidthControl->i_commit();
12013
12014 /* Since mNetworkAdapters is a list which might have been changed (resized)
12015 * without using the Backupable<> template we need to handle the copying
12016 * of the list entries manually, including the creation of peers for the
12017 * new objects. */
12018 bool commitNetworkAdapters = false;
12019 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12020 if (mPeer)
12021 {
12022 /* commit everything, even the ones which will go away */
12023 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12024 mNetworkAdapters[slot]->i_commit();
12025 /* copy over the new entries, creating a peer and uninit the original */
12026 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12027 for (size_t slot = 0; slot < newSize; slot++)
12028 {
12029 /* look if this adapter has a peer device */
12030 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12031 if (!peer)
12032 {
12033 /* no peer means the adapter is a newly created one;
12034 * create a peer owning data this data share it with */
12035 peer.createObject();
12036 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12037 }
12038 mPeer->mNetworkAdapters[slot] = peer;
12039 }
12040 /* uninit any no longer needed network adapters */
12041 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12042 mNetworkAdapters[slot]->uninit();
12043 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12044 {
12045 if (mPeer->mNetworkAdapters[slot])
12046 mPeer->mNetworkAdapters[slot]->uninit();
12047 }
12048 /* Keep the original network adapter count until this point, so that
12049 * discarding a chipset type change will not lose settings. */
12050 mNetworkAdapters.resize(newSize);
12051 mPeer->mNetworkAdapters.resize(newSize);
12052 }
12053 else
12054 {
12055 /* we have no peer (our parent is the newly created machine);
12056 * just commit changes to the network adapters */
12057 commitNetworkAdapters = true;
12058 }
12059 if (commitNetworkAdapters)
12060 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12061 mNetworkAdapters[slot]->i_commit();
12062
12063 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12064 mSerialPorts[slot]->i_commit();
12065 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12066 mParallelPorts[slot]->i_commit();
12067
12068 bool commitStorageControllers = false;
12069
12070 if (mStorageControllers.isBackedUp())
12071 {
12072 mStorageControllers.commit();
12073
12074 if (mPeer)
12075 {
12076 /* Commit all changes to new controllers (this will reshare data with
12077 * peers for those who have peers) */
12078 StorageControllerList *newList = new StorageControllerList();
12079 for (StorageControllerList::const_iterator
12080 it = mStorageControllers->begin();
12081 it != mStorageControllers->end();
12082 ++it)
12083 {
12084 (*it)->i_commit();
12085
12086 /* look if this controller has a peer device */
12087 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12088 if (!peer)
12089 {
12090 /* no peer means the device is a newly created one;
12091 * create a peer owning data this device share it with */
12092 peer.createObject();
12093 peer->init(mPeer, *it, true /* aReshare */);
12094 }
12095 else
12096 {
12097 /* remove peer from the old list */
12098 mPeer->mStorageControllers->remove(peer);
12099 }
12100 /* and add it to the new list */
12101 newList->push_back(peer);
12102 }
12103
12104 /* uninit old peer's controllers that are left */
12105 for (StorageControllerList::const_iterator
12106 it = mPeer->mStorageControllers->begin();
12107 it != mPeer->mStorageControllers->end();
12108 ++it)
12109 {
12110 (*it)->uninit();
12111 }
12112
12113 /* attach new list of controllers to our peer */
12114 mPeer->mStorageControllers.attach(newList);
12115 }
12116 else
12117 {
12118 /* we have no peer (our parent is the newly created machine);
12119 * just commit changes to devices */
12120 commitStorageControllers = true;
12121 }
12122 }
12123 else
12124 {
12125 /* the list of controllers itself is not changed,
12126 * just commit changes to controllers themselves */
12127 commitStorageControllers = true;
12128 }
12129
12130 if (commitStorageControllers)
12131 {
12132 for (StorageControllerList::const_iterator
12133 it = mStorageControllers->begin();
12134 it != mStorageControllers->end();
12135 ++it)
12136 {
12137 (*it)->i_commit();
12138 }
12139 }
12140
12141 bool commitUSBControllers = false;
12142
12143 if (mUSBControllers.isBackedUp())
12144 {
12145 mUSBControllers.commit();
12146
12147 if (mPeer)
12148 {
12149 /* Commit all changes to new controllers (this will reshare data with
12150 * peers for those who have peers) */
12151 USBControllerList *newList = new USBControllerList();
12152 for (USBControllerList::const_iterator
12153 it = mUSBControllers->begin();
12154 it != mUSBControllers->end();
12155 ++it)
12156 {
12157 (*it)->i_commit();
12158
12159 /* look if this controller has a peer device */
12160 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12161 if (!peer)
12162 {
12163 /* no peer means the device is a newly created one;
12164 * create a peer owning data this device share it with */
12165 peer.createObject();
12166 peer->init(mPeer, *it, true /* aReshare */);
12167 }
12168 else
12169 {
12170 /* remove peer from the old list */
12171 mPeer->mUSBControllers->remove(peer);
12172 }
12173 /* and add it to the new list */
12174 newList->push_back(peer);
12175 }
12176
12177 /* uninit old peer's controllers that are left */
12178 for (USBControllerList::const_iterator
12179 it = mPeer->mUSBControllers->begin();
12180 it != mPeer->mUSBControllers->end();
12181 ++it)
12182 {
12183 (*it)->uninit();
12184 }
12185
12186 /* attach new list of controllers to our peer */
12187 mPeer->mUSBControllers.attach(newList);
12188 }
12189 else
12190 {
12191 /* we have no peer (our parent is the newly created machine);
12192 * just commit changes to devices */
12193 commitUSBControllers = true;
12194 }
12195 }
12196 else
12197 {
12198 /* the list of controllers itself is not changed,
12199 * just commit changes to controllers themselves */
12200 commitUSBControllers = true;
12201 }
12202
12203 if (commitUSBControllers)
12204 {
12205 for (USBControllerList::const_iterator
12206 it = mUSBControllers->begin();
12207 it != mUSBControllers->end();
12208 ++it)
12209 {
12210 (*it)->i_commit();
12211 }
12212 }
12213
12214 if (i_isSessionMachine())
12215 {
12216 /* attach new data to the primary machine and reshare it */
12217 mPeer->mUserData.attach(mUserData);
12218 mPeer->mHWData.attach(mHWData);
12219 /* mmMediumAttachments is reshared by fixupMedia */
12220 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12221 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12222 }
12223}
12224
12225/**
12226 * Copies all the hardware data from the given machine.
12227 *
12228 * Currently, only called when the VM is being restored from a snapshot. In
12229 * particular, this implies that the VM is not running during this method's
12230 * call.
12231 *
12232 * @note This method must be called from under this object's lock.
12233 *
12234 * @note This method doesn't call #i_commit(), so all data remains backed up and
12235 * unsaved.
12236 */
12237void Machine::i_copyFrom(Machine *aThat)
12238{
12239 AssertReturnVoid(!i_isSnapshotMachine());
12240 AssertReturnVoid(aThat->i_isSnapshotMachine());
12241
12242 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12243
12244 mHWData.assignCopy(aThat->mHWData);
12245
12246 // create copies of all shared folders (mHWData after attaching a copy
12247 // contains just references to original objects)
12248 for (HWData::SharedFolderList::iterator
12249 it = mHWData->mSharedFolders.begin();
12250 it != mHWData->mSharedFolders.end();
12251 ++it)
12252 {
12253 ComObjPtr<SharedFolder> folder;
12254 folder.createObject();
12255 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12256 AssertComRC(rc);
12257 *it = folder;
12258 }
12259
12260 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12261 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12262 mNvramStore->i_copyFrom(aThat->mNvramStore);
12263 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12264 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12265 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12266 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12267 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12268 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12269
12270 /* create private copies of all controllers */
12271 mStorageControllers.backup();
12272 mStorageControllers->clear();
12273 for (StorageControllerList::const_iterator
12274 it = aThat->mStorageControllers->begin();
12275 it != aThat->mStorageControllers->end();
12276 ++it)
12277 {
12278 ComObjPtr<StorageController> ctrl;
12279 ctrl.createObject();
12280 ctrl->initCopy(this, *it);
12281 mStorageControllers->push_back(ctrl);
12282 }
12283
12284 /* create private copies of all USB controllers */
12285 mUSBControllers.backup();
12286 mUSBControllers->clear();
12287 for (USBControllerList::const_iterator
12288 it = aThat->mUSBControllers->begin();
12289 it != aThat->mUSBControllers->end();
12290 ++it)
12291 {
12292 ComObjPtr<USBController> ctrl;
12293 ctrl.createObject();
12294 ctrl->initCopy(this, *it);
12295 mUSBControllers->push_back(ctrl);
12296 }
12297
12298 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12299 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12300 {
12301 if (mNetworkAdapters[slot].isNotNull())
12302 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12303 else
12304 {
12305 unconst(mNetworkAdapters[slot]).createObject();
12306 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12307 }
12308 }
12309 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12310 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12311 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12312 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12313}
12314
12315/**
12316 * Returns whether the given storage controller is hotplug capable.
12317 *
12318 * @returns true if the controller supports hotplugging
12319 * false otherwise.
12320 * @param enmCtrlType The controller type to check for.
12321 */
12322bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12323{
12324 ComPtr<ISystemProperties> systemProperties;
12325 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12326 if (FAILED(rc))
12327 return false;
12328
12329 BOOL aHotplugCapable = FALSE;
12330 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12331
12332 return RT_BOOL(aHotplugCapable);
12333}
12334
12335#ifdef VBOX_WITH_RESOURCE_USAGE_API
12336
12337void Machine::i_getDiskList(MediaList &list)
12338{
12339 for (MediumAttachmentList::const_iterator
12340 it = mMediumAttachments->begin();
12341 it != mMediumAttachments->end();
12342 ++it)
12343 {
12344 MediumAttachment *pAttach = *it;
12345 /* just in case */
12346 AssertContinue(pAttach);
12347
12348 AutoCaller localAutoCallerA(pAttach);
12349 if (FAILED(localAutoCallerA.rc())) continue;
12350
12351 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12352
12353 if (pAttach->i_getType() == DeviceType_HardDisk)
12354 list.push_back(pAttach->i_getMedium());
12355 }
12356}
12357
12358void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12359{
12360 AssertReturnVoid(isWriteLockOnCurrentThread());
12361 AssertPtrReturnVoid(aCollector);
12362
12363 pm::CollectorHAL *hal = aCollector->getHAL();
12364 /* Create sub metrics */
12365 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12366 "Percentage of processor time spent in user mode by the VM process.");
12367 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12368 "Percentage of processor time spent in kernel mode by the VM process.");
12369 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12370 "Size of resident portion of VM process in memory.");
12371 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12372 "Actual size of all VM disks combined.");
12373 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12374 "Network receive rate.");
12375 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12376 "Network transmit rate.");
12377 /* Create and register base metrics */
12378 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12379 cpuLoadUser, cpuLoadKernel);
12380 aCollector->registerBaseMetric(cpuLoad);
12381 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12382 ramUsageUsed);
12383 aCollector->registerBaseMetric(ramUsage);
12384 MediaList disks;
12385 i_getDiskList(disks);
12386 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12387 diskUsageUsed);
12388 aCollector->registerBaseMetric(diskUsage);
12389
12390 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12391 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12392 new pm::AggregateAvg()));
12393 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12394 new pm::AggregateMin()));
12395 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12396 new pm::AggregateMax()));
12397 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12398 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12399 new pm::AggregateAvg()));
12400 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12401 new pm::AggregateMin()));
12402 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12403 new pm::AggregateMax()));
12404
12405 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12406 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12407 new pm::AggregateAvg()));
12408 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12409 new pm::AggregateMin()));
12410 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12411 new pm::AggregateMax()));
12412
12413 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12414 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12415 new pm::AggregateAvg()));
12416 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12417 new pm::AggregateMin()));
12418 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12419 new pm::AggregateMax()));
12420
12421
12422 /* Guest metrics collector */
12423 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12424 aCollector->registerGuest(mCollectorGuest);
12425 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12426
12427 /* Create sub metrics */
12428 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12429 "Percentage of processor time spent in user mode as seen by the guest.");
12430 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12431 "Percentage of processor time spent in kernel mode as seen by the guest.");
12432 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12433 "Percentage of processor time spent idling as seen by the guest.");
12434
12435 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12436 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12437 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12438 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12439 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12440 pm::SubMetric *guestMemCache = new pm::SubMetric(
12441 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12442
12443 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12444 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12445
12446 /* Create and register base metrics */
12447 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12448 machineNetRx, machineNetTx);
12449 aCollector->registerBaseMetric(machineNetRate);
12450
12451 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12452 guestLoadUser, guestLoadKernel, guestLoadIdle);
12453 aCollector->registerBaseMetric(guestCpuLoad);
12454
12455 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12456 guestMemTotal, guestMemFree,
12457 guestMemBalloon, guestMemShared,
12458 guestMemCache, guestPagedTotal);
12459 aCollector->registerBaseMetric(guestCpuMem);
12460
12461 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12462 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12463 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12464 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12465
12466 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12467 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12468 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12469 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12470
12471 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12472 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12473 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12474 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12475
12476 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12477 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12478 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12479 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12480
12481 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12482 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12483 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12484 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12485
12486 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12487 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12488 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12489 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12490
12491 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12492 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12493 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12494 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12495
12496 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12497 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12498 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12499 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12500
12501 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12502 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12503 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12504 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12505
12506 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12507 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12508 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12509 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12510
12511 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12512 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12513 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12514 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12515}
12516
12517void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12518{
12519 AssertReturnVoid(isWriteLockOnCurrentThread());
12520
12521 if (aCollector)
12522 {
12523 aCollector->unregisterMetricsFor(aMachine);
12524 aCollector->unregisterBaseMetricsFor(aMachine);
12525 }
12526}
12527
12528#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12529
12530
12531////////////////////////////////////////////////////////////////////////////////
12532
12533DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12534
12535HRESULT SessionMachine::FinalConstruct()
12536{
12537 LogFlowThisFunc(("\n"));
12538
12539 mClientToken = NULL;
12540
12541 return BaseFinalConstruct();
12542}
12543
12544void SessionMachine::FinalRelease()
12545{
12546 LogFlowThisFunc(("\n"));
12547
12548 Assert(!mClientToken);
12549 /* paranoia, should not hang around any more */
12550 if (mClientToken)
12551 {
12552 delete mClientToken;
12553 mClientToken = NULL;
12554 }
12555
12556 uninit(Uninit::Unexpected);
12557
12558 BaseFinalRelease();
12559}
12560
12561/**
12562 * @note Must be called only by Machine::LockMachine() from its own write lock.
12563 */
12564HRESULT SessionMachine::init(Machine *aMachine)
12565{
12566 LogFlowThisFuncEnter();
12567 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12568
12569 AssertReturn(aMachine, E_INVALIDARG);
12570
12571 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12572
12573 /* Enclose the state transition NotReady->InInit->Ready */
12574 AutoInitSpan autoInitSpan(this);
12575 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12576
12577 HRESULT rc = S_OK;
12578
12579 RT_ZERO(mAuthLibCtx);
12580
12581 /* create the machine client token */
12582 try
12583 {
12584 mClientToken = new ClientToken(aMachine, this);
12585 if (!mClientToken->isReady())
12586 {
12587 delete mClientToken;
12588 mClientToken = NULL;
12589 rc = E_FAIL;
12590 }
12591 }
12592 catch (std::bad_alloc &)
12593 {
12594 rc = E_OUTOFMEMORY;
12595 }
12596 if (FAILED(rc))
12597 return rc;
12598
12599 /* memorize the peer Machine */
12600 unconst(mPeer) = aMachine;
12601 /* share the parent pointer */
12602 unconst(mParent) = aMachine->mParent;
12603
12604 /* take the pointers to data to share */
12605 mData.share(aMachine->mData);
12606 mSSData.share(aMachine->mSSData);
12607
12608 mUserData.share(aMachine->mUserData);
12609 mHWData.share(aMachine->mHWData);
12610 mMediumAttachments.share(aMachine->mMediumAttachments);
12611
12612 mStorageControllers.allocate();
12613 for (StorageControllerList::const_iterator
12614 it = aMachine->mStorageControllers->begin();
12615 it != aMachine->mStorageControllers->end();
12616 ++it)
12617 {
12618 ComObjPtr<StorageController> ctl;
12619 ctl.createObject();
12620 ctl->init(this, *it);
12621 mStorageControllers->push_back(ctl);
12622 }
12623
12624 mUSBControllers.allocate();
12625 for (USBControllerList::const_iterator
12626 it = aMachine->mUSBControllers->begin();
12627 it != aMachine->mUSBControllers->end();
12628 ++it)
12629 {
12630 ComObjPtr<USBController> ctl;
12631 ctl.createObject();
12632 ctl->init(this, *it);
12633 mUSBControllers->push_back(ctl);
12634 }
12635
12636 unconst(mBIOSSettings).createObject();
12637 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12638
12639 unconst(mTrustedPlatformModule).createObject();
12640 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12641
12642 unconst(mNvramStore).createObject();
12643 mNvramStore->init(this, aMachine->mNvramStore);
12644
12645 unconst(mRecordingSettings).createObject();
12646 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12647 /* create another GraphicsAdapter object that will be mutable */
12648 unconst(mGraphicsAdapter).createObject();
12649 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12650 /* create another VRDEServer object that will be mutable */
12651 unconst(mVRDEServer).createObject();
12652 mVRDEServer->init(this, aMachine->mVRDEServer);
12653 /* create another audio adapter object that will be mutable */
12654 unconst(mAudioAdapter).createObject();
12655 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12656 /* create a list of serial ports that will be mutable */
12657 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12658 {
12659 unconst(mSerialPorts[slot]).createObject();
12660 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12661 }
12662 /* create a list of parallel ports that will be mutable */
12663 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12664 {
12665 unconst(mParallelPorts[slot]).createObject();
12666 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12667 }
12668
12669 /* create another USB device filters object that will be mutable */
12670 unconst(mUSBDeviceFilters).createObject();
12671 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12672
12673 /* create a list of network adapters that will be mutable */
12674 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12675 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12676 {
12677 unconst(mNetworkAdapters[slot]).createObject();
12678 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12679 }
12680
12681 /* create another bandwidth control object that will be mutable */
12682 unconst(mBandwidthControl).createObject();
12683 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12684
12685 /* default is to delete saved state on Saved -> PoweredOff transition */
12686 mRemoveSavedState = true;
12687
12688 /* Confirm a successful initialization when it's the case */
12689 autoInitSpan.setSucceeded();
12690
12691 miNATNetworksStarted = 0;
12692
12693 LogFlowThisFuncLeave();
12694 return rc;
12695}
12696
12697/**
12698 * Uninitializes this session object. If the reason is other than
12699 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12700 * or the client watcher code.
12701 *
12702 * @param aReason uninitialization reason
12703 *
12704 * @note Locks mParent + this object for writing.
12705 */
12706void SessionMachine::uninit(Uninit::Reason aReason)
12707{
12708 LogFlowThisFuncEnter();
12709 LogFlowThisFunc(("reason=%d\n", aReason));
12710
12711 /*
12712 * Strongly reference ourselves to prevent this object deletion after
12713 * mData->mSession.mMachine.setNull() below (which can release the last
12714 * reference and call the destructor). Important: this must be done before
12715 * accessing any members (and before AutoUninitSpan that does it as well).
12716 * This self reference will be released as the very last step on return.
12717 */
12718 ComObjPtr<SessionMachine> selfRef;
12719 if (aReason != Uninit::Unexpected)
12720 selfRef = this;
12721
12722 /* Enclose the state transition Ready->InUninit->NotReady */
12723 AutoUninitSpan autoUninitSpan(this);
12724 if (autoUninitSpan.uninitDone())
12725 {
12726 LogFlowThisFunc(("Already uninitialized\n"));
12727 LogFlowThisFuncLeave();
12728 return;
12729 }
12730
12731 if (autoUninitSpan.initFailed())
12732 {
12733 /* We've been called by init() because it's failed. It's not really
12734 * necessary (nor it's safe) to perform the regular uninit sequence
12735 * below, the following is enough.
12736 */
12737 LogFlowThisFunc(("Initialization failed.\n"));
12738 /* destroy the machine client token */
12739 if (mClientToken)
12740 {
12741 delete mClientToken;
12742 mClientToken = NULL;
12743 }
12744 uninitDataAndChildObjects();
12745 mData.free();
12746 unconst(mParent) = NULL;
12747 unconst(mPeer) = NULL;
12748 LogFlowThisFuncLeave();
12749 return;
12750 }
12751
12752 MachineState_T lastState;
12753 {
12754 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12755 lastState = mData->mMachineState;
12756 }
12757 NOREF(lastState);
12758
12759#ifdef VBOX_WITH_USB
12760 // release all captured USB devices, but do this before requesting the locks below
12761 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12762 {
12763 /* Console::captureUSBDevices() is called in the VM process only after
12764 * setting the machine state to Starting or Restoring.
12765 * Console::detachAllUSBDevices() will be called upon successful
12766 * termination. So, we need to release USB devices only if there was
12767 * an abnormal termination of a running VM.
12768 *
12769 * This is identical to SessionMachine::DetachAllUSBDevices except
12770 * for the aAbnormal argument. */
12771 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12772 AssertComRC(rc);
12773 NOREF(rc);
12774
12775 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12776 if (service)
12777 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12778 }
12779#endif /* VBOX_WITH_USB */
12780
12781 // we need to lock this object in uninit() because the lock is shared
12782 // with mPeer (as well as data we modify below). mParent lock is needed
12783 // by several calls to it.
12784 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12785
12786#ifdef VBOX_WITH_RESOURCE_USAGE_API
12787 /*
12788 * It is safe to call Machine::i_unregisterMetrics() here because
12789 * PerformanceCollector::samplerCallback no longer accesses guest methods
12790 * holding the lock.
12791 */
12792 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12793 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12794 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12795 if (mCollectorGuest)
12796 {
12797 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12798 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12799 mCollectorGuest = NULL;
12800 }
12801#endif
12802
12803 if (aReason == Uninit::Abnormal)
12804 {
12805 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12806
12807 /*
12808 * Move the VM to the 'Aborted' machine state unless we are restoring a
12809 * VM that was in the 'Saved' machine state. In that case, if the VM
12810 * fails before reaching either the 'Restoring' machine state or the
12811 * 'Running' machine state then we set the machine state to
12812 * 'AbortedSaved' in order to preserve the saved state file so that the
12813 * VM can be restored in the future.
12814 */
12815 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12816 i_setMachineState(MachineState_AbortedSaved);
12817 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12818 i_setMachineState(MachineState_Aborted);
12819 }
12820
12821 // any machine settings modified?
12822 if (mData->flModifications)
12823 {
12824 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12825 i_rollback(false /* aNotify */);
12826 }
12827
12828 mData->mSession.mPID = NIL_RTPROCESS;
12829
12830 if (aReason == Uninit::Unexpected)
12831 {
12832 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12833 * client watcher thread to update the set of machines that have open
12834 * sessions. */
12835 mParent->i_updateClientWatcher();
12836 }
12837
12838 /* uninitialize all remote controls */
12839 if (mData->mSession.mRemoteControls.size())
12840 {
12841 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12842 mData->mSession.mRemoteControls.size()));
12843
12844 /* Always restart a the beginning, since the iterator is invalidated
12845 * by using erase(). */
12846 for (Data::Session::RemoteControlList::iterator
12847 it = mData->mSession.mRemoteControls.begin();
12848 it != mData->mSession.mRemoteControls.end();
12849 it = mData->mSession.mRemoteControls.begin())
12850 {
12851 ComPtr<IInternalSessionControl> pControl = *it;
12852 mData->mSession.mRemoteControls.erase(it);
12853 multilock.release();
12854 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12855 HRESULT rc = pControl->Uninitialize();
12856 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12857 if (FAILED(rc))
12858 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12859 multilock.acquire();
12860 }
12861 mData->mSession.mRemoteControls.clear();
12862 }
12863
12864 /* Remove all references to the NAT network service. The service will stop
12865 * if all references (also from other VMs) are removed. */
12866 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12867 {
12868 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12869 {
12870 BOOL enabled;
12871 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12872 if ( FAILED(hrc)
12873 || !enabled)
12874 continue;
12875
12876 NetworkAttachmentType_T type;
12877 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12878 if ( SUCCEEDED(hrc)
12879 && type == NetworkAttachmentType_NATNetwork)
12880 {
12881 Bstr name;
12882 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12883 if (SUCCEEDED(hrc))
12884 {
12885 multilock.release();
12886 Utf8Str strName(name);
12887 LogRel(("VM '%s' stops using NAT network '%s'\n",
12888 mUserData->s.strName.c_str(), strName.c_str()));
12889 mParent->i_natNetworkRefDec(strName);
12890 multilock.acquire();
12891 }
12892 }
12893 }
12894 }
12895
12896 /*
12897 * An expected uninitialization can come only from #i_checkForDeath().
12898 * Otherwise it means that something's gone really wrong (for example,
12899 * the Session implementation has released the VirtualBox reference
12900 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12901 * etc). However, it's also possible, that the client releases the IPC
12902 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12903 * but the VirtualBox release event comes first to the server process.
12904 * This case is practically possible, so we should not assert on an
12905 * unexpected uninit, just log a warning.
12906 */
12907
12908 if (aReason == Uninit::Unexpected)
12909 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12910
12911 if (aReason != Uninit::Normal)
12912 {
12913 mData->mSession.mDirectControl.setNull();
12914 }
12915 else
12916 {
12917 /* this must be null here (see #OnSessionEnd()) */
12918 Assert(mData->mSession.mDirectControl.isNull());
12919 Assert(mData->mSession.mState == SessionState_Unlocking);
12920 Assert(!mData->mSession.mProgress.isNull());
12921 }
12922 if (mData->mSession.mProgress)
12923 {
12924 if (aReason == Uninit::Normal)
12925 mData->mSession.mProgress->i_notifyComplete(S_OK);
12926 else
12927 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12928 COM_IIDOF(ISession),
12929 getComponentName(),
12930 tr("The VM session was aborted"));
12931 mData->mSession.mProgress.setNull();
12932 }
12933
12934 if (mConsoleTaskData.mProgress)
12935 {
12936 Assert(aReason == Uninit::Abnormal);
12937 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12938 COM_IIDOF(ISession),
12939 getComponentName(),
12940 tr("The VM session was aborted"));
12941 mConsoleTaskData.mProgress.setNull();
12942 }
12943
12944 /* remove the association between the peer machine and this session machine */
12945 Assert( (SessionMachine*)mData->mSession.mMachine == this
12946 || aReason == Uninit::Unexpected);
12947
12948 /* reset the rest of session data */
12949 mData->mSession.mLockType = LockType_Null;
12950 mData->mSession.mMachine.setNull();
12951 mData->mSession.mState = SessionState_Unlocked;
12952 mData->mSession.mName.setNull();
12953
12954 /* destroy the machine client token before leaving the exclusive lock */
12955 if (mClientToken)
12956 {
12957 delete mClientToken;
12958 mClientToken = NULL;
12959 }
12960
12961 /* fire an event */
12962 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12963
12964 uninitDataAndChildObjects();
12965
12966 /* free the essential data structure last */
12967 mData.free();
12968
12969 /* release the exclusive lock before setting the below two to NULL */
12970 multilock.release();
12971
12972 unconst(mParent) = NULL;
12973 unconst(mPeer) = NULL;
12974
12975 AuthLibUnload(&mAuthLibCtx);
12976
12977 LogFlowThisFuncLeave();
12978}
12979
12980// util::Lockable interface
12981////////////////////////////////////////////////////////////////////////////////
12982
12983/**
12984 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12985 * with the primary Machine instance (mPeer).
12986 */
12987RWLockHandle *SessionMachine::lockHandle() const
12988{
12989 AssertReturn(mPeer != NULL, NULL);
12990 return mPeer->lockHandle();
12991}
12992
12993// IInternalMachineControl methods
12994////////////////////////////////////////////////////////////////////////////////
12995
12996/**
12997 * Passes collected guest statistics to performance collector object
12998 */
12999HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13000 ULONG aCpuKernel, ULONG aCpuIdle,
13001 ULONG aMemTotal, ULONG aMemFree,
13002 ULONG aMemBalloon, ULONG aMemShared,
13003 ULONG aMemCache, ULONG aPageTotal,
13004 ULONG aAllocVMM, ULONG aFreeVMM,
13005 ULONG aBalloonedVMM, ULONG aSharedVMM,
13006 ULONG aVmNetRx, ULONG aVmNetTx)
13007{
13008#ifdef VBOX_WITH_RESOURCE_USAGE_API
13009 if (mCollectorGuest)
13010 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13011 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13012 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13013 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13014
13015 return S_OK;
13016#else
13017 NOREF(aValidStats);
13018 NOREF(aCpuUser);
13019 NOREF(aCpuKernel);
13020 NOREF(aCpuIdle);
13021 NOREF(aMemTotal);
13022 NOREF(aMemFree);
13023 NOREF(aMemBalloon);
13024 NOREF(aMemShared);
13025 NOREF(aMemCache);
13026 NOREF(aPageTotal);
13027 NOREF(aAllocVMM);
13028 NOREF(aFreeVMM);
13029 NOREF(aBalloonedVMM);
13030 NOREF(aSharedVMM);
13031 NOREF(aVmNetRx);
13032 NOREF(aVmNetTx);
13033 return E_NOTIMPL;
13034#endif
13035}
13036
13037////////////////////////////////////////////////////////////////////////////////
13038//
13039// SessionMachine task records
13040//
13041////////////////////////////////////////////////////////////////////////////////
13042
13043/**
13044 * Task record for saving the machine state.
13045 */
13046class SessionMachine::SaveStateTask
13047 : public Machine::Task
13048{
13049public:
13050 SaveStateTask(SessionMachine *m,
13051 Progress *p,
13052 const Utf8Str &t,
13053 Reason_T enmReason,
13054 const Utf8Str &strStateFilePath)
13055 : Task(m, p, t),
13056 m_enmReason(enmReason),
13057 m_strStateFilePath(strStateFilePath)
13058 {}
13059
13060private:
13061 void handler()
13062 {
13063 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13064 }
13065
13066 Reason_T m_enmReason;
13067 Utf8Str m_strStateFilePath;
13068
13069 friend class SessionMachine;
13070};
13071
13072/**
13073 * Task thread implementation for SessionMachine::SaveState(), called from
13074 * SessionMachine::taskHandler().
13075 *
13076 * @note Locks this object for writing.
13077 *
13078 * @param task
13079 * @return
13080 */
13081void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13082{
13083 LogFlowThisFuncEnter();
13084
13085 AutoCaller autoCaller(this);
13086 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13087 if (FAILED(autoCaller.rc()))
13088 {
13089 /* we might have been uninitialized because the session was accidentally
13090 * closed by the client, so don't assert */
13091 HRESULT rc = setError(E_FAIL,
13092 tr("The session has been accidentally closed"));
13093 task.m_pProgress->i_notifyComplete(rc);
13094 LogFlowThisFuncLeave();
13095 return;
13096 }
13097
13098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13099
13100 HRESULT rc = S_OK;
13101
13102 try
13103 {
13104 ComPtr<IInternalSessionControl> directControl;
13105 if (mData->mSession.mLockType == LockType_VM)
13106 directControl = mData->mSession.mDirectControl;
13107 if (directControl.isNull())
13108 throw setError(VBOX_E_INVALID_VM_STATE,
13109 tr("Trying to save state without a running VM"));
13110 alock.release();
13111 BOOL fSuspendedBySave;
13112 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13113 Assert(!fSuspendedBySave);
13114 alock.acquire();
13115
13116 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13117 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13118 throw E_FAIL);
13119
13120 if (SUCCEEDED(rc))
13121 {
13122 mSSData->strStateFilePath = task.m_strStateFilePath;
13123
13124 /* save all VM settings */
13125 rc = i_saveSettings(NULL, alock);
13126 // no need to check whether VirtualBox.xml needs saving also since
13127 // we can't have a name change pending at this point
13128 }
13129 else
13130 {
13131 // On failure, set the state to the state we had at the beginning.
13132 i_setMachineState(task.m_machineStateBackup);
13133 i_updateMachineStateOnClient();
13134
13135 // Delete the saved state file (might have been already created).
13136 // No need to check whether this is shared with a snapshot here
13137 // because we certainly created a fresh saved state file here.
13138 RTFileDelete(task.m_strStateFilePath.c_str());
13139 }
13140 }
13141 catch (HRESULT aRC) { rc = aRC; }
13142
13143 task.m_pProgress->i_notifyComplete(rc);
13144
13145 LogFlowThisFuncLeave();
13146}
13147
13148/**
13149 * @note Locks this object for writing.
13150 */
13151HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13152{
13153 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13154}
13155
13156HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13157{
13158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13159
13160 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13161 if (FAILED(rc)) return rc;
13162
13163 if ( mData->mMachineState != MachineState_Running
13164 && mData->mMachineState != MachineState_Paused
13165 )
13166 return setError(VBOX_E_INVALID_VM_STATE,
13167 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13168 Global::stringifyMachineState(mData->mMachineState));
13169
13170 ComObjPtr<Progress> pProgress;
13171 pProgress.createObject();
13172 rc = pProgress->init(i_getVirtualBox(),
13173 static_cast<IMachine *>(this) /* aInitiator */,
13174 tr("Saving the execution state of the virtual machine"),
13175 FALSE /* aCancelable */);
13176 if (FAILED(rc))
13177 return rc;
13178
13179 Utf8Str strStateFilePath;
13180 i_composeSavedStateFilename(strStateFilePath);
13181
13182 /* create and start the task on a separate thread (note that it will not
13183 * start working until we release alock) */
13184 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13185 rc = pTask->createThread();
13186 if (FAILED(rc))
13187 return rc;
13188
13189 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13190 i_setMachineState(MachineState_Saving);
13191 i_updateMachineStateOnClient();
13192
13193 pProgress.queryInterfaceTo(aProgress.asOutParam());
13194
13195 return S_OK;
13196}
13197
13198/**
13199 * @note Locks this object for writing.
13200 */
13201HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13202{
13203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13204
13205 HRESULT rc = i_checkStateDependency(MutableStateDep);
13206 if (FAILED(rc)) return rc;
13207
13208 if ( mData->mMachineState != MachineState_PoweredOff
13209 && mData->mMachineState != MachineState_Teleported
13210 && mData->mMachineState != MachineState_Aborted
13211 )
13212 return setError(VBOX_E_INVALID_VM_STATE,
13213 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13214 Global::stringifyMachineState(mData->mMachineState));
13215
13216 com::Utf8Str stateFilePathFull;
13217 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13218 if (RT_FAILURE(vrc))
13219 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13220 tr("Invalid saved state file path '%s' (%Rrc)"),
13221 aSavedStateFile.c_str(),
13222 vrc);
13223
13224 mSSData->strStateFilePath = stateFilePathFull;
13225
13226 /* The below i_setMachineState() will detect the state transition and will
13227 * update the settings file */
13228
13229 return i_setMachineState(MachineState_Saved);
13230}
13231
13232/**
13233 * @note Locks this object for writing.
13234 */
13235HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13236{
13237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13238
13239 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13240 if (FAILED(rc)) return rc;
13241
13242 if ( mData->mMachineState != MachineState_Saved
13243 && mData->mMachineState != MachineState_AbortedSaved)
13244 return setError(VBOX_E_INVALID_VM_STATE,
13245 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13246 Global::stringifyMachineState(mData->mMachineState));
13247
13248 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13249
13250 /*
13251 * Saved -> PoweredOff transition will be detected in the SessionMachine
13252 * and properly handled.
13253 */
13254 rc = i_setMachineState(MachineState_PoweredOff);
13255 return rc;
13256}
13257
13258
13259/**
13260 * @note Locks the same as #i_setMachineState() does.
13261 */
13262HRESULT SessionMachine::updateState(MachineState_T aState)
13263{
13264 return i_setMachineState(aState);
13265}
13266
13267/**
13268 * @note Locks this object for writing.
13269 */
13270HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13271{
13272 IProgress *pProgress(aProgress);
13273
13274 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13275
13276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13277
13278 if (mData->mSession.mState != SessionState_Locked)
13279 return VBOX_E_INVALID_OBJECT_STATE;
13280
13281 if (!mData->mSession.mProgress.isNull())
13282 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13283
13284 /* If we didn't reference the NAT network service yet, add a reference to
13285 * force a start */
13286 if (miNATNetworksStarted < 1)
13287 {
13288 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13289 {
13290 BOOL enabled;
13291 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13292 if ( FAILED(hrc)
13293 || !enabled)
13294 continue;
13295
13296 NetworkAttachmentType_T type;
13297 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13298 if ( SUCCEEDED(hrc)
13299 && type == NetworkAttachmentType_NATNetwork)
13300 {
13301 Bstr name;
13302 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13303 if (SUCCEEDED(hrc))
13304 {
13305 Utf8Str strName(name);
13306 LogRel(("VM '%s' starts using NAT network '%s'\n",
13307 mUserData->s.strName.c_str(), strName.c_str()));
13308 mPeer->lockHandle()->unlockWrite();
13309 mParent->i_natNetworkRefInc(strName);
13310#ifdef RT_LOCK_STRICT
13311 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13312#else
13313 mPeer->lockHandle()->lockWrite();
13314#endif
13315 }
13316 }
13317 }
13318 miNATNetworksStarted++;
13319 }
13320
13321 LogFlowThisFunc(("returns S_OK.\n"));
13322 return S_OK;
13323}
13324
13325/**
13326 * @note Locks this object for writing.
13327 */
13328HRESULT SessionMachine::endPowerUp(LONG aResult)
13329{
13330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13331
13332 if (mData->mSession.mState != SessionState_Locked)
13333 return VBOX_E_INVALID_OBJECT_STATE;
13334
13335 /* Finalize the LaunchVMProcess progress object. */
13336 if (mData->mSession.mProgress)
13337 {
13338 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13339 mData->mSession.mProgress.setNull();
13340 }
13341
13342 if (SUCCEEDED((HRESULT)aResult))
13343 {
13344#ifdef VBOX_WITH_RESOURCE_USAGE_API
13345 /* The VM has been powered up successfully, so it makes sense
13346 * now to offer the performance metrics for a running machine
13347 * object. Doing it earlier wouldn't be safe. */
13348 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13349 mData->mSession.mPID);
13350#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13351 }
13352
13353 return S_OK;
13354}
13355
13356/**
13357 * @note Locks this object for writing.
13358 */
13359HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13360{
13361 LogFlowThisFuncEnter();
13362
13363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13364
13365 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13366 E_FAIL);
13367
13368 /* create a progress object to track operation completion */
13369 ComObjPtr<Progress> pProgress;
13370 pProgress.createObject();
13371 pProgress->init(i_getVirtualBox(),
13372 static_cast<IMachine *>(this) /* aInitiator */,
13373 tr("Stopping the virtual machine"),
13374 FALSE /* aCancelable */);
13375
13376 /* fill in the console task data */
13377 mConsoleTaskData.mLastState = mData->mMachineState;
13378 mConsoleTaskData.mProgress = pProgress;
13379
13380 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13381 i_setMachineState(MachineState_Stopping);
13382
13383 pProgress.queryInterfaceTo(aProgress.asOutParam());
13384
13385 return S_OK;
13386}
13387
13388/**
13389 * @note Locks this object for writing.
13390 */
13391HRESULT SessionMachine::endPoweringDown(LONG aResult,
13392 const com::Utf8Str &aErrMsg)
13393{
13394 HRESULT const hrcResult = (HRESULT)aResult;
13395 LogFlowThisFuncEnter();
13396
13397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13398
13399 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13400 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13401 && mConsoleTaskData.mLastState != MachineState_Null,
13402 E_FAIL);
13403
13404 /*
13405 * On failure, set the state to the state we had when BeginPoweringDown()
13406 * was called (this is expected by Console::PowerDown() and the associated
13407 * task). On success the VM process already changed the state to
13408 * MachineState_PoweredOff, so no need to do anything.
13409 */
13410 if (FAILED(hrcResult))
13411 i_setMachineState(mConsoleTaskData.mLastState);
13412
13413 /* notify the progress object about operation completion */
13414 Assert(mConsoleTaskData.mProgress);
13415 if (SUCCEEDED(hrcResult))
13416 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13417 else
13418 {
13419 if (aErrMsg.length())
13420 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13421 COM_IIDOF(ISession),
13422 getComponentName(),
13423 aErrMsg.c_str());
13424 else
13425 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13426 }
13427
13428 /* clear out the temporary saved state data */
13429 mConsoleTaskData.mLastState = MachineState_Null;
13430 mConsoleTaskData.mProgress.setNull();
13431
13432 LogFlowThisFuncLeave();
13433 return S_OK;
13434}
13435
13436
13437/**
13438 * Goes through the USB filters of the given machine to see if the given
13439 * device matches any filter or not.
13440 *
13441 * @note Locks the same as USBController::hasMatchingFilter() does.
13442 */
13443HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13444 BOOL *aMatched,
13445 ULONG *aMaskedInterfaces)
13446{
13447 LogFlowThisFunc(("\n"));
13448
13449#ifdef VBOX_WITH_USB
13450 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13451#else
13452 NOREF(aDevice);
13453 NOREF(aMaskedInterfaces);
13454 *aMatched = FALSE;
13455#endif
13456
13457 return S_OK;
13458}
13459
13460/**
13461 * @note Locks the same as Host::captureUSBDevice() does.
13462 */
13463HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13464{
13465 LogFlowThisFunc(("\n"));
13466
13467#ifdef VBOX_WITH_USB
13468 /* if captureDeviceForVM() fails, it must have set extended error info */
13469 clearError();
13470 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13471 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13472 return rc;
13473
13474 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13475 AssertReturn(service, E_FAIL);
13476 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13477#else
13478 RT_NOREF(aId, aCaptureFilename);
13479 return E_NOTIMPL;
13480#endif
13481}
13482
13483/**
13484 * @note Locks the same as Host::detachUSBDevice() does.
13485 */
13486HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13487 BOOL aDone)
13488{
13489 LogFlowThisFunc(("\n"));
13490
13491#ifdef VBOX_WITH_USB
13492 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13493 AssertReturn(service, E_FAIL);
13494 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13495#else
13496 NOREF(aId);
13497 NOREF(aDone);
13498 return E_NOTIMPL;
13499#endif
13500}
13501
13502/**
13503 * Inserts all machine filters to the USB proxy service and then calls
13504 * Host::autoCaptureUSBDevices().
13505 *
13506 * Called by Console from the VM process upon VM startup.
13507 *
13508 * @note Locks what called methods lock.
13509 */
13510HRESULT SessionMachine::autoCaptureUSBDevices()
13511{
13512 LogFlowThisFunc(("\n"));
13513
13514#ifdef VBOX_WITH_USB
13515 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13516 AssertComRC(rc);
13517 NOREF(rc);
13518
13519 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13520 AssertReturn(service, E_FAIL);
13521 return service->autoCaptureDevicesForVM(this);
13522#else
13523 return S_OK;
13524#endif
13525}
13526
13527/**
13528 * Removes all machine filters from the USB proxy service and then calls
13529 * Host::detachAllUSBDevices().
13530 *
13531 * Called by Console from the VM process upon normal VM termination or by
13532 * SessionMachine::uninit() upon abnormal VM termination (from under the
13533 * Machine/SessionMachine lock).
13534 *
13535 * @note Locks what called methods lock.
13536 */
13537HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13538{
13539 LogFlowThisFunc(("\n"));
13540
13541#ifdef VBOX_WITH_USB
13542 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13543 AssertComRC(rc);
13544 NOREF(rc);
13545
13546 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13547 AssertReturn(service, E_FAIL);
13548 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13549#else
13550 NOREF(aDone);
13551 return S_OK;
13552#endif
13553}
13554
13555/**
13556 * @note Locks this object for writing.
13557 */
13558HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13559 ComPtr<IProgress> &aProgress)
13560{
13561 LogFlowThisFuncEnter();
13562
13563 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13564 /*
13565 * We don't assert below because it might happen that a non-direct session
13566 * informs us it is closed right after we've been uninitialized -- it's ok.
13567 */
13568
13569 /* get IInternalSessionControl interface */
13570 ComPtr<IInternalSessionControl> control(aSession);
13571
13572 ComAssertRet(!control.isNull(), E_INVALIDARG);
13573
13574 /* Creating a Progress object requires the VirtualBox lock, and
13575 * thus locking it here is required by the lock order rules. */
13576 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13577
13578 if (control == mData->mSession.mDirectControl)
13579 {
13580 /* The direct session is being normally closed by the client process
13581 * ----------------------------------------------------------------- */
13582
13583 /* go to the closing state (essential for all open*Session() calls and
13584 * for #i_checkForDeath()) */
13585 Assert(mData->mSession.mState == SessionState_Locked);
13586 mData->mSession.mState = SessionState_Unlocking;
13587
13588 /* set direct control to NULL to release the remote instance */
13589 mData->mSession.mDirectControl.setNull();
13590 LogFlowThisFunc(("Direct control is set to NULL\n"));
13591
13592 if (mData->mSession.mProgress)
13593 {
13594 /* finalize the progress, someone might wait if a frontend
13595 * closes the session before powering on the VM. */
13596 mData->mSession.mProgress->notifyComplete(E_FAIL,
13597 COM_IIDOF(ISession),
13598 getComponentName(),
13599 tr("The VM session was closed before any attempt to power it on"));
13600 mData->mSession.mProgress.setNull();
13601 }
13602
13603 /* Create the progress object the client will use to wait until
13604 * #i_checkForDeath() is called to uninitialize this session object after
13605 * it releases the IPC semaphore.
13606 * Note! Because we're "reusing" mProgress here, this must be a proxy
13607 * object just like for LaunchVMProcess. */
13608 Assert(mData->mSession.mProgress.isNull());
13609 ComObjPtr<ProgressProxy> progress;
13610 progress.createObject();
13611 ComPtr<IUnknown> pPeer(mPeer);
13612 progress->init(mParent, pPeer,
13613 Bstr(tr("Closing session")).raw(),
13614 FALSE /* aCancelable */);
13615 progress.queryInterfaceTo(aProgress.asOutParam());
13616 mData->mSession.mProgress = progress;
13617 }
13618 else
13619 {
13620 /* the remote session is being normally closed */
13621 bool found = false;
13622 for (Data::Session::RemoteControlList::iterator
13623 it = mData->mSession.mRemoteControls.begin();
13624 it != mData->mSession.mRemoteControls.end();
13625 ++it)
13626 {
13627 if (control == *it)
13628 {
13629 found = true;
13630 // This MUST be erase(it), not remove(*it) as the latter
13631 // triggers a very nasty use after free due to the place where
13632 // the value "lives".
13633 mData->mSession.mRemoteControls.erase(it);
13634 break;
13635 }
13636 }
13637 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13638 E_INVALIDARG);
13639 }
13640
13641 /* signal the client watcher thread, because the client is going away */
13642 mParent->i_updateClientWatcher();
13643
13644 LogFlowThisFuncLeave();
13645 return S_OK;
13646}
13647
13648HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13649 std::vector<com::Utf8Str> &aValues,
13650 std::vector<LONG64> &aTimestamps,
13651 std::vector<com::Utf8Str> &aFlags)
13652{
13653 LogFlowThisFunc(("\n"));
13654
13655#ifdef VBOX_WITH_GUEST_PROPS
13656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13657
13658 size_t cEntries = mHWData->mGuestProperties.size();
13659 aNames.resize(cEntries);
13660 aValues.resize(cEntries);
13661 aTimestamps.resize(cEntries);
13662 aFlags.resize(cEntries);
13663
13664 size_t i = 0;
13665 for (HWData::GuestPropertyMap::const_iterator
13666 it = mHWData->mGuestProperties.begin();
13667 it != mHWData->mGuestProperties.end();
13668 ++it, ++i)
13669 {
13670 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13671 aNames[i] = it->first;
13672 aValues[i] = it->second.strValue;
13673 aTimestamps[i] = it->second.mTimestamp;
13674
13675 /* If it is NULL, keep it NULL. */
13676 if (it->second.mFlags)
13677 {
13678 GuestPropWriteFlags(it->second.mFlags, szFlags);
13679 aFlags[i] = szFlags;
13680 }
13681 else
13682 aFlags[i] = "";
13683
13684 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13685 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13686 }
13687 return S_OK;
13688#else
13689 ReturnComNotImplemented();
13690#endif
13691}
13692
13693HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13694 const com::Utf8Str &aValue,
13695 LONG64 aTimestamp,
13696 const com::Utf8Str &aFlags,
13697 BOOL fWasDeleted)
13698{
13699 LogFlowThisFunc(("\n"));
13700
13701#ifdef VBOX_WITH_GUEST_PROPS
13702 try
13703 {
13704 /*
13705 * Convert input up front.
13706 */
13707 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13708 if (aFlags.length())
13709 {
13710 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13711 AssertRCReturn(vrc, E_INVALIDARG);
13712 }
13713
13714 /*
13715 * Now grab the object lock, validate the state and do the update.
13716 */
13717
13718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13719
13720 if (!Global::IsOnline(mData->mMachineState))
13721 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13722
13723 i_setModified(IsModified_MachineData);
13724 mHWData.backup();
13725
13726 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13727 if (it != mHWData->mGuestProperties.end())
13728 {
13729 if (!fWasDeleted)
13730 {
13731 it->second.strValue = aValue;
13732 it->second.mTimestamp = aTimestamp;
13733 it->second.mFlags = fFlags;
13734 }
13735 else
13736 mHWData->mGuestProperties.erase(it);
13737
13738 mData->mGuestPropertiesModified = TRUE;
13739 }
13740 else if (!fWasDeleted)
13741 {
13742 HWData::GuestProperty prop;
13743 prop.strValue = aValue;
13744 prop.mTimestamp = aTimestamp;
13745 prop.mFlags = fFlags;
13746
13747 mHWData->mGuestProperties[aName] = prop;
13748 mData->mGuestPropertiesModified = TRUE;
13749 }
13750
13751 alock.release();
13752
13753 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
13754 }
13755 catch (...)
13756 {
13757 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13758 }
13759 return S_OK;
13760#else
13761 ReturnComNotImplemented();
13762#endif
13763}
13764
13765
13766HRESULT SessionMachine::lockMedia()
13767{
13768 AutoMultiWriteLock2 alock(this->lockHandle(),
13769 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13770
13771 AssertReturn( mData->mMachineState == MachineState_Starting
13772 || mData->mMachineState == MachineState_Restoring
13773 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13774
13775 clearError();
13776 alock.release();
13777 return i_lockMedia();
13778}
13779
13780HRESULT SessionMachine::unlockMedia()
13781{
13782 HRESULT hrc = i_unlockMedia();
13783 return hrc;
13784}
13785
13786HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13787 ComPtr<IMediumAttachment> &aNewAttachment)
13788{
13789 // request the host lock first, since might be calling Host methods for getting host drives;
13790 // next, protect the media tree all the while we're in here, as well as our member variables
13791 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13792 this->lockHandle(),
13793 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13794
13795 IMediumAttachment *iAttach = aAttachment;
13796 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13797
13798 Utf8Str ctrlName;
13799 LONG lPort;
13800 LONG lDevice;
13801 bool fTempEject;
13802 {
13803 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13804
13805 /* Need to query the details first, as the IMediumAttachment reference
13806 * might be to the original settings, which we are going to change. */
13807 ctrlName = pAttach->i_getControllerName();
13808 lPort = pAttach->i_getPort();
13809 lDevice = pAttach->i_getDevice();
13810 fTempEject = pAttach->i_getTempEject();
13811 }
13812
13813 if (!fTempEject)
13814 {
13815 /* Remember previously mounted medium. The medium before taking the
13816 * backup is not necessarily the same thing. */
13817 ComObjPtr<Medium> oldmedium;
13818 oldmedium = pAttach->i_getMedium();
13819
13820 i_setModified(IsModified_Storage);
13821 mMediumAttachments.backup();
13822
13823 // The backup operation makes the pAttach reference point to the
13824 // old settings. Re-get the correct reference.
13825 pAttach = i_findAttachment(*mMediumAttachments.data(),
13826 ctrlName,
13827 lPort,
13828 lDevice);
13829
13830 {
13831 AutoCaller autoAttachCaller(this);
13832 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13833
13834 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13835 if (!oldmedium.isNull())
13836 oldmedium->i_removeBackReference(mData->mUuid);
13837
13838 pAttach->i_updateMedium(NULL);
13839 pAttach->i_updateEjected();
13840 }
13841
13842 i_setModified(IsModified_Storage);
13843 }
13844 else
13845 {
13846 {
13847 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13848 pAttach->i_updateEjected();
13849 }
13850 }
13851
13852 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13853
13854 return S_OK;
13855}
13856
13857HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13858 com::Utf8Str &aResult)
13859{
13860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13861
13862 HRESULT hr = S_OK;
13863
13864 if (!mAuthLibCtx.hAuthLibrary)
13865 {
13866 /* Load the external authentication library. */
13867 Bstr authLibrary;
13868 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13869
13870 Utf8Str filename = authLibrary;
13871
13872 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13873 if (RT_FAILURE(vrc))
13874 hr = setErrorBoth(E_FAIL, vrc,
13875 tr("Could not load the external authentication library '%s' (%Rrc)"),
13876 filename.c_str(), vrc);
13877 }
13878
13879 /* The auth library might need the machine lock. */
13880 alock.release();
13881
13882 if (FAILED(hr))
13883 return hr;
13884
13885 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13886 {
13887 enum VRDEAuthParams
13888 {
13889 parmUuid = 1,
13890 parmGuestJudgement,
13891 parmUser,
13892 parmPassword,
13893 parmDomain,
13894 parmClientId
13895 };
13896
13897 AuthResult result = AuthResultAccessDenied;
13898
13899 Guid uuid(aAuthParams[parmUuid]);
13900 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13901 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13902
13903 result = AuthLibAuthenticate(&mAuthLibCtx,
13904 uuid.raw(), guestJudgement,
13905 aAuthParams[parmUser].c_str(),
13906 aAuthParams[parmPassword].c_str(),
13907 aAuthParams[parmDomain].c_str(),
13908 u32ClientId);
13909
13910 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13911 size_t cbPassword = aAuthParams[parmPassword].length();
13912 if (cbPassword)
13913 {
13914 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13915 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13916 }
13917
13918 if (result == AuthResultAccessGranted)
13919 aResult = "granted";
13920 else
13921 aResult = "denied";
13922
13923 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13924 aAuthParams[parmUser].c_str(), aResult.c_str()));
13925 }
13926 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13927 {
13928 enum VRDEAuthDisconnectParams
13929 {
13930 parmUuid = 1,
13931 parmClientId
13932 };
13933
13934 Guid uuid(aAuthParams[parmUuid]);
13935 uint32_t u32ClientId = 0;
13936 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13937 }
13938 else
13939 {
13940 hr = E_INVALIDARG;
13941 }
13942
13943 return hr;
13944}
13945
13946// public methods only for internal purposes
13947/////////////////////////////////////////////////////////////////////////////
13948
13949#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13950/**
13951 * Called from the client watcher thread to check for expected or unexpected
13952 * death of the client process that has a direct session to this machine.
13953 *
13954 * On Win32 and on OS/2, this method is called only when we've got the
13955 * mutex (i.e. the client has either died or terminated normally) so it always
13956 * returns @c true (the client is terminated, the session machine is
13957 * uninitialized).
13958 *
13959 * On other platforms, the method returns @c true if the client process has
13960 * terminated normally or abnormally and the session machine was uninitialized,
13961 * and @c false if the client process is still alive.
13962 *
13963 * @note Locks this object for writing.
13964 */
13965bool SessionMachine::i_checkForDeath()
13966{
13967 Uninit::Reason reason;
13968 bool terminated = false;
13969
13970 /* Enclose autoCaller with a block because calling uninit() from under it
13971 * will deadlock. */
13972 {
13973 AutoCaller autoCaller(this);
13974 if (!autoCaller.isOk())
13975 {
13976 /* return true if not ready, to cause the client watcher to exclude
13977 * the corresponding session from watching */
13978 LogFlowThisFunc(("Already uninitialized!\n"));
13979 return true;
13980 }
13981
13982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13983
13984 /* Determine the reason of death: if the session state is Closing here,
13985 * everything is fine. Otherwise it means that the client did not call
13986 * OnSessionEnd() before it released the IPC semaphore. This may happen
13987 * either because the client process has abnormally terminated, or
13988 * because it simply forgot to call ISession::Close() before exiting. We
13989 * threat the latter also as an abnormal termination (see
13990 * Session::uninit() for details). */
13991 reason = mData->mSession.mState == SessionState_Unlocking ?
13992 Uninit::Normal :
13993 Uninit::Abnormal;
13994
13995 if (mClientToken)
13996 terminated = mClientToken->release();
13997 } /* AutoCaller block */
13998
13999 if (terminated)
14000 uninit(reason);
14001
14002 return terminated;
14003}
14004
14005void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14006{
14007 LogFlowThisFunc(("\n"));
14008
14009 strTokenId.setNull();
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturnVoid(autoCaller.rc());
14013
14014 Assert(mClientToken);
14015 if (mClientToken)
14016 mClientToken->getId(strTokenId);
14017}
14018#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14019IToken *SessionMachine::i_getToken()
14020{
14021 LogFlowThisFunc(("\n"));
14022
14023 AutoCaller autoCaller(this);
14024 AssertComRCReturn(autoCaller.rc(), NULL);
14025
14026 Assert(mClientToken);
14027 if (mClientToken)
14028 return mClientToken->getToken();
14029 else
14030 return NULL;
14031}
14032#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14033
14034Machine::ClientToken *SessionMachine::i_getClientToken()
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 AutoCaller autoCaller(this);
14039 AssertComRCReturn(autoCaller.rc(), NULL);
14040
14041 return mClientToken;
14042}
14043
14044
14045/**
14046 * @note Locks this object for reading.
14047 */
14048HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14049{
14050 LogFlowThisFunc(("\n"));
14051
14052 AutoCaller autoCaller(this);
14053 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14054
14055 ComPtr<IInternalSessionControl> directControl;
14056 {
14057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14058 if (mData->mSession.mLockType == LockType_VM)
14059 directControl = mData->mSession.mDirectControl;
14060 }
14061
14062 /* ignore notifications sent after #OnSessionEnd() is called */
14063 if (!directControl)
14064 return S_OK;
14065
14066 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14067}
14068
14069/**
14070 * @note Locks this object for reading.
14071 */
14072HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14073 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14074 const Utf8Str &aGuestIp, LONG aGuestPort)
14075{
14076 LogFlowThisFunc(("\n"));
14077
14078 AutoCaller autoCaller(this);
14079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14080
14081 ComPtr<IInternalSessionControl> directControl;
14082 {
14083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14084 if (mData->mSession.mLockType == LockType_VM)
14085 directControl = mData->mSession.mDirectControl;
14086 }
14087
14088 /* ignore notifications sent after #OnSessionEnd() is called */
14089 if (!directControl)
14090 return S_OK;
14091 /*
14092 * instead acting like callback we ask IVirtualBox deliver corresponding event
14093 */
14094
14095 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14096 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14097 return S_OK;
14098}
14099
14100/**
14101 * @note Locks this object for reading.
14102 */
14103HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 if (mData->mSession.mLockType == LockType_VM)
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnAudioAdapterChange(audioAdapter);
14122}
14123
14124/**
14125 * @note Locks this object for reading.
14126 */
14127HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14128{
14129 LogFlowThisFunc(("\n"));
14130
14131 AutoCaller autoCaller(this);
14132 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14133
14134 ComPtr<IInternalSessionControl> directControl;
14135 {
14136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14137 if (mData->mSession.mLockType == LockType_VM)
14138 directControl = mData->mSession.mDirectControl;
14139 }
14140
14141 /* ignore notifications sent after #OnSessionEnd() is called */
14142 if (!directControl)
14143 return S_OK;
14144
14145 return directControl->OnSerialPortChange(serialPort);
14146}
14147
14148/**
14149 * @note Locks this object for reading.
14150 */
14151HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14152{
14153 LogFlowThisFunc(("\n"));
14154
14155 AutoCaller autoCaller(this);
14156 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14157
14158 ComPtr<IInternalSessionControl> directControl;
14159 {
14160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14161 if (mData->mSession.mLockType == LockType_VM)
14162 directControl = mData->mSession.mDirectControl;
14163 }
14164
14165 /* ignore notifications sent after #OnSessionEnd() is called */
14166 if (!directControl)
14167 return S_OK;
14168
14169 return directControl->OnParallelPortChange(parallelPort);
14170}
14171
14172/**
14173 * @note Locks this object for reading.
14174 */
14175HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14176{
14177 LogFlowThisFunc(("\n"));
14178
14179 AutoCaller autoCaller(this);
14180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14181
14182 ComPtr<IInternalSessionControl> directControl;
14183 {
14184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14185 if (mData->mSession.mLockType == LockType_VM)
14186 directControl = mData->mSession.mDirectControl;
14187 }
14188
14189 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14190
14191 /* ignore notifications sent after #OnSessionEnd() is called */
14192 if (!directControl)
14193 return S_OK;
14194
14195 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14196}
14197
14198/**
14199 * @note Locks this object for reading.
14200 */
14201HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14202{
14203 LogFlowThisFunc(("\n"));
14204
14205 AutoCaller autoCaller(this);
14206 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14207
14208 ComPtr<IInternalSessionControl> directControl;
14209 {
14210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14211 if (mData->mSession.mLockType == LockType_VM)
14212 directControl = mData->mSession.mDirectControl;
14213 }
14214
14215 mParent->i_onMediumChanged(aAttachment);
14216
14217 /* ignore notifications sent after #OnSessionEnd() is called */
14218 if (!directControl)
14219 return S_OK;
14220
14221 return directControl->OnMediumChange(aAttachment, aForce);
14222}
14223
14224HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14225{
14226 LogFlowThisFunc(("\n"));
14227
14228 AutoCaller autoCaller(this);
14229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14230
14231 ComPtr<IInternalSessionControl> directControl;
14232 {
14233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14234 if (mData->mSession.mLockType == LockType_VM)
14235 directControl = mData->mSession.mDirectControl;
14236 }
14237
14238 /* ignore notifications sent after #OnSessionEnd() is called */
14239 if (!directControl)
14240 return S_OK;
14241
14242 return directControl->OnVMProcessPriorityChange(aPriority);
14243}
14244
14245/**
14246 * @note Locks this object for reading.
14247 */
14248HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14249{
14250 LogFlowThisFunc(("\n"));
14251
14252 AutoCaller autoCaller(this);
14253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14254
14255 ComPtr<IInternalSessionControl> directControl;
14256 {
14257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14258 if (mData->mSession.mLockType == LockType_VM)
14259 directControl = mData->mSession.mDirectControl;
14260 }
14261
14262 /* ignore notifications sent after #OnSessionEnd() is called */
14263 if (!directControl)
14264 return S_OK;
14265
14266 return directControl->OnCPUChange(aCPU, aRemove);
14267}
14268
14269HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14270{
14271 LogFlowThisFunc(("\n"));
14272
14273 AutoCaller autoCaller(this);
14274 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14275
14276 ComPtr<IInternalSessionControl> directControl;
14277 {
14278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14279 if (mData->mSession.mLockType == LockType_VM)
14280 directControl = mData->mSession.mDirectControl;
14281 }
14282
14283 /* ignore notifications sent after #OnSessionEnd() is called */
14284 if (!directControl)
14285 return S_OK;
14286
14287 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14288}
14289
14290/**
14291 * @note Locks this object for reading.
14292 */
14293HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14294{
14295 LogFlowThisFunc(("\n"));
14296
14297 AutoCaller autoCaller(this);
14298 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14299
14300 ComPtr<IInternalSessionControl> directControl;
14301 {
14302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14303 if (mData->mSession.mLockType == LockType_VM)
14304 directControl = mData->mSession.mDirectControl;
14305 }
14306
14307 /* ignore notifications sent after #OnSessionEnd() is called */
14308 if (!directControl)
14309 return S_OK;
14310
14311 return directControl->OnVRDEServerChange(aRestart);
14312}
14313
14314/**
14315 * @note Locks this object for reading.
14316 */
14317HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14318{
14319 LogFlowThisFunc(("\n"));
14320
14321 AutoCaller autoCaller(this);
14322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14323
14324 ComPtr<IInternalSessionControl> directControl;
14325 {
14326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14327 if (mData->mSession.mLockType == LockType_VM)
14328 directControl = mData->mSession.mDirectControl;
14329 }
14330
14331 /* ignore notifications sent after #OnSessionEnd() is called */
14332 if (!directControl)
14333 return S_OK;
14334
14335 return directControl->OnRecordingChange(aEnable);
14336}
14337
14338/**
14339 * @note Locks this object for reading.
14340 */
14341HRESULT SessionMachine::i_onUSBControllerChange()
14342{
14343 LogFlowThisFunc(("\n"));
14344
14345 AutoCaller autoCaller(this);
14346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14347
14348 ComPtr<IInternalSessionControl> directControl;
14349 {
14350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14351 if (mData->mSession.mLockType == LockType_VM)
14352 directControl = mData->mSession.mDirectControl;
14353 }
14354
14355 /* ignore notifications sent after #OnSessionEnd() is called */
14356 if (!directControl)
14357 return S_OK;
14358
14359 return directControl->OnUSBControllerChange();
14360}
14361
14362/**
14363 * @note Locks this object for reading.
14364 */
14365HRESULT SessionMachine::i_onSharedFolderChange()
14366{
14367 LogFlowThisFunc(("\n"));
14368
14369 AutoCaller autoCaller(this);
14370 AssertComRCReturnRC(autoCaller.rc());
14371
14372 ComPtr<IInternalSessionControl> directControl;
14373 {
14374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14375 if (mData->mSession.mLockType == LockType_VM)
14376 directControl = mData->mSession.mDirectControl;
14377 }
14378
14379 /* ignore notifications sent after #OnSessionEnd() is called */
14380 if (!directControl)
14381 return S_OK;
14382
14383 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14384}
14385
14386/**
14387 * @note Locks this object for reading.
14388 */
14389HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14390{
14391 LogFlowThisFunc(("\n"));
14392
14393 AutoCaller autoCaller(this);
14394 AssertComRCReturnRC(autoCaller.rc());
14395
14396 ComPtr<IInternalSessionControl> directControl;
14397 {
14398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14399 if (mData->mSession.mLockType == LockType_VM)
14400 directControl = mData->mSession.mDirectControl;
14401 }
14402
14403 /* ignore notifications sent after #OnSessionEnd() is called */
14404 if (!directControl)
14405 return S_OK;
14406
14407 return directControl->OnClipboardModeChange(aClipboardMode);
14408}
14409
14410/**
14411 * @note Locks this object for reading.
14412 */
14413HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14414{
14415 LogFlowThisFunc(("\n"));
14416
14417 AutoCaller autoCaller(this);
14418 AssertComRCReturnRC(autoCaller.rc());
14419
14420 ComPtr<IInternalSessionControl> directControl;
14421 {
14422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14423 if (mData->mSession.mLockType == LockType_VM)
14424 directControl = mData->mSession.mDirectControl;
14425 }
14426
14427 /* ignore notifications sent after #OnSessionEnd() is called */
14428 if (!directControl)
14429 return S_OK;
14430
14431 return directControl->OnClipboardFileTransferModeChange(aEnable);
14432}
14433
14434/**
14435 * @note Locks this object for reading.
14436 */
14437HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14438{
14439 LogFlowThisFunc(("\n"));
14440
14441 AutoCaller autoCaller(this);
14442 AssertComRCReturnRC(autoCaller.rc());
14443
14444 ComPtr<IInternalSessionControl> directControl;
14445 {
14446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14447 if (mData->mSession.mLockType == LockType_VM)
14448 directControl = mData->mSession.mDirectControl;
14449 }
14450
14451 /* ignore notifications sent after #OnSessionEnd() is called */
14452 if (!directControl)
14453 return S_OK;
14454
14455 return directControl->OnDnDModeChange(aDnDMode);
14456}
14457
14458/**
14459 * @note Locks this object for reading.
14460 */
14461HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14462{
14463 LogFlowThisFunc(("\n"));
14464
14465 AutoCaller autoCaller(this);
14466 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14467
14468 ComPtr<IInternalSessionControl> directControl;
14469 {
14470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14471 if (mData->mSession.mLockType == LockType_VM)
14472 directControl = mData->mSession.mDirectControl;
14473 }
14474
14475 /* ignore notifications sent after #OnSessionEnd() is called */
14476 if (!directControl)
14477 return S_OK;
14478
14479 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14480}
14481
14482/**
14483 * @note Locks this object for reading.
14484 */
14485HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14486{
14487 LogFlowThisFunc(("\n"));
14488
14489 AutoCaller autoCaller(this);
14490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14491
14492 ComPtr<IInternalSessionControl> directControl;
14493 {
14494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14495 if (mData->mSession.mLockType == LockType_VM)
14496 directControl = mData->mSession.mDirectControl;
14497 }
14498
14499 /* ignore notifications sent after #OnSessionEnd() is called */
14500 if (!directControl)
14501 return S_OK;
14502
14503 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14504}
14505
14506/**
14507 * Returns @c true if this machine's USB controller reports it has a matching
14508 * filter for the given USB device and @c false otherwise.
14509 *
14510 * @note locks this object for reading.
14511 */
14512bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14513{
14514 AutoCaller autoCaller(this);
14515 /* silently return if not ready -- this method may be called after the
14516 * direct machine session has been called */
14517 if (!autoCaller.isOk())
14518 return false;
14519
14520#ifdef VBOX_WITH_USB
14521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14522
14523 switch (mData->mMachineState)
14524 {
14525 case MachineState_Starting:
14526 case MachineState_Restoring:
14527 case MachineState_TeleportingIn:
14528 case MachineState_Paused:
14529 case MachineState_Running:
14530 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14531 * elsewhere... */
14532 alock.release();
14533 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14534 default: break;
14535 }
14536#else
14537 NOREF(aDevice);
14538 NOREF(aMaskedIfs);
14539#endif
14540 return false;
14541}
14542
14543/**
14544 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14545 */
14546HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14547 IVirtualBoxErrorInfo *aError,
14548 ULONG aMaskedIfs,
14549 const com::Utf8Str &aCaptureFilename)
14550{
14551 LogFlowThisFunc(("\n"));
14552
14553 AutoCaller autoCaller(this);
14554
14555 /* This notification may happen after the machine object has been
14556 * uninitialized (the session was closed), so don't assert. */
14557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14558
14559 ComPtr<IInternalSessionControl> directControl;
14560 {
14561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14562 if (mData->mSession.mLockType == LockType_VM)
14563 directControl = mData->mSession.mDirectControl;
14564 }
14565
14566 /* fail on notifications sent after #OnSessionEnd() is called, it is
14567 * expected by the caller */
14568 if (!directControl)
14569 return E_FAIL;
14570
14571 /* No locks should be held at this point. */
14572 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14573 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14574
14575 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14576}
14577
14578/**
14579 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14580 */
14581HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14582 IVirtualBoxErrorInfo *aError)
14583{
14584 LogFlowThisFunc(("\n"));
14585
14586 AutoCaller autoCaller(this);
14587
14588 /* This notification may happen after the machine object has been
14589 * uninitialized (the session was closed), so don't assert. */
14590 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14591
14592 ComPtr<IInternalSessionControl> directControl;
14593 {
14594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14595 if (mData->mSession.mLockType == LockType_VM)
14596 directControl = mData->mSession.mDirectControl;
14597 }
14598
14599 /* fail on notifications sent after #OnSessionEnd() is called, it is
14600 * expected by the caller */
14601 if (!directControl)
14602 return E_FAIL;
14603
14604 /* No locks should be held at this point. */
14605 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14606 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14607
14608 return directControl->OnUSBDeviceDetach(aId, aError);
14609}
14610
14611// protected methods
14612/////////////////////////////////////////////////////////////////////////////
14613
14614/**
14615 * Deletes the given file if it is no longer in use by either the current machine state
14616 * (if the machine is "saved") or any of the machine's snapshots.
14617 *
14618 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14619 * but is different for each SnapshotMachine. When calling this, the order of calling this
14620 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14621 * is therefore critical. I know, it's all rather messy.
14622 *
14623 * @param strStateFile
14624 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14625 * the test for whether the saved state file is in use.
14626 */
14627void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14628 Snapshot *pSnapshotToIgnore)
14629{
14630 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14631 if ( (strStateFile.isNotEmpty())
14632 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14633 )
14634 // ... and it must also not be shared with other snapshots
14635 if ( !mData->mFirstSnapshot
14636 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14637 // this checks the SnapshotMachine's state file paths
14638 )
14639 RTFileDelete(strStateFile.c_str());
14640}
14641
14642/**
14643 * Locks the attached media.
14644 *
14645 * All attached hard disks are locked for writing and DVD/floppy are locked for
14646 * reading. Parents of attached hard disks (if any) are locked for reading.
14647 *
14648 * This method also performs accessibility check of all media it locks: if some
14649 * media is inaccessible, the method will return a failure and a bunch of
14650 * extended error info objects per each inaccessible medium.
14651 *
14652 * Note that this method is atomic: if it returns a success, all media are
14653 * locked as described above; on failure no media is locked at all (all
14654 * succeeded individual locks will be undone).
14655 *
14656 * The caller is responsible for doing the necessary state sanity checks.
14657 *
14658 * The locks made by this method must be undone by calling #unlockMedia() when
14659 * no more needed.
14660 */
14661HRESULT SessionMachine::i_lockMedia()
14662{
14663 AutoCaller autoCaller(this);
14664 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14665
14666 AutoMultiWriteLock2 alock(this->lockHandle(),
14667 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14668
14669 /* bail out if trying to lock things with already set up locking */
14670 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14671
14672 MultiResult mrc(S_OK);
14673
14674 /* Collect locking information for all medium objects attached to the VM. */
14675 for (MediumAttachmentList::const_iterator
14676 it = mMediumAttachments->begin();
14677 it != mMediumAttachments->end();
14678 ++it)
14679 {
14680 MediumAttachment *pAtt = *it;
14681 DeviceType_T devType = pAtt->i_getType();
14682 Medium *pMedium = pAtt->i_getMedium();
14683
14684 MediumLockList *pMediumLockList(new MediumLockList());
14685 // There can be attachments without a medium (floppy/dvd), and thus
14686 // it's impossible to create a medium lock list. It still makes sense
14687 // to have the empty medium lock list in the map in case a medium is
14688 // attached later.
14689 if (pMedium != NULL)
14690 {
14691 MediumType_T mediumType = pMedium->i_getType();
14692 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14693 || mediumType == MediumType_Shareable;
14694 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14695
14696 alock.release();
14697 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14698 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14699 false /* fMediumLockWriteAll */,
14700 NULL,
14701 *pMediumLockList);
14702 alock.acquire();
14703 if (FAILED(mrc))
14704 {
14705 delete pMediumLockList;
14706 mData->mSession.mLockedMedia.Clear();
14707 break;
14708 }
14709 }
14710
14711 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14712 if (FAILED(rc))
14713 {
14714 mData->mSession.mLockedMedia.Clear();
14715 mrc = setError(rc,
14716 tr("Collecting locking information for all attached media failed"));
14717 break;
14718 }
14719 }
14720
14721 if (SUCCEEDED(mrc))
14722 {
14723 /* Now lock all media. If this fails, nothing is locked. */
14724 alock.release();
14725 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14726 alock.acquire();
14727 if (FAILED(rc))
14728 {
14729 mrc = setError(rc,
14730 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14731 }
14732 }
14733
14734 return mrc;
14735}
14736
14737/**
14738 * Undoes the locks made by by #lockMedia().
14739 */
14740HRESULT SessionMachine::i_unlockMedia()
14741{
14742 AutoCaller autoCaller(this);
14743 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14744
14745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14746
14747 /* we may be holding important error info on the current thread;
14748 * preserve it */
14749 ErrorInfoKeeper eik;
14750
14751 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14752 AssertComRC(rc);
14753 return rc;
14754}
14755
14756/**
14757 * Helper to change the machine state (reimplementation).
14758 *
14759 * @note Locks this object for writing.
14760 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14761 * it can cause crashes in random places due to unexpectedly committing
14762 * the current settings. The caller is responsible for that. The call
14763 * to saveStateSettings is fine, because this method does not commit.
14764 */
14765HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14766{
14767 LogFlowThisFuncEnter();
14768
14769 AutoCaller autoCaller(this);
14770 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14771
14772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14773
14774 MachineState_T oldMachineState = mData->mMachineState;
14775
14776 AssertMsgReturn(oldMachineState != aMachineState,
14777 ("oldMachineState=%s, aMachineState=%s\n",
14778 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14779 E_FAIL);
14780
14781 HRESULT rc = S_OK;
14782
14783 int stsFlags = 0;
14784 bool deleteSavedState = false;
14785
14786 /* detect some state transitions */
14787
14788 if ( ( ( oldMachineState == MachineState_Saved
14789 || oldMachineState == MachineState_AbortedSaved
14790 )
14791 && aMachineState == MachineState_Restoring
14792 )
14793 || ( ( oldMachineState == MachineState_PoweredOff
14794 || oldMachineState == MachineState_Teleported
14795 || oldMachineState == MachineState_Aborted
14796 )
14797 && ( aMachineState == MachineState_TeleportingIn
14798 || aMachineState == MachineState_Starting
14799 )
14800 )
14801 )
14802 {
14803 /* The EMT thread is about to start */
14804
14805 /* Nothing to do here for now... */
14806
14807 /// @todo NEWMEDIA don't let mDVDDrive and other children
14808 /// change anything when in the Starting/Restoring state
14809 }
14810 else if ( ( oldMachineState == MachineState_Running
14811 || oldMachineState == MachineState_Paused
14812 || oldMachineState == MachineState_Teleporting
14813 || oldMachineState == MachineState_OnlineSnapshotting
14814 || oldMachineState == MachineState_LiveSnapshotting
14815 || oldMachineState == MachineState_Stuck
14816 || oldMachineState == MachineState_Starting
14817 || oldMachineState == MachineState_Stopping
14818 || oldMachineState == MachineState_Saving
14819 || oldMachineState == MachineState_Restoring
14820 || oldMachineState == MachineState_TeleportingPausedVM
14821 || oldMachineState == MachineState_TeleportingIn
14822 )
14823 && ( aMachineState == MachineState_PoweredOff
14824 || aMachineState == MachineState_Saved
14825 || aMachineState == MachineState_Teleported
14826 || aMachineState == MachineState_Aborted
14827 || aMachineState == MachineState_AbortedSaved
14828 )
14829 )
14830 {
14831 /* The EMT thread has just stopped, unlock attached media. Note that as
14832 * opposed to locking that is done from Console, we do unlocking here
14833 * because the VM process may have aborted before having a chance to
14834 * properly unlock all media it locked. */
14835
14836 unlockMedia();
14837 }
14838
14839 if (oldMachineState == MachineState_Restoring)
14840 {
14841 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14842 {
14843 /*
14844 * delete the saved state file once the machine has finished
14845 * restoring from it (note that Console sets the state from
14846 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14847 * to give the user an ability to fix an error and retry --
14848 * we keep the saved state file in this case)
14849 */
14850 deleteSavedState = true;
14851 }
14852 }
14853 else if ( oldMachineState == MachineState_Saved
14854 && ( aMachineState == MachineState_PoweredOff
14855 || aMachineState == MachineState_Teleported
14856 )
14857 )
14858 {
14859 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14860 deleteSavedState = true;
14861 mData->mCurrentStateModified = TRUE;
14862 stsFlags |= SaveSTS_CurStateModified;
14863 }
14864 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14865 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14866
14867 if ( aMachineState == MachineState_Starting
14868 || aMachineState == MachineState_Restoring
14869 || aMachineState == MachineState_TeleportingIn
14870 )
14871 {
14872 /* set the current state modified flag to indicate that the current
14873 * state is no more identical to the state in the
14874 * current snapshot */
14875 if (!mData->mCurrentSnapshot.isNull())
14876 {
14877 mData->mCurrentStateModified = TRUE;
14878 stsFlags |= SaveSTS_CurStateModified;
14879 }
14880 }
14881
14882 if (deleteSavedState)
14883 {
14884 if (mRemoveSavedState)
14885 {
14886 Assert(!mSSData->strStateFilePath.isEmpty());
14887
14888 // it is safe to delete the saved state file if ...
14889 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14890 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14891 // ... none of the snapshots share the saved state file
14892 )
14893 RTFileDelete(mSSData->strStateFilePath.c_str());
14894 }
14895
14896 mSSData->strStateFilePath.setNull();
14897 stsFlags |= SaveSTS_StateFilePath;
14898 }
14899
14900 /* redirect to the underlying peer machine */
14901 mPeer->i_setMachineState(aMachineState);
14902
14903 if ( oldMachineState != MachineState_RestoringSnapshot
14904 && ( aMachineState == MachineState_PoweredOff
14905 || aMachineState == MachineState_Teleported
14906 || aMachineState == MachineState_Aborted
14907 || aMachineState == MachineState_AbortedSaved
14908 || aMachineState == MachineState_Saved))
14909 {
14910 /* the machine has stopped execution
14911 * (or the saved state file was adopted) */
14912 stsFlags |= SaveSTS_StateTimeStamp;
14913 }
14914
14915 if ( ( oldMachineState == MachineState_PoweredOff
14916 || oldMachineState == MachineState_Aborted
14917 || oldMachineState == MachineState_Teleported
14918 )
14919 && aMachineState == MachineState_Saved)
14920 {
14921 /* the saved state file was adopted */
14922 Assert(!mSSData->strStateFilePath.isEmpty());
14923 stsFlags |= SaveSTS_StateFilePath;
14924 }
14925
14926#ifdef VBOX_WITH_GUEST_PROPS
14927 if ( aMachineState == MachineState_PoweredOff
14928 || aMachineState == MachineState_Aborted
14929 || aMachineState == MachineState_Teleported)
14930 {
14931 /* Make sure any transient guest properties get removed from the
14932 * property store on shutdown. */
14933 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14934
14935 /* remove it from the settings representation */
14936 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14937 for (settings::GuestPropertiesList::iterator
14938 it = llGuestProperties.begin();
14939 it != llGuestProperties.end();
14940 /*nothing*/)
14941 {
14942 const settings::GuestProperty &prop = *it;
14943 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14944 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14945 {
14946 it = llGuestProperties.erase(it);
14947 fNeedsSaving = true;
14948 }
14949 else
14950 {
14951 ++it;
14952 }
14953 }
14954
14955 /* Additionally remove it from the HWData representation. Required to
14956 * keep everything in sync, as this is what the API keeps using. */
14957 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14958 for (HWData::GuestPropertyMap::iterator
14959 it = llHWGuestProperties.begin();
14960 it != llHWGuestProperties.end();
14961 /*nothing*/)
14962 {
14963 uint32_t fFlags = it->second.mFlags;
14964 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14965 {
14966 /* iterator where we need to continue after the erase call
14967 * (C++03 is a fact still, and it doesn't return the iterator
14968 * which would allow continuing) */
14969 HWData::GuestPropertyMap::iterator it2 = it;
14970 ++it2;
14971 llHWGuestProperties.erase(it);
14972 it = it2;
14973 fNeedsSaving = true;
14974 }
14975 else
14976 {
14977 ++it;
14978 }
14979 }
14980
14981 if (fNeedsSaving)
14982 {
14983 mData->mCurrentStateModified = TRUE;
14984 stsFlags |= SaveSTS_CurStateModified;
14985 }
14986 }
14987#endif /* VBOX_WITH_GUEST_PROPS */
14988
14989 rc = i_saveStateSettings(stsFlags);
14990
14991 if ( ( oldMachineState != MachineState_PoweredOff
14992 && oldMachineState != MachineState_Aborted
14993 && oldMachineState != MachineState_Teleported
14994 )
14995 && ( aMachineState == MachineState_PoweredOff
14996 || aMachineState == MachineState_Aborted
14997 || aMachineState == MachineState_Teleported
14998 )
14999 )
15000 {
15001 /* we've been shut down for any reason */
15002 /* no special action so far */
15003 }
15004
15005 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15006 LogFlowThisFuncLeave();
15007 return rc;
15008}
15009
15010/**
15011 * Sends the current machine state value to the VM process.
15012 *
15013 * @note Locks this object for reading, then calls a client process.
15014 */
15015HRESULT SessionMachine::i_updateMachineStateOnClient()
15016{
15017 AutoCaller autoCaller(this);
15018 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15019
15020 ComPtr<IInternalSessionControl> directControl;
15021 {
15022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15023 AssertReturn(!!mData, E_FAIL);
15024 if (mData->mSession.mLockType == LockType_VM)
15025 directControl = mData->mSession.mDirectControl;
15026
15027 /* directControl may be already set to NULL here in #OnSessionEnd()
15028 * called too early by the direct session process while there is still
15029 * some operation (like deleting the snapshot) in progress. The client
15030 * process in this case is waiting inside Session::close() for the
15031 * "end session" process object to complete, while #uninit() called by
15032 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15033 * operation to complete. For now, we accept this inconsistent behavior
15034 * and simply do nothing here. */
15035
15036 if (mData->mSession.mState == SessionState_Unlocking)
15037 return S_OK;
15038 }
15039
15040 /* ignore notifications sent after #OnSessionEnd() is called */
15041 if (!directControl)
15042 return S_OK;
15043
15044 return directControl->UpdateMachineState(mData->mMachineState);
15045}
15046
15047
15048/*static*/
15049HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15050{
15051 va_list args;
15052 va_start(args, pcszMsg);
15053 HRESULT rc = setErrorInternalV(aResultCode,
15054 getStaticClassIID(),
15055 getStaticComponentName(),
15056 pcszMsg, args,
15057 false /* aWarning */,
15058 true /* aLogIt */);
15059 va_end(args);
15060 return rc;
15061}
15062
15063
15064HRESULT Machine::updateState(MachineState_T aState)
15065{
15066 NOREF(aState);
15067 ReturnComNotImplemented();
15068}
15069
15070HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15071{
15072 NOREF(aProgress);
15073 ReturnComNotImplemented();
15074}
15075
15076HRESULT Machine::endPowerUp(LONG aResult)
15077{
15078 NOREF(aResult);
15079 ReturnComNotImplemented();
15080}
15081
15082HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15083{
15084 NOREF(aProgress);
15085 ReturnComNotImplemented();
15086}
15087
15088HRESULT Machine::endPoweringDown(LONG aResult,
15089 const com::Utf8Str &aErrMsg)
15090{
15091 NOREF(aResult);
15092 NOREF(aErrMsg);
15093 ReturnComNotImplemented();
15094}
15095
15096HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15097 BOOL *aMatched,
15098 ULONG *aMaskedInterfaces)
15099{
15100 NOREF(aDevice);
15101 NOREF(aMatched);
15102 NOREF(aMaskedInterfaces);
15103 ReturnComNotImplemented();
15104
15105}
15106
15107HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15108{
15109 NOREF(aId); NOREF(aCaptureFilename);
15110 ReturnComNotImplemented();
15111}
15112
15113HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15114 BOOL aDone)
15115{
15116 NOREF(aId);
15117 NOREF(aDone);
15118 ReturnComNotImplemented();
15119}
15120
15121HRESULT Machine::autoCaptureUSBDevices()
15122{
15123 ReturnComNotImplemented();
15124}
15125
15126HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15127{
15128 NOREF(aDone);
15129 ReturnComNotImplemented();
15130}
15131
15132HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15133 ComPtr<IProgress> &aProgress)
15134{
15135 NOREF(aSession);
15136 NOREF(aProgress);
15137 ReturnComNotImplemented();
15138}
15139
15140HRESULT Machine::finishOnlineMergeMedium()
15141{
15142 ReturnComNotImplemented();
15143}
15144
15145HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15146 std::vector<com::Utf8Str> &aValues,
15147 std::vector<LONG64> &aTimestamps,
15148 std::vector<com::Utf8Str> &aFlags)
15149{
15150 NOREF(aNames);
15151 NOREF(aValues);
15152 NOREF(aTimestamps);
15153 NOREF(aFlags);
15154 ReturnComNotImplemented();
15155}
15156
15157HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15158 const com::Utf8Str &aValue,
15159 LONG64 aTimestamp,
15160 const com::Utf8Str &aFlags,
15161 BOOL fWasDeleted)
15162{
15163 NOREF(aName);
15164 NOREF(aValue);
15165 NOREF(aTimestamp);
15166 NOREF(aFlags);
15167 NOREF(fWasDeleted);
15168 ReturnComNotImplemented();
15169}
15170
15171HRESULT Machine::lockMedia()
15172{
15173 ReturnComNotImplemented();
15174}
15175
15176HRESULT Machine::unlockMedia()
15177{
15178 ReturnComNotImplemented();
15179}
15180
15181HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15182 ComPtr<IMediumAttachment> &aNewAttachment)
15183{
15184 NOREF(aAttachment);
15185 NOREF(aNewAttachment);
15186 ReturnComNotImplemented();
15187}
15188
15189HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15190 ULONG aCpuUser,
15191 ULONG aCpuKernel,
15192 ULONG aCpuIdle,
15193 ULONG aMemTotal,
15194 ULONG aMemFree,
15195 ULONG aMemBalloon,
15196 ULONG aMemShared,
15197 ULONG aMemCache,
15198 ULONG aPagedTotal,
15199 ULONG aMemAllocTotal,
15200 ULONG aMemFreeTotal,
15201 ULONG aMemBalloonTotal,
15202 ULONG aMemSharedTotal,
15203 ULONG aVmNetRx,
15204 ULONG aVmNetTx)
15205{
15206 NOREF(aValidStats);
15207 NOREF(aCpuUser);
15208 NOREF(aCpuKernel);
15209 NOREF(aCpuIdle);
15210 NOREF(aMemTotal);
15211 NOREF(aMemFree);
15212 NOREF(aMemBalloon);
15213 NOREF(aMemShared);
15214 NOREF(aMemCache);
15215 NOREF(aPagedTotal);
15216 NOREF(aMemAllocTotal);
15217 NOREF(aMemFreeTotal);
15218 NOREF(aMemBalloonTotal);
15219 NOREF(aMemSharedTotal);
15220 NOREF(aVmNetRx);
15221 NOREF(aVmNetTx);
15222 ReturnComNotImplemented();
15223}
15224
15225HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15226 com::Utf8Str &aResult)
15227{
15228 NOREF(aAuthParams);
15229 NOREF(aResult);
15230 ReturnComNotImplemented();
15231}
15232
15233com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15234{
15235 com::Utf8Str strControllerName = "Unknown";
15236 switch (aBusType)
15237 {
15238 case StorageBus_IDE:
15239 {
15240 strControllerName = "IDE";
15241 break;
15242 }
15243 case StorageBus_SATA:
15244 {
15245 strControllerName = "SATA";
15246 break;
15247 }
15248 case StorageBus_SCSI:
15249 {
15250 strControllerName = "SCSI";
15251 break;
15252 }
15253 case StorageBus_Floppy:
15254 {
15255 strControllerName = "Floppy";
15256 break;
15257 }
15258 case StorageBus_SAS:
15259 {
15260 strControllerName = "SAS";
15261 break;
15262 }
15263 case StorageBus_USB:
15264 {
15265 strControllerName = "USB";
15266 break;
15267 }
15268 default:
15269 break;
15270 }
15271 return strControllerName;
15272}
15273
15274HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15275{
15276 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15277
15278 AutoCaller autoCaller(this);
15279 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15280
15281 HRESULT rc = S_OK;
15282
15283 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15284 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15285 rc = getUSBDeviceFilters(usbDeviceFilters);
15286 if (FAILED(rc)) return rc;
15287
15288 NOREF(aFlags);
15289 com::Utf8Str osTypeId;
15290 ComObjPtr<GuestOSType> osType = NULL;
15291
15292 /* Get the guest os type as a string from the VB. */
15293 rc = getOSTypeId(osTypeId);
15294 if (FAILED(rc)) return rc;
15295
15296 /* Get the os type obj that coresponds, can be used to get
15297 * the defaults for this guest OS. */
15298 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15299 if (FAILED(rc)) return rc;
15300
15301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15302
15303 /* Let the OS type select 64-bit ness. */
15304 mHWData->mLongMode = osType->i_is64Bit()
15305 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15306
15307 /* Let the OS type enable the X2APIC */
15308 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15309
15310 /* This one covers IOAPICEnabled. */
15311 mBIOSSettings->i_applyDefaults(osType);
15312
15313 /* Initialize default record settings. */
15314 mRecordingSettings->i_applyDefaults();
15315
15316 /* Initialize default BIOS settings here */
15317 /* Hardware virtualization must be ON by default */
15318 mHWData->mAPIC = true;
15319 mHWData->mHWVirtExEnabled = true;
15320
15321 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15322 if (FAILED(rc)) return rc;
15323
15324 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15325 if (FAILED(rc)) return rc;
15326
15327 /* Graphics stuff. */
15328 GraphicsControllerType_T graphicsController;
15329 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15330 if (FAILED(rc)) return rc;
15331
15332 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15333 if (FAILED(rc)) return rc;
15334
15335 ULONG vramSize;
15336 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15337 if (FAILED(rc)) return rc;
15338
15339 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15340 if (FAILED(rc)) return rc;
15341
15342 BOOL fAccelerate2DVideoEnabled;
15343 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15344 if (FAILED(rc)) return rc;
15345
15346 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15347 if (FAILED(rc)) return rc;
15348
15349 BOOL fAccelerate3DEnabled;
15350 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15351 if (FAILED(rc)) return rc;
15352
15353 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15354 if (FAILED(rc)) return rc;
15355
15356 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15357 if (FAILED(rc)) return rc;
15358
15359 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15360 if (FAILED(rc)) return rc;
15361
15362 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15363 if (FAILED(rc)) return rc;
15364
15365 BOOL mRTCUseUTC;
15366 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15367 if (FAILED(rc)) return rc;
15368
15369 setRTCUseUTC(mRTCUseUTC);
15370 if (FAILED(rc)) return rc;
15371
15372 /* the setter does more than just the assignment, so use it */
15373 ChipsetType_T enmChipsetType;
15374 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15375 if (FAILED(rc)) return rc;
15376
15377 rc = COMSETTER(ChipsetType)(enmChipsetType);
15378 if (FAILED(rc)) return rc;
15379
15380 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15381 if (FAILED(rc)) return rc;
15382
15383 /* Apply IOMMU defaults. */
15384 IommuType_T enmIommuType;
15385 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15386 if (FAILED(rc)) return rc;
15387
15388 rc = COMSETTER(IommuType)(enmIommuType);
15389 if (FAILED(rc)) return rc;
15390
15391 /* Apply network adapters defaults */
15392 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15393 mNetworkAdapters[slot]->i_applyDefaults(osType);
15394
15395 /* Apply serial port defaults */
15396 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15397 mSerialPorts[slot]->i_applyDefaults(osType);
15398
15399 /* Apply parallel port defaults - not OS dependent*/
15400 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15401 mParallelPorts[slot]->i_applyDefaults();
15402
15403 /* This one covers the TPM type. */
15404 mTrustedPlatformModule->i_applyDefaults(osType);
15405
15406 /* This one covers secure boot. */
15407 rc = mNvramStore->i_applyDefaults(osType);
15408 if (FAILED(rc)) return rc;
15409
15410 /* Audio stuff. */
15411 AudioControllerType_T audioController;
15412 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15413 if (FAILED(rc)) return rc;
15414
15415 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15416 if (FAILED(rc)) return rc;
15417
15418 AudioCodecType_T audioCodec;
15419 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15420 if (FAILED(rc)) return rc;
15421
15422 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15423 if (FAILED(rc)) return rc;
15424
15425 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15426 if (FAILED(rc)) return rc;
15427
15428 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15429 if (FAILED(rc)) return rc;
15430
15431 /* Storage Controllers */
15432 StorageControllerType_T hdStorageControllerType;
15433 StorageBus_T hdStorageBusType;
15434 StorageControllerType_T dvdStorageControllerType;
15435 StorageBus_T dvdStorageBusType;
15436 BOOL recommendedFloppy;
15437 ComPtr<IStorageController> floppyController;
15438 ComPtr<IStorageController> hdController;
15439 ComPtr<IStorageController> dvdController;
15440 Utf8Str strFloppyName, strDVDName, strHDName;
15441
15442 /* GUI auto generates controller names using bus type. Do the same*/
15443 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15444
15445 /* Floppy recommended? add one. */
15446 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15447 if (FAILED(rc)) return rc;
15448 if (recommendedFloppy)
15449 {
15450 rc = addStorageController(strFloppyName,
15451 StorageBus_Floppy,
15452 floppyController);
15453 if (FAILED(rc)) return rc;
15454 }
15455
15456 /* Setup one DVD storage controller. */
15457 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15458 if (FAILED(rc)) return rc;
15459
15460 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15461 if (FAILED(rc)) return rc;
15462
15463 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15464
15465 rc = addStorageController(strDVDName,
15466 dvdStorageBusType,
15467 dvdController);
15468 if (FAILED(rc)) return rc;
15469
15470 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15471 if (FAILED(rc)) return rc;
15472
15473 /* Setup one HDD storage controller. */
15474 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15475 if (FAILED(rc)) return rc;
15476
15477 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15478 if (FAILED(rc)) return rc;
15479
15480 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15481
15482 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15483 {
15484 rc = addStorageController(strHDName,
15485 hdStorageBusType,
15486 hdController);
15487 if (FAILED(rc)) return rc;
15488
15489 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15490 if (FAILED(rc)) return rc;
15491 }
15492 else
15493 {
15494 /* The HD controller is the same as DVD: */
15495 hdController = dvdController;
15496 }
15497
15498 /* Limit the AHCI port count if it's used because windows has trouble with
15499 * too many ports and other guest (OS X in particular) may take extra long
15500 * boot: */
15501
15502 // pParent = static_cast<Medium*>(aP)
15503 IStorageController *temp = hdController;
15504 ComObjPtr<StorageController> storageController;
15505 storageController = static_cast<StorageController *>(temp);
15506
15507 // tempHDController = aHDController;
15508 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15509 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15510 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15511 storageController->COMSETTER(PortCount)(1);
15512
15513 /* USB stuff */
15514
15515 bool ohciEnabled = false;
15516
15517 ComPtr<IUSBController> usbController;
15518 BOOL recommendedUSB3;
15519 BOOL recommendedUSB;
15520 BOOL usbProxyAvailable;
15521
15522 getUSBProxyAvailable(&usbProxyAvailable);
15523 if (FAILED(rc)) return rc;
15524
15525 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15526 if (FAILED(rc)) return rc;
15527 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15528 if (FAILED(rc)) return rc;
15529
15530 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15531 {
15532#ifdef VBOX_WITH_EXTPACK
15533 /* USB 3.0 is only available if the proper ExtPack is installed. */
15534 ExtPackManager *aManager = mParent->i_getExtPackManager();
15535 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15536 {
15537 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15538 if (FAILED(rc)) return rc;
15539
15540 /* xHci includes OHCI */
15541 ohciEnabled = true;
15542 }
15543#endif
15544 }
15545 if ( !ohciEnabled
15546 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15547 {
15548 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15549 if (FAILED(rc)) return rc;
15550 ohciEnabled = true;
15551
15552#ifdef VBOX_WITH_EXTPACK
15553 /* USB 2.0 is only available if the proper ExtPack is installed.
15554 * Note. Configuring EHCI here and providing messages about
15555 * the missing extpack isn't exactly clean, but it is a
15556 * necessary evil to patch over legacy compatability issues
15557 * introduced by the new distribution model. */
15558 ExtPackManager *manager = mParent->i_getExtPackManager();
15559 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15560 {
15561 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15562 if (FAILED(rc)) return rc;
15563 }
15564#endif
15565 }
15566
15567 /* Set recommended human interface device types: */
15568 BOOL recommendedUSBHID;
15569 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15570 if (FAILED(rc)) return rc;
15571
15572 if (recommendedUSBHID)
15573 {
15574 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15575 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15576 if (!ohciEnabled && !usbDeviceFilters.isNull())
15577 {
15578 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15579 if (FAILED(rc)) return rc;
15580 }
15581 }
15582
15583 BOOL recommendedUSBTablet;
15584 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15585 if (FAILED(rc)) return rc;
15586
15587 if (recommendedUSBTablet)
15588 {
15589 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15590 if (!ohciEnabled && !usbDeviceFilters.isNull())
15591 {
15592 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15593 if (FAILED(rc)) return rc;
15594 }
15595 }
15596
15597 /* Enable the VMMDev testing feature for bootsector VMs: */
15598 if (osTypeId == "VBoxBS_64")
15599 {
15600 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15601 if (FAILED(rc))
15602 return rc;
15603 }
15604
15605 return S_OK;
15606}
15607
15608HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
15609 const com::Utf8Str &aCipher,
15610 const com::Utf8Str &aNewPassword,
15611 const com::Utf8Str &aNewPasswordId,
15612 BOOL aForce,
15613 ComPtr<IProgress> &aProgress)
15614{
15615 LogFlowFuncEnter();
15616
15617#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15618 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15619 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15620#else
15621 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15622 /** @todo */
15623 return E_NOTIMPL;
15624#endif
15625}
15626
15627HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
15628 com::Utf8Str &aPasswordId)
15629{
15630#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15631 RT_NOREF(aCipher, aPasswordId);
15632 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15633#else
15634 RT_NOREF(aCipher, aPasswordId);
15635 /** @todo */
15636 return E_NOTIMPL;
15637#endif
15638}
15639
15640HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
15641{
15642#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15643 RT_NOREF(aPassword);
15644 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15645#else
15646 RT_NOREF(aPassword);
15647 /** @todo */
15648 return E_NOTIMPL;
15649#endif
15650}
15651
15652HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
15653 const com::Utf8Str &aPassword)
15654{
15655#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15656 RT_NOREF(aId, aPassword);
15657 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15658#else
15659 RT_NOREF(aId, aPassword);
15660 /** @todo */
15661 return E_NOTIMPL;
15662#endif
15663}
15664
15665HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
15666 const std::vector<com::Utf8Str> &aPasswords)
15667{
15668#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15669 RT_NOREF(aIds, aPasswords);
15670 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15671#else
15672 RT_NOREF(aIds, aPasswords);
15673 /** @todo */
15674 return E_NOTIMPL;
15675#endif
15676}
15677
15678HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
15679{
15680#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15681 RT_NOREF(autoCaller, aId);
15682 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15683#else
15684 RT_NOREF(autoCaller, aId);
15685 /** @todo */
15686 return E_NOTIMPL;
15687#endif
15688}
15689
15690HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
15691{
15692#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15693 RT_NOREF(autoCaller);
15694 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15695#else
15696 RT_NOREF(autoCaller);
15697 /** @todo */
15698 return E_NOTIMPL;
15699#endif
15700}
15701
15702/* This isn't handled entirely by the wrapper generator yet. */
15703#ifdef VBOX_WITH_XPCOM
15704NS_DECL_CLASSINFO(SessionMachine)
15705NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15706
15707NS_DECL_CLASSINFO(SnapshotMachine)
15708NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15709#endif
Note: See TracBrowser for help on using the repository browser.

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