VirtualBox

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

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

Main/Machine: When saving the settings fails when creating a new file, delete the file because it usually does not contain anything (or something invalid). Leaving behind such files in the failure case sabotages retrying, since it will then fail with VERR_ALREADY_EXISTS.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 541.8 KB
Line 
1/* $Id: MachineImpl.cpp 93548 2022-02-02 18:08:28Z 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.virtualbox.org. 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 "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63#include "StringifyEnums.h"
64
65#include <iprt/asm.h>
66#include <iprt/path.h>
67#include <iprt/dir.h>
68#include <iprt/env.h>
69#include <iprt/lockvalidator.h>
70#include <iprt/process.h>
71#include <iprt/cpp/utils.h>
72#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
73#include <iprt/sha.h>
74#include <iprt/string.h>
75#include <iprt/ctype.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/VMMDev.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#ifdef VBOX_WITH_SHARED_CLIPBOARD
92# include <VBox/HostServices/VBoxClipboardSvc.h>
93#endif
94
95#include "VBox/com/MultiResult.h"
96
97#include <algorithm>
98
99#ifdef VBOX_WITH_DTRACE_R3_MAIN
100# include "dtrace/VBoxAPI.h"
101#endif
102
103#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
104# define HOSTSUFF_EXE ".exe"
105#else /* !RT_OS_WINDOWS */
106# define HOSTSUFF_EXE ""
107#endif /* !RT_OS_WINDOWS */
108
109// defines / prototypes
110/////////////////////////////////////////////////////////////////////////////
111
112/////////////////////////////////////////////////////////////////////////////
113// Machine::Data structure
114/////////////////////////////////////////////////////////////////////////////
115
116Machine::Data::Data()
117{
118 mRegistered = FALSE;
119 pMachineConfigFile = NULL;
120 /* Contains hints on what has changed when the user is using the VM (config
121 * changes, running the VM, ...). This is used to decide if a config needs
122 * to be written to disk. */
123 flModifications = 0;
124 /* VM modification usually also trigger setting the current state to
125 * "Modified". Although this is not always the case. An e.g. is the VM
126 * initialization phase or when snapshot related data is changed. The
127 * actually behavior is controlled by the following flag. */
128 m_fAllowStateModification = false;
129 mAccessible = FALSE;
130 /* mUuid is initialized in Machine::init() */
131
132 mMachineState = MachineState_PoweredOff;
133 RTTimeNow(&mLastStateChange);
134
135 mMachineStateDeps = 0;
136 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
137 mMachineStateChangePending = 0;
138
139 mCurrentStateModified = TRUE;
140 mGuestPropertiesModified = FALSE;
141
142 mSession.mPID = NIL_RTPROCESS;
143 mSession.mLockType = LockType_Null;
144 mSession.mState = SessionState_Unlocked;
145}
146
147Machine::Data::~Data()
148{
149 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
150 {
151 RTSemEventMultiDestroy(mMachineStateDepsSem);
152 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
153 }
154 if (pMachineConfigFile)
155 {
156 delete pMachineConfigFile;
157 pMachineConfigFile = NULL;
158 }
159}
160
161/////////////////////////////////////////////////////////////////////////////
162// Machine::HWData structure
163/////////////////////////////////////////////////////////////////////////////
164
165Machine::HWData::HWData()
166{
167 /* default values for a newly created machine */
168 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
169 mMemorySize = 128;
170 mCPUCount = 1;
171 mCPUHotPlugEnabled = false;
172 mMemoryBalloonSize = 0;
173 mPageFusionEnabled = false;
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
177 mHWVirtExVPIDEnabled = true;
178 mHWVirtExUXEnabled = true;
179 mHWVirtExForceEnabled = false;
180 mHWVirtExUseNativeApi = false;
181 mHWVirtExVirtVmsaveVmload = true;
182#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
183 mPAEEnabled = true;
184#else
185 mPAEEnabled = false;
186#endif
187 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
188 mTripleFaultReset = false;
189 mAPIC = true;
190 mX2APIC = false;
191 mIBPBOnVMExit = false;
192 mIBPBOnVMEntry = false;
193 mSpecCtrl = false;
194 mSpecCtrlByHost = false;
195 mL1DFlushOnSched = true;
196 mL1DFlushOnVMEntry = false;
197 mMDSClearOnSched = true;
198 mMDSClearOnVMEntry = false;
199 mNestedHWVirt = false;
200 mHPETEnabled = false;
201 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
202 mCpuIdPortabilityLevel = 0;
203 mCpuProfile = "host";
204
205 /* default boot order: floppy - DVD - HDD */
206 mBootOrder[0] = DeviceType_Floppy;
207 mBootOrder[1] = DeviceType_DVD;
208 mBootOrder[2] = DeviceType_HardDisk;
209 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
210 mBootOrder[i] = DeviceType_Null;
211
212 mClipboardMode = ClipboardMode_Disabled;
213 mClipboardFileTransfersEnabled = FALSE;
214
215 mDnDMode = DnDMode_Disabled;
216
217 mFirmwareType = FirmwareType_BIOS;
218 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
219 mPointingHIDType = PointingHIDType_PS2Mouse;
220 mChipsetType = ChipsetType_PIIX3;
221 mIommuType = IommuType_None;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param strOsType OS Type string (stored as is if aOsType is NULL).
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 const Utf8Str &strOsType,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 if (llGroups.size())
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352
353 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
354 AssertComRC(rc);
355 }
356 else if (!strOsType.isEmpty())
357 {
358 /* Store OS type */
359 mUserData->s.strOsType = strOsType;
360
361 /* No guest OS type object. Pick some plausible defaults which the
362 * host can handle. There's no way to know or validate anything. */
363 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 mHWData->mX2APIC = false;
365 }
366
367 /* Apply BIOS defaults. */
368 mBIOSSettings->i_applyDefaults(aOsType);
369
370 /* Apply TPM defaults. */
371 mTrustedPlatformModule->i_applyDefaults(aOsType);
372
373 /* Apply record defaults. */
374 mRecordingSettings->i_applyDefaults();
375
376 /* Apply network adapters defaults */
377 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
378 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
379
380 /* Apply serial port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
382 mSerialPorts[slot]->i_applyDefaults(aOsType);
383
384 /* Apply parallel port defaults */
385 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
386 mParallelPorts[slot]->i_applyDefaults();
387
388 /* Enable the VMMDev testing feature for bootsector VMs: */
389 if (aOsType && aOsType->i_id() == "VBoxBS_64")
390 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
391
392 /* At this point the changing of the current state modification
393 * flag is allowed. */
394 i_allowStateModification();
395
396 /* commit all changes made during the initialization */
397 i_commit();
398 }
399
400 /* Confirm a successful initialization when it's the case */
401 if (SUCCEEDED(rc))
402 {
403 if (mData->mAccessible)
404 autoInitSpan.setSucceeded();
405 else
406 autoInitSpan.setLimited();
407 }
408
409 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
410 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
411 mData->mRegistered,
412 mData->mAccessible,
413 rc));
414
415 LogFlowThisFuncLeave();
416
417 return rc;
418}
419
420/**
421 * Initializes a new instance with data from machine XML (formerly Init_Registered).
422 * Gets called in two modes:
423 *
424 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
425 * UUID is specified and we mark the machine as "registered";
426 *
427 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
428 * and the machine remains unregistered until RegisterMachine() is called.
429 *
430 * @param aParent Associated parent object
431 * @param strConfigFile Local file system path to the VM settings file (can
432 * be relative to the VirtualBox config directory).
433 * @param aId UUID of the machine or NULL (see above).
434 *
435 * @return Success indicator. if not S_OK, the machine object is invalid
436 */
437HRESULT Machine::initFromSettings(VirtualBox *aParent,
438 const Utf8Str &strConfigFile,
439 const Guid *aId)
440{
441 LogFlowThisFuncEnter();
442 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
443
444 /* Enclose the state transition NotReady->InInit->Ready */
445 AutoInitSpan autoInitSpan(this);
446 AssertReturn(autoInitSpan.isOk(), E_FAIL);
447
448 HRESULT rc = initImpl(aParent, strConfigFile);
449 if (FAILED(rc)) return rc;
450
451 if (aId)
452 {
453 // loading a registered VM:
454 unconst(mData->mUuid) = *aId;
455 mData->mRegistered = TRUE;
456 // now load the settings from XML:
457 rc = i_registeredInit();
458 // this calls initDataAndChildObjects() and loadSettings()
459 }
460 else
461 {
462 // opening an unregistered VM (VirtualBox::OpenMachine()):
463 rc = initDataAndChildObjects();
464
465 if (SUCCEEDED(rc))
466 {
467 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
468 mData->mAccessible = TRUE;
469
470 try
471 {
472 // load and parse machine XML; this will throw on XML or logic errors
473 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
474
475 // reject VM UUID duplicates, they can happen if someone
476 // tries to register an already known VM config again
477 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
478 true /* fPermitInaccessible */,
479 false /* aDoSetError */,
480 NULL) != VBOX_E_OBJECT_NOT_FOUND)
481 {
482 throw setError(E_FAIL,
483 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
484 mData->m_strConfigFile.c_str());
485 }
486
487 // use UUID from machine config
488 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
489
490 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
491 NULL /* puuidRegistry */);
492 if (FAILED(rc)) throw rc;
493
494 /* At this point the changing of the current state modification
495 * flag is allowed. */
496 i_allowStateModification();
497
498 i_commit();
499 }
500 catch (HRESULT err)
501 {
502 /* we assume that error info is set by the thrower */
503 rc = err;
504 }
505 catch (...)
506 {
507 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
508 }
509 }
510 }
511
512 /* Confirm a successful initialization when it's the case */
513 if (SUCCEEDED(rc))
514 {
515 if (mData->mAccessible)
516 autoInitSpan.setSucceeded();
517 else
518 {
519 autoInitSpan.setLimited();
520
521 // uninit media from this machine's media registry, or else
522 // reloading the settings will fail
523 mParent->i_unregisterMachineMedia(i_getId());
524 }
525 }
526
527 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
528 "rc=%08X\n",
529 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
530 mData->mRegistered, mData->mAccessible, rc));
531
532 LogFlowThisFuncLeave();
533
534 return rc;
535}
536
537/**
538 * Initializes a new instance from a machine config that is already in memory
539 * (import OVF case). Since we are importing, the UUID in the machine
540 * config is ignored and we always generate a fresh one.
541 *
542 * @param aParent Associated parent object.
543 * @param strName Name for the new machine; this overrides what is specified in config.
544 * @param strSettingsFilename File name of .vbox file.
545 * @param config Machine configuration loaded and parsed from XML.
546 *
547 * @return Success indicator. if not S_OK, the machine object is invalid
548 */
549HRESULT Machine::init(VirtualBox *aParent,
550 const Utf8Str &strName,
551 const Utf8Str &strSettingsFilename,
552 const settings::MachineConfigFile &config)
553{
554 LogFlowThisFuncEnter();
555
556 /* Enclose the state transition NotReady->InInit->Ready */
557 AutoInitSpan autoInitSpan(this);
558 AssertReturn(autoInitSpan.isOk(), E_FAIL);
559
560 HRESULT rc = initImpl(aParent, strSettingsFilename);
561 if (FAILED(rc)) return rc;
562
563 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
564 if (FAILED(rc)) return rc;
565
566 rc = initDataAndChildObjects();
567
568 if (SUCCEEDED(rc))
569 {
570 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
571 mData->mAccessible = TRUE;
572
573 // create empty machine config for instance data
574 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
575
576 // generate fresh UUID, ignore machine config
577 unconst(mData->mUuid).create();
578
579 rc = i_loadMachineDataFromSettings(config,
580 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
581
582 // override VM name as well, it may be different
583 mUserData->s.strName = strName;
584
585 if (SUCCEEDED(rc))
586 {
587 /* At this point the changing of the current state modification
588 * flag is allowed. */
589 i_allowStateModification();
590
591 /* commit all changes made during the initialization */
592 i_commit();
593 }
594 }
595
596 /* Confirm a successful initialization when it's the case */
597 if (SUCCEEDED(rc))
598 {
599 if (mData->mAccessible)
600 autoInitSpan.setSucceeded();
601 else
602 {
603 /* Ignore all errors from unregistering, they would destroy
604- * the more interesting error information we already have,
605- * pinpointing the issue with the VM config. */
606 ErrorInfoKeeper eik;
607
608 autoInitSpan.setLimited();
609
610 // uninit media from this machine's media registry, or else
611 // reloading the settings will fail
612 mParent->i_unregisterMachineMedia(i_getId());
613 }
614 }
615
616 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
617 "rc=%08X\n",
618 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
619 mData->mRegistered, mData->mAccessible, rc));
620
621 LogFlowThisFuncLeave();
622
623 return rc;
624}
625
626/**
627 * Shared code between the various init() implementations.
628 * @param aParent The VirtualBox object.
629 * @param strConfigFile Settings file.
630 * @return
631 */
632HRESULT Machine::initImpl(VirtualBox *aParent,
633 const Utf8Str &strConfigFile)
634{
635 LogFlowThisFuncEnter();
636
637 AssertReturn(aParent, E_INVALIDARG);
638 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
639
640 HRESULT rc = S_OK;
641
642 /* share the parent weakly */
643 unconst(mParent) = aParent;
644
645 /* allocate the essential machine data structure (the rest will be
646 * allocated later by initDataAndChildObjects() */
647 mData.allocate();
648
649 /* memorize the config file name (as provided) */
650 mData->m_strConfigFile = strConfigFile;
651
652 /* get the full file name */
653 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
654 if (RT_FAILURE(vrc1))
655 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
656 tr("Invalid machine settings file name '%s' (%Rrc)"),
657 strConfigFile.c_str(),
658 vrc1);
659
660 LogFlowThisFuncLeave();
661
662 return rc;
663}
664
665/**
666 * Tries to create a machine settings file in the path stored in the machine
667 * instance data. Used when a new machine is created to fail gracefully if
668 * the settings file could not be written (e.g. because machine dir is read-only).
669 * @return
670 */
671HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
672{
673 HRESULT rc = S_OK;
674
675 // when we create a new machine, we must be able to create the settings file
676 RTFILE f = NIL_RTFILE;
677 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
678 if ( RT_SUCCESS(vrc)
679 || vrc == VERR_SHARING_VIOLATION
680 )
681 {
682 if (RT_SUCCESS(vrc))
683 RTFileClose(f);
684 if (!fForceOverwrite)
685 rc = setError(VBOX_E_FILE_ERROR,
686 tr("Machine settings file '%s' already exists"),
687 mData->m_strConfigFileFull.c_str());
688 else
689 {
690 /* try to delete the config file, as otherwise the creation
691 * of a new settings file will fail. */
692 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
693 if (RT_FAILURE(vrc2))
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
695 tr("Could not delete the existing settings file '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(), vrc2);
697 }
698 }
699 else if ( vrc != VERR_FILE_NOT_FOUND
700 && vrc != VERR_PATH_NOT_FOUND
701 )
702 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
703 tr("Invalid machine settings file name '%s' (%Rrc)"),
704 mData->m_strConfigFileFull.c_str(),
705 vrc);
706 return rc;
707}
708
709/**
710 * Initializes the registered machine by loading the settings file.
711 * This method is separated from #init() in order to make it possible to
712 * retry the operation after VirtualBox startup instead of refusing to
713 * startup the whole VirtualBox server in case if the settings file of some
714 * registered VM is invalid or inaccessible.
715 *
716 * @note Must be always called from this object's write lock
717 * (unless called from #init() that doesn't need any locking).
718 * @note Locks the mUSBController method for writing.
719 * @note Subclasses must not call this method.
720 */
721HRESULT Machine::i_registeredInit()
722{
723 AssertReturn(!i_isSessionMachine(), E_FAIL);
724 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
725 AssertReturn(mData->mUuid.isValid(), E_FAIL);
726 AssertReturn(!mData->mAccessible, E_FAIL);
727
728 HRESULT rc = initDataAndChildObjects();
729
730 if (SUCCEEDED(rc))
731 {
732 /* Temporarily reset the registered flag in order to let setters
733 * potentially called from loadSettings() succeed (isMutable() used in
734 * all setters will return FALSE for a Machine instance if mRegistered
735 * is TRUE). */
736 mData->mRegistered = FALSE;
737
738 try
739 {
740 // load and parse machine XML; this will throw on XML or logic errors
741 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
742
743 if (mData->mUuid != mData->pMachineConfigFile->uuid)
744 throw setError(E_FAIL,
745 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
746 mData->pMachineConfigFile->uuid.raw(),
747 mData->m_strConfigFileFull.c_str(),
748 mData->mUuid.toString().c_str(),
749 mParent->i_settingsFilePath().c_str());
750
751 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
752 NULL /* const Guid *puuidRegistry */);
753 if (FAILED(rc)) throw rc;
754 }
755 catch (HRESULT err)
756 {
757 /* we assume that error info is set by the thrower */
758 rc = err;
759 }
760 catch (...)
761 {
762 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
763 }
764
765 /* Restore the registered flag (even on failure) */
766 mData->mRegistered = TRUE;
767 }
768
769 if (SUCCEEDED(rc))
770 {
771 /* Set mAccessible to TRUE only if we successfully locked and loaded
772 * the settings file */
773 mData->mAccessible = TRUE;
774
775 /* commit all changes made during loading the settings file */
776 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
777 /// @todo r=klaus for some reason the settings loading logic backs up
778 // the settings, and therefore a commit is needed. Should probably be changed.
779 }
780 else
781 {
782 /* If the machine is registered, then, instead of returning a
783 * failure, we mark it as inaccessible and set the result to
784 * success to give it a try later */
785
786 /* fetch the current error info */
787 mData->mAccessError = com::ErrorInfo();
788 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
789
790 /* rollback all changes */
791 i_rollback(false /* aNotify */);
792
793 // uninit media from this machine's media registry, or else
794 // reloading the settings will fail
795 mParent->i_unregisterMachineMedia(i_getId());
796
797 /* uninitialize the common part to make sure all data is reset to
798 * default (null) values */
799 uninitDataAndChildObjects();
800
801 rc = S_OK;
802 }
803
804 return rc;
805}
806
807/**
808 * Uninitializes the instance.
809 * Called either from FinalRelease() or by the parent when it gets destroyed.
810 *
811 * @note The caller of this method must make sure that this object
812 * a) doesn't have active callers on the current thread and b) is not locked
813 * by the current thread; otherwise uninit() will hang either a) due to
814 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
815 * a dead-lock caused by this thread waiting for all callers on the other
816 * threads are done but preventing them from doing so by holding a lock.
817 */
818void Machine::uninit()
819{
820 LogFlowThisFuncEnter();
821
822 Assert(!isWriteLockOnCurrentThread());
823
824 Assert(!uRegistryNeedsSaving);
825 if (uRegistryNeedsSaving)
826 {
827 AutoCaller autoCaller(this);
828 if (SUCCEEDED(autoCaller.rc()))
829 {
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831 i_saveSettings(NULL, alock, Machine::SaveS_Force);
832 }
833 }
834
835 /* Enclose the state transition Ready->InUninit->NotReady */
836 AutoUninitSpan autoUninitSpan(this);
837 if (autoUninitSpan.uninitDone())
838 return;
839
840 Assert(!i_isSnapshotMachine());
841 Assert(!i_isSessionMachine());
842 Assert(!!mData);
843
844 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
845 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
846
847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
848
849 if (!mData->mSession.mMachine.isNull())
850 {
851 /* Theoretically, this can only happen if the VirtualBox server has been
852 * terminated while there were clients running that owned open direct
853 * sessions. Since in this case we are definitely called by
854 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
855 * won't happen on the client watcher thread (because it has a
856 * VirtualBox caller for the duration of the
857 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
858 * cannot happen until the VirtualBox caller is released). This is
859 * important, because SessionMachine::uninit() cannot correctly operate
860 * after we return from this method (it expects the Machine instance is
861 * still valid). We'll call it ourselves below.
862 */
863 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
864 (SessionMachine*)mData->mSession.mMachine));
865
866 if (Global::IsOnlineOrTransient(mData->mMachineState))
867 {
868 Log1WarningThisFunc(("Setting state to Aborted!\n"));
869 /* set machine state using SessionMachine reimplementation */
870 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
871 }
872
873 /*
874 * Uninitialize SessionMachine using public uninit() to indicate
875 * an unexpected uninitialization.
876 */
877 mData->mSession.mMachine->uninit();
878 /* SessionMachine::uninit() must set mSession.mMachine to null */
879 Assert(mData->mSession.mMachine.isNull());
880 }
881
882 // uninit media from this machine's media registry, if they're still there
883 Guid uuidMachine(i_getId());
884
885 /* the lock is no more necessary (SessionMachine is uninitialized) */
886 alock.release();
887
888 /* XXX This will fail with
889 * "cannot be closed because it is still attached to 1 virtual machines"
890 * because at this point we did not call uninitDataAndChildObjects() yet
891 * and therefore also removeBackReference() for all these mediums was not called! */
892
893 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
894 mParent->i_unregisterMachineMedia(uuidMachine);
895
896 // has machine been modified?
897 if (mData->flModifications)
898 {
899 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
900 i_rollback(false /* aNotify */);
901 }
902
903 if (mData->mAccessible)
904 uninitDataAndChildObjects();
905
906 /* free the essential data structure last */
907 mData.free();
908
909 LogFlowThisFuncLeave();
910}
911
912// Wrapped IMachine properties
913/////////////////////////////////////////////////////////////////////////////
914HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
915{
916 /* mParent is constant during life time, no need to lock */
917 ComObjPtr<VirtualBox> pVirtualBox(mParent);
918 aParent = pVirtualBox;
919
920 return S_OK;
921}
922
923
924HRESULT Machine::getAccessible(BOOL *aAccessible)
925{
926 /* In some cases (medium registry related), it is necessary to be able to
927 * go through the list of all machines. Happens when an inaccessible VM
928 * has a sensible medium registry. */
929 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
931
932 HRESULT rc = S_OK;
933
934 if (!mData->mAccessible)
935 {
936 /* try to initialize the VM once more if not accessible */
937
938 AutoReinitSpan autoReinitSpan(this);
939 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
940
941#ifdef DEBUG
942 LogFlowThisFunc(("Dumping media backreferences\n"));
943 mParent->i_dumpAllBackRefs();
944#endif
945
946 if (mData->pMachineConfigFile)
947 {
948 // reset the XML file to force loadSettings() (called from i_registeredInit())
949 // to parse it again; the file might have changed
950 delete mData->pMachineConfigFile;
951 mData->pMachineConfigFile = NULL;
952 }
953
954 rc = i_registeredInit();
955
956 if (SUCCEEDED(rc) && mData->mAccessible)
957 {
958 autoReinitSpan.setSucceeded();
959
960 /* make sure interesting parties will notice the accessibility
961 * state change */
962 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
963 mParent->i_onMachineDataChanged(mData->mUuid);
964 }
965 }
966
967 if (SUCCEEDED(rc))
968 *aAccessible = mData->mAccessible;
969
970 LogFlowThisFuncLeave();
971
972 return rc;
973}
974
975HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
976{
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
980 {
981 /* return shortly */
982 aAccessError = NULL;
983 return S_OK;
984 }
985
986 HRESULT rc = S_OK;
987
988 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
989 rc = errorInfo.createObject();
990 if (SUCCEEDED(rc))
991 {
992 errorInfo->init(mData->mAccessError.getResultCode(),
993 mData->mAccessError.getInterfaceID().ref(),
994 Utf8Str(mData->mAccessError.getComponent()).c_str(),
995 Utf8Str(mData->mAccessError.getText()));
996 aAccessError = errorInfo;
997 }
998
999 return rc;
1000}
1001
1002HRESULT Machine::getName(com::Utf8Str &aName)
1003{
1004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1005
1006 aName = mUserData->s.strName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::setName(const com::Utf8Str &aName)
1012{
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 i_setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1033{
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 aDescription = mUserData->s.strDescription;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1042{
1043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 // this can be done in principle in any state as it doesn't affect the VM
1046 // significantly, but play safe by not messing around while complex
1047 // activities are going on
1048 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1049 if (FAILED(rc)) return rc;
1050
1051 i_setModified(IsModified_MachineData);
1052 mUserData.backup();
1053 mUserData->s.strDescription = aDescription;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getId(com::Guid &aId)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 aId = mData->mUuid;
1063
1064 return S_OK;
1065}
1066
1067HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1068{
1069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1070 aGroups.resize(mUserData->s.llGroups.size());
1071 size_t i = 0;
1072 for (StringsList::const_iterator
1073 it = mUserData->s.llGroups.begin();
1074 it != mUserData->s.llGroups.end();
1075 ++it, ++i)
1076 aGroups[i] = (*it);
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1082{
1083 StringsList llGroups;
1084 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1085 if (FAILED(rc))
1086 return rc;
1087
1088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1089
1090 rc = i_checkStateDependency(MutableOrSavedStateDep);
1091 if (FAILED(rc)) return rc;
1092
1093 i_setModified(IsModified_MachineData);
1094 mUserData.backup();
1095 mUserData->s.llGroups = llGroups;
1096
1097 return S_OK;
1098}
1099
1100HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1101{
1102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 aOSTypeId = mUserData->s.strOsType;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1110{
1111 /* look up the object by Id to check it is valid */
1112 ComObjPtr<GuestOSType> pGuestOSType;
1113 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1114
1115 /* when setting, always use the "etalon" value for consistency -- lookup
1116 * by ID is case-insensitive and the input value may have different case */
1117 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1118
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mUserData.backup();
1126 mUserData->s.strOsType = osTypeId;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aFirmwareType = mHWData->mFirmwareType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mFirmwareType = aFirmwareType;
1150 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1151 alock.release();
1152
1153 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aPointingHIDType = mHWData->mPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mPointingHIDType = aPointingHIDType;
1200
1201 return S_OK;
1202}
1203
1204HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1205{
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aChipsetType = mHWData->mChipsetType;
1209
1210 return S_OK;
1211}
1212
1213HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1214{
1215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1216
1217 HRESULT rc = i_checkStateDependency(MutableStateDep);
1218 if (FAILED(rc)) return rc;
1219
1220 if (aChipsetType != mHWData->mChipsetType)
1221 {
1222 i_setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mChipsetType = aChipsetType;
1225
1226 // Resize network adapter array, to be finalized on commit/rollback.
1227 // We must not throw away entries yet, otherwise settings are lost
1228 // without a way to roll back.
1229 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1230 size_t oldCount = mNetworkAdapters.size();
1231 if (newCount > oldCount)
1232 {
1233 mNetworkAdapters.resize(newCount);
1234 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1235 {
1236 unconst(mNetworkAdapters[slot]).createObject();
1237 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1238 }
1239 }
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aIommuType = mHWData->mIommuType;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setIommuType(IommuType_T aIommuType)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aIommuType != mHWData->mIommuType)
1262 {
1263 if (aIommuType == IommuType_Intel)
1264 {
1265#ifndef VBOX_WITH_IOMMU_INTEL
1266 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1267 return E_UNEXPECTED;
1268#endif
1269 }
1270
1271 i_setModified(IsModified_MachineData);
1272 mHWData.backup();
1273 mHWData->mIommuType = aIommuType;
1274 }
1275
1276 return S_OK;
1277}
1278
1279HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1280{
1281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 aParavirtDebug = mHWData->mParavirtDebug;
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1288{
1289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1290
1291 HRESULT rc = i_checkStateDependency(MutableStateDep);
1292 if (FAILED(rc)) return rc;
1293
1294 /** @todo Parse/validate options? */
1295 if (aParavirtDebug != mHWData->mParavirtDebug)
1296 {
1297 i_setModified(IsModified_MachineData);
1298 mHWData.backup();
1299 mHWData->mParavirtDebug = aParavirtDebug;
1300 }
1301
1302 return S_OK;
1303}
1304
1305HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1306{
1307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 *aParavirtProvider = mHWData->mParavirtProvider;
1310
1311 return S_OK;
1312}
1313
1314HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1315{
1316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1317
1318 HRESULT rc = i_checkStateDependency(MutableStateDep);
1319 if (FAILED(rc)) return rc;
1320
1321 if (aParavirtProvider != mHWData->mParavirtProvider)
1322 {
1323 i_setModified(IsModified_MachineData);
1324 mHWData.backup();
1325 mHWData->mParavirtProvider = aParavirtProvider;
1326 }
1327
1328 return S_OK;
1329}
1330
1331HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1332{
1333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 *aParavirtProvider = mHWData->mParavirtProvider;
1336 switch (mHWData->mParavirtProvider)
1337 {
1338 case ParavirtProvider_None:
1339 case ParavirtProvider_HyperV:
1340 case ParavirtProvider_KVM:
1341 case ParavirtProvider_Minimal:
1342 break;
1343
1344 /* Resolve dynamic provider types to the effective types. */
1345 default:
1346 {
1347 ComObjPtr<GuestOSType> pGuestOSType;
1348 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1349 pGuestOSType);
1350 if (FAILED(hrc2) || pGuestOSType.isNull())
1351 {
1352 *aParavirtProvider = ParavirtProvider_None;
1353 break;
1354 }
1355
1356 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1357 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1358
1359 switch (mHWData->mParavirtProvider)
1360 {
1361 case ParavirtProvider_Legacy:
1362 {
1363 if (fOsXGuest)
1364 *aParavirtProvider = ParavirtProvider_Minimal;
1365 else
1366 *aParavirtProvider = ParavirtProvider_None;
1367 break;
1368 }
1369
1370 case ParavirtProvider_Default:
1371 {
1372 if (fOsXGuest)
1373 *aParavirtProvider = ParavirtProvider_Minimal;
1374 else if ( mUserData->s.strOsType == "Windows11_64"
1375 || mUserData->s.strOsType == "Windows10"
1376 || mUserData->s.strOsType == "Windows10_64"
1377 || mUserData->s.strOsType == "Windows81"
1378 || mUserData->s.strOsType == "Windows81_64"
1379 || mUserData->s.strOsType == "Windows8"
1380 || mUserData->s.strOsType == "Windows8_64"
1381 || mUserData->s.strOsType == "Windows7"
1382 || mUserData->s.strOsType == "Windows7_64"
1383 || mUserData->s.strOsType == "WindowsVista"
1384 || mUserData->s.strOsType == "WindowsVista_64"
1385 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1386 || mUserData->s.strOsType.startsWith("Windows201"))
1387 && mUserData->s.strOsType.endsWith("_64"))
1388 || mUserData->s.strOsType == "Windows2012"
1389 || mUserData->s.strOsType == "Windows2012_64"
1390 || mUserData->s.strOsType == "Windows2008"
1391 || mUserData->s.strOsType == "Windows2008_64")
1392 {
1393 *aParavirtProvider = ParavirtProvider_HyperV;
1394 }
1395 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1396 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1397 || mUserData->s.strOsType == "Linux"
1398 || mUserData->s.strOsType == "Linux_64"
1399 || mUserData->s.strOsType == "ArchLinux"
1400 || mUserData->s.strOsType == "ArchLinux_64"
1401 || mUserData->s.strOsType == "Debian"
1402 || mUserData->s.strOsType == "Debian_64"
1403 || mUserData->s.strOsType == "Fedora"
1404 || mUserData->s.strOsType == "Fedora_64"
1405 || mUserData->s.strOsType == "Gentoo"
1406 || mUserData->s.strOsType == "Gentoo_64"
1407 || mUserData->s.strOsType == "Mandriva"
1408 || mUserData->s.strOsType == "Mandriva_64"
1409 || mUserData->s.strOsType == "OpenSUSE"
1410 || mUserData->s.strOsType == "OpenSUSE_64"
1411 || mUserData->s.strOsType == "Oracle"
1412 || mUserData->s.strOsType == "Oracle_64"
1413 || mUserData->s.strOsType == "RedHat"
1414 || mUserData->s.strOsType == "RedHat_64"
1415 || mUserData->s.strOsType == "Turbolinux"
1416 || mUserData->s.strOsType == "Turbolinux_64"
1417 || mUserData->s.strOsType == "Ubuntu"
1418 || mUserData->s.strOsType == "Ubuntu_64"
1419 || mUserData->s.strOsType == "Xandros"
1420 || mUserData->s.strOsType == "Xandros_64")
1421 {
1422 *aParavirtProvider = ParavirtProvider_KVM;
1423 }
1424 else
1425 *aParavirtProvider = ParavirtProvider_None;
1426 break;
1427 }
1428
1429 default: AssertFailedBreak(); /* Shut up MSC. */
1430 }
1431 break;
1432 }
1433 }
1434
1435 Assert( *aParavirtProvider == ParavirtProvider_None
1436 || *aParavirtProvider == ParavirtProvider_Minimal
1437 || *aParavirtProvider == ParavirtProvider_HyperV
1438 || *aParavirtProvider == ParavirtProvider_KVM);
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 aHardwareVersion = mHWData->mHWVersion;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1452{
1453 /* check known version */
1454 Utf8Str hwVersion = aHardwareVersion;
1455 if ( hwVersion.compare("1") != 0
1456 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1457 return setError(E_INVALIDARG,
1458 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 HRESULT rc = i_checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mHWVersion = aHardwareVersion;
1468
1469 return S_OK;
1470}
1471
1472HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1473{
1474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 if (!mHWData->mHardwareUUID.isZero())
1477 aHardwareUUID = mHWData->mHardwareUUID;
1478 else
1479 aHardwareUUID = mData->mUuid;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1485{
1486 if (!aHardwareUUID.isValid())
1487 return E_INVALIDARG;
1488
1489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1490
1491 HRESULT rc = i_checkStateDependency(MutableStateDep);
1492 if (FAILED(rc)) return rc;
1493
1494 i_setModified(IsModified_MachineData);
1495 mHWData.backup();
1496 if (aHardwareUUID == mData->mUuid)
1497 mHWData->mHardwareUUID.clear();
1498 else
1499 mHWData->mHardwareUUID = aHardwareUUID;
1500
1501 return S_OK;
1502}
1503
1504HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1505{
1506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 *aMemorySize = mHWData->mMemorySize;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::setMemorySize(ULONG aMemorySize)
1514{
1515 /* check RAM limits */
1516 if ( aMemorySize < MM_RAM_MIN_IN_MB
1517 || aMemorySize > MM_RAM_MAX_IN_MB
1518 )
1519 return setError(E_INVALIDARG,
1520 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1521 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1522
1523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 HRESULT rc = i_checkStateDependency(MutableStateDep);
1526 if (FAILED(rc)) return rc;
1527
1528 i_setModified(IsModified_MachineData);
1529 mHWData.backup();
1530 mHWData->mMemorySize = aMemorySize;
1531
1532 return S_OK;
1533}
1534
1535HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1536{
1537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 *aCPUCount = mHWData->mCPUCount;
1540
1541 return S_OK;
1542}
1543
1544HRESULT Machine::setCPUCount(ULONG aCPUCount)
1545{
1546 /* check CPU limits */
1547 if ( aCPUCount < SchemaDefs::MinCPUCount
1548 || aCPUCount > SchemaDefs::MaxCPUCount
1549 )
1550 return setError(E_INVALIDARG,
1551 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1552 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1557 if (mHWData->mCPUHotPlugEnabled)
1558 {
1559 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1560 {
1561 if (mHWData->mCPUAttached[idx])
1562 return setError(E_INVALIDARG,
1563 tr("There is still a CPU attached to socket %lu."
1564 "Detach the CPU before removing the socket"),
1565 aCPUCount, idx+1);
1566 }
1567 }
1568
1569 HRESULT rc = i_checkStateDependency(MutableStateDep);
1570 if (FAILED(rc)) return rc;
1571
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574 mHWData->mCPUCount = aCPUCount;
1575
1576 return S_OK;
1577}
1578
1579HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1580{
1581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1582
1583 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1584
1585 return S_OK;
1586}
1587
1588HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1589{
1590 HRESULT rc = S_OK;
1591
1592 /* check throttle limits */
1593 if ( aCPUExecutionCap < 1
1594 || aCPUExecutionCap > 100
1595 )
1596 return setError(E_INVALIDARG,
1597 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1598 aCPUExecutionCap, 1, 100);
1599
1600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 alock.release();
1603 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1604 alock.acquire();
1605 if (FAILED(rc)) return rc;
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1610
1611 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1612 if (Global::IsOnline(mData->mMachineState))
1613 i_saveSettings(NULL, alock);
1614
1615 return S_OK;
1616}
1617
1618HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1619{
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1623
1624 return S_OK;
1625}
1626
1627HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1628{
1629 HRESULT rc = S_OK;
1630
1631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1632
1633 rc = i_checkStateDependency(MutableStateDep);
1634 if (FAILED(rc)) return rc;
1635
1636 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1637 {
1638 if (aCPUHotPlugEnabled)
1639 {
1640 i_setModified(IsModified_MachineData);
1641 mHWData.backup();
1642
1643 /* Add the amount of CPUs currently attached */
1644 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1645 mHWData->mCPUAttached[i] = true;
1646 }
1647 else
1648 {
1649 /*
1650 * We can disable hotplug only if the amount of maximum CPUs is equal
1651 * to the amount of attached CPUs
1652 */
1653 unsigned cCpusAttached = 0;
1654 unsigned iHighestId = 0;
1655
1656 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1657 {
1658 if (mHWData->mCPUAttached[i])
1659 {
1660 cCpusAttached++;
1661 iHighestId = i;
1662 }
1663 }
1664
1665 if ( (cCpusAttached != mHWData->mCPUCount)
1666 || (iHighestId >= mHWData->mCPUCount))
1667 return setError(E_INVALIDARG,
1668 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1669
1670 i_setModified(IsModified_MachineData);
1671 mHWData.backup();
1672 }
1673 }
1674
1675 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1676
1677 return rc;
1678}
1679
1680HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1681{
1682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1683
1684 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1685
1686 return S_OK;
1687}
1688
1689HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1690{
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1694 if (SUCCEEDED(hrc))
1695 {
1696 i_setModified(IsModified_MachineData);
1697 mHWData.backup();
1698 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1699 }
1700 return hrc;
1701}
1702
1703HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1704{
1705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1706 aCPUProfile = mHWData->mCpuProfile;
1707 return S_OK;
1708}
1709
1710HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1711{
1712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1713 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1714 if (SUCCEEDED(hrc))
1715 {
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718 /* Empty equals 'host'. */
1719 if (aCPUProfile.isNotEmpty())
1720 mHWData->mCpuProfile = aCPUProfile;
1721 else
1722 mHWData->mCpuProfile = "host";
1723 }
1724 return hrc;
1725}
1726
1727HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1728{
1729#ifdef VBOX_WITH_USB_CARDREADER
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1733
1734 return S_OK;
1735#else
1736 NOREF(aEmulatedUSBCardReaderEnabled);
1737 return E_NOTIMPL;
1738#endif
1739}
1740
1741HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1742{
1743#ifdef VBOX_WITH_USB_CARDREADER
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1747 if (FAILED(rc)) return rc;
1748
1749 i_setModified(IsModified_MachineData);
1750 mHWData.backup();
1751 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1752
1753 return S_OK;
1754#else
1755 NOREF(aEmulatedUSBCardReaderEnabled);
1756 return E_NOTIMPL;
1757#endif
1758}
1759
1760HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763
1764 *aHPETEnabled = mHWData->mHPETEnabled;
1765
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1770{
1771 HRESULT rc = S_OK;
1772
1773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 rc = i_checkStateDependency(MutableStateDep);
1776 if (FAILED(rc)) return rc;
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780
1781 mHWData->mHPETEnabled = aHPETEnabled;
1782
1783 return rc;
1784}
1785
1786/** @todo this method should not be public */
1787HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1788{
1789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1790
1791 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1792
1793 return S_OK;
1794}
1795
1796/**
1797 * Set the memory balloon size.
1798 *
1799 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1800 * we have to make sure that we never call IGuest from here.
1801 */
1802HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1803{
1804 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1805#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1806 /* check limits */
1807 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1808 return setError(E_INVALIDARG,
1809 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1810 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1811
1812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1813
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1817
1818 return S_OK;
1819#else
1820 NOREF(aMemoryBalloonSize);
1821 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1822#endif
1823}
1824
1825HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1826{
1827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1830 return S_OK;
1831}
1832
1833HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1834{
1835#ifdef VBOX_WITH_PAGE_SHARING
1836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1839 i_setModified(IsModified_MachineData);
1840 mHWData.backup();
1841 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1842 return S_OK;
1843#else
1844 NOREF(aPageFusionEnabled);
1845 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1846#endif
1847}
1848
1849HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1850{
1851 /* mBIOSSettings is constant during life time, no need to lock */
1852 aBIOSSettings = mBIOSSettings;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1858{
1859 /* mTrustedPlatformModule is constant during life time, no need to lock */
1860 aTrustedPlatformModule = mTrustedPlatformModule;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1866{
1867 /* mNvramStore is constant during life time, no need to lock */
1868 aNvramStore = mNvramStore;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 aRecordingSettings = mRecordingSettings;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 aGraphicsAdapter = mGraphicsAdapter;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1892{
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 switch (aProperty)
1896 {
1897 case CPUPropertyType_PAE:
1898 *aValue = mHWData->mPAEEnabled;
1899 break;
1900
1901 case CPUPropertyType_LongMode:
1902 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1903 *aValue = TRUE;
1904 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1905 *aValue = FALSE;
1906#if HC_ARCH_BITS == 64
1907 else
1908 *aValue = TRUE;
1909#else
1910 else
1911 {
1912 *aValue = FALSE;
1913
1914 ComObjPtr<GuestOSType> pGuestOSType;
1915 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1916 pGuestOSType);
1917 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1918 {
1919 if (pGuestOSType->i_is64Bit())
1920 {
1921 ComObjPtr<Host> pHost = mParent->i_host();
1922 alock.release();
1923
1924 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1925 if (FAILED(hrc2))
1926 *aValue = FALSE;
1927 }
1928 }
1929 }
1930#endif
1931 break;
1932
1933 case CPUPropertyType_TripleFaultReset:
1934 *aValue = mHWData->mTripleFaultReset;
1935 break;
1936
1937 case CPUPropertyType_APIC:
1938 *aValue = mHWData->mAPIC;
1939 break;
1940
1941 case CPUPropertyType_X2APIC:
1942 *aValue = mHWData->mX2APIC;
1943 break;
1944
1945 case CPUPropertyType_IBPBOnVMExit:
1946 *aValue = mHWData->mIBPBOnVMExit;
1947 break;
1948
1949 case CPUPropertyType_IBPBOnVMEntry:
1950 *aValue = mHWData->mIBPBOnVMEntry;
1951 break;
1952
1953 case CPUPropertyType_SpecCtrl:
1954 *aValue = mHWData->mSpecCtrl;
1955 break;
1956
1957 case CPUPropertyType_SpecCtrlByHost:
1958 *aValue = mHWData->mSpecCtrlByHost;
1959 break;
1960
1961 case CPUPropertyType_HWVirt:
1962 *aValue = mHWData->mNestedHWVirt;
1963 break;
1964
1965 case CPUPropertyType_L1DFlushOnEMTScheduling:
1966 *aValue = mHWData->mL1DFlushOnSched;
1967 break;
1968
1969 case CPUPropertyType_L1DFlushOnVMEntry:
1970 *aValue = mHWData->mL1DFlushOnVMEntry;
1971 break;
1972
1973 case CPUPropertyType_MDSClearOnEMTScheduling:
1974 *aValue = mHWData->mMDSClearOnSched;
1975 break;
1976
1977 case CPUPropertyType_MDSClearOnVMEntry:
1978 *aValue = mHWData->mMDSClearOnVMEntry;
1979 break;
1980
1981 default:
1982 return E_INVALIDARG;
1983 }
1984 return S_OK;
1985}
1986
1987HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1988{
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = i_checkStateDependency(MutableStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 switch (aProperty)
1995 {
1996 case CPUPropertyType_PAE:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mPAEEnabled = !!aValue;
2000 break;
2001
2002 case CPUPropertyType_LongMode:
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2006 break;
2007
2008 case CPUPropertyType_TripleFaultReset:
2009 i_setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mTripleFaultReset = !!aValue;
2012 break;
2013
2014 case CPUPropertyType_APIC:
2015 if (mHWData->mX2APIC)
2016 aValue = TRUE;
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mAPIC = !!aValue;
2020 break;
2021
2022 case CPUPropertyType_X2APIC:
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mX2APIC = !!aValue;
2026 if (aValue)
2027 mHWData->mAPIC = !!aValue;
2028 break;
2029
2030 case CPUPropertyType_IBPBOnVMExit:
2031 i_setModified(IsModified_MachineData);
2032 mHWData.backup();
2033 mHWData->mIBPBOnVMExit = !!aValue;
2034 break;
2035
2036 case CPUPropertyType_IBPBOnVMEntry:
2037 i_setModified(IsModified_MachineData);
2038 mHWData.backup();
2039 mHWData->mIBPBOnVMEntry = !!aValue;
2040 break;
2041
2042 case CPUPropertyType_SpecCtrl:
2043 i_setModified(IsModified_MachineData);
2044 mHWData.backup();
2045 mHWData->mSpecCtrl = !!aValue;
2046 break;
2047
2048 case CPUPropertyType_SpecCtrlByHost:
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mSpecCtrlByHost = !!aValue;
2052 break;
2053
2054 case CPUPropertyType_HWVirt:
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mNestedHWVirt = !!aValue;
2058 break;
2059
2060 case CPUPropertyType_L1DFlushOnEMTScheduling:
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mL1DFlushOnSched = !!aValue;
2064 break;
2065
2066 case CPUPropertyType_L1DFlushOnVMEntry:
2067 i_setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mL1DFlushOnVMEntry = !!aValue;
2070 break;
2071
2072 case CPUPropertyType_MDSClearOnEMTScheduling:
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mMDSClearOnSched = !!aValue;
2076 break;
2077
2078 case CPUPropertyType_MDSClearOnVMEntry:
2079 i_setModified(IsModified_MachineData);
2080 mHWData.backup();
2081 mHWData->mMDSClearOnVMEntry = !!aValue;
2082 break;
2083
2084 default:
2085 return E_INVALIDARG;
2086 }
2087 return S_OK;
2088}
2089
2090HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2091 ULONG *aValEcx, ULONG *aValEdx)
2092{
2093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2094 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2095 {
2096 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2097 it != mHWData->mCpuIdLeafList.end();
2098 ++it)
2099 {
2100 if (aOrdinal == 0)
2101 {
2102 const settings::CpuIdLeaf &rLeaf= *it;
2103 *aIdx = rLeaf.idx;
2104 *aSubIdx = rLeaf.idxSub;
2105 *aValEax = rLeaf.uEax;
2106 *aValEbx = rLeaf.uEbx;
2107 *aValEcx = rLeaf.uEcx;
2108 *aValEdx = rLeaf.uEdx;
2109 return S_OK;
2110 }
2111 aOrdinal--;
2112 }
2113 }
2114 return E_INVALIDARG;
2115}
2116
2117HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 /*
2122 * Search the list.
2123 */
2124 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2125 {
2126 const settings::CpuIdLeaf &rLeaf= *it;
2127 if ( rLeaf.idx == aIdx
2128 && ( aSubIdx == UINT32_MAX
2129 || rLeaf.idxSub == aSubIdx) )
2130 {
2131 *aValEax = rLeaf.uEax;
2132 *aValEbx = rLeaf.uEbx;
2133 *aValEcx = rLeaf.uEcx;
2134 *aValEdx = rLeaf.uEdx;
2135 return S_OK;
2136 }
2137 }
2138
2139 return E_INVALIDARG;
2140}
2141
2142
2143HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2144{
2145 /*
2146 * Validate input before taking locks and checking state.
2147 */
2148 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2149 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2150 if ( aIdx >= UINT32_C(0x20)
2151 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2152 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2153 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2154
2155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2156 HRESULT rc = i_checkStateDependency(MutableStateDep);
2157 if (FAILED(rc)) return rc;
2158
2159 /*
2160 * Impose a maximum number of leaves.
2161 */
2162 if (mHWData->mCpuIdLeafList.size() > 256)
2163 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2164
2165 /*
2166 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2167 */
2168 i_setModified(IsModified_MachineData);
2169 mHWData.backup();
2170
2171 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2172 {
2173 settings::CpuIdLeaf &rLeaf= *it;
2174 if ( rLeaf.idx == aIdx
2175 && ( aSubIdx == UINT32_MAX
2176 || rLeaf.idxSub == aSubIdx) )
2177 it = mHWData->mCpuIdLeafList.erase(it);
2178 else
2179 ++it;
2180 }
2181
2182 settings::CpuIdLeaf NewLeaf;
2183 NewLeaf.idx = aIdx;
2184 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2185 NewLeaf.uEax = aValEax;
2186 NewLeaf.uEbx = aValEbx;
2187 NewLeaf.uEcx = aValEcx;
2188 NewLeaf.uEdx = aValEdx;
2189 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2190 return S_OK;
2191}
2192
2193HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2194{
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = i_checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 /*
2201 * Do the removal.
2202 */
2203 bool fModified = mHWData.isBackedUp();
2204 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2205 {
2206 settings::CpuIdLeaf &rLeaf= *it;
2207 if ( rLeaf.idx == aIdx
2208 && ( aSubIdx == UINT32_MAX
2209 || rLeaf.idxSub == aSubIdx) )
2210 {
2211 if (!fModified)
2212 {
2213 fModified = true;
2214 i_setModified(IsModified_MachineData);
2215 mHWData.backup();
2216 // Start from the beginning, since mHWData.backup() creates
2217 // a new list, causing iterator mixup. This makes sure that
2218 // the settings are not unnecessarily marked as modified,
2219 // at the price of extra list walking.
2220 it = mHWData->mCpuIdLeafList.begin();
2221 }
2222 else
2223 it = mHWData->mCpuIdLeafList.erase(it);
2224 }
2225 else
2226 ++it;
2227 }
2228
2229 return S_OK;
2230}
2231
2232HRESULT Machine::removeAllCPUIDLeaves()
2233{
2234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2235
2236 HRESULT rc = i_checkStateDependency(MutableStateDep);
2237 if (FAILED(rc)) return rc;
2238
2239 if (mHWData->mCpuIdLeafList.size() > 0)
2240 {
2241 i_setModified(IsModified_MachineData);
2242 mHWData.backup();
2243
2244 mHWData->mCpuIdLeafList.clear();
2245 }
2246
2247 return S_OK;
2248}
2249HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2250{
2251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2252
2253 switch(aProperty)
2254 {
2255 case HWVirtExPropertyType_Enabled:
2256 *aValue = mHWData->mHWVirtExEnabled;
2257 break;
2258
2259 case HWVirtExPropertyType_VPID:
2260 *aValue = mHWData->mHWVirtExVPIDEnabled;
2261 break;
2262
2263 case HWVirtExPropertyType_NestedPaging:
2264 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2265 break;
2266
2267 case HWVirtExPropertyType_UnrestrictedExecution:
2268 *aValue = mHWData->mHWVirtExUXEnabled;
2269 break;
2270
2271 case HWVirtExPropertyType_LargePages:
2272 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2273 break;
2274
2275 case HWVirtExPropertyType_Force:
2276 *aValue = mHWData->mHWVirtExForceEnabled;
2277 break;
2278
2279 case HWVirtExPropertyType_UseNativeApi:
2280 *aValue = mHWData->mHWVirtExUseNativeApi;
2281 break;
2282
2283 case HWVirtExPropertyType_VirtVmsaveVmload:
2284 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2285 break;
2286
2287 default:
2288 return E_INVALIDARG;
2289 }
2290 return S_OK;
2291}
2292
2293HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2294{
2295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 HRESULT rc = i_checkStateDependency(MutableStateDep);
2298 if (FAILED(rc)) return rc;
2299
2300 switch (aProperty)
2301 {
2302 case HWVirtExPropertyType_Enabled:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mHWVirtExEnabled = !!aValue;
2306 break;
2307
2308 case HWVirtExPropertyType_VPID:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2312 break;
2313
2314 case HWVirtExPropertyType_NestedPaging:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_UnrestrictedExecution:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExUXEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_LargePages:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_Force:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExForceEnabled = !!aValue;
2336 break;
2337
2338 case HWVirtExPropertyType_UseNativeApi:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mHWVirtExUseNativeApi = !!aValue;
2342 break;
2343
2344 case HWVirtExPropertyType_VirtVmsaveVmload:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2348 break;
2349
2350 default:
2351 return E_INVALIDARG;
2352 }
2353
2354 return S_OK;
2355}
2356
2357HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2358{
2359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2362
2363 return S_OK;
2364}
2365
2366HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2367{
2368 /** @todo (r=dmik):
2369 * 1. Allow to change the name of the snapshot folder containing snapshots
2370 * 2. Rename the folder on disk instead of just changing the property
2371 * value (to be smart and not to leave garbage). Note that it cannot be
2372 * done here because the change may be rolled back. Thus, the right
2373 * place is #saveSettings().
2374 */
2375
2376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2377
2378 HRESULT rc = i_checkStateDependency(MutableStateDep);
2379 if (FAILED(rc)) return rc;
2380
2381 if (!mData->mCurrentSnapshot.isNull())
2382 return setError(E_FAIL,
2383 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2384
2385 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2386
2387 if (strSnapshotFolder.isEmpty())
2388 strSnapshotFolder = "Snapshots";
2389 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2390 if (RT_FAILURE(vrc))
2391 return setErrorBoth(E_FAIL, vrc,
2392 tr("Invalid snapshot folder '%s' (%Rrc)"),
2393 strSnapshotFolder.c_str(), vrc);
2394
2395 i_setModified(IsModified_MachineData);
2396 mUserData.backup();
2397
2398 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2404{
2405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2406
2407 aMediumAttachments.resize(mMediumAttachments->size());
2408 size_t i = 0;
2409 for (MediumAttachmentList::const_iterator
2410 it = mMediumAttachments->begin();
2411 it != mMediumAttachments->end();
2412 ++it, ++i)
2413 aMediumAttachments[i] = *it;
2414
2415 return S_OK;
2416}
2417
2418HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2419{
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 Assert(!!mVRDEServer);
2423
2424 aVRDEServer = mVRDEServer;
2425
2426 return S_OK;
2427}
2428
2429HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2430{
2431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
2433 aAudioAdapter = mAudioAdapter;
2434
2435 return S_OK;
2436}
2437
2438HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2439{
2440#ifdef VBOX_WITH_VUSB
2441 clearError();
2442 MultiResult rc(S_OK);
2443
2444# ifdef VBOX_WITH_USB
2445 rc = mParent->i_host()->i_checkUSBProxyService();
2446 if (FAILED(rc)) return rc;
2447# endif
2448
2449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2450
2451 aUSBControllers.resize(mUSBControllers->size());
2452 size_t i = 0;
2453 for (USBControllerList::const_iterator
2454 it = mUSBControllers->begin();
2455 it != mUSBControllers->end();
2456 ++it, ++i)
2457 aUSBControllers[i] = *it;
2458
2459 return S_OK;
2460#else
2461 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2462 * extended error info to indicate that USB is simply not available
2463 * (w/o treating it as a failure), for example, as in OSE */
2464 NOREF(aUSBControllers);
2465 ReturnComNotImplemented();
2466#endif /* VBOX_WITH_VUSB */
2467}
2468
2469HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2470{
2471#ifdef VBOX_WITH_VUSB
2472 clearError();
2473 MultiResult rc(S_OK);
2474
2475# ifdef VBOX_WITH_USB
2476 rc = mParent->i_host()->i_checkUSBProxyService();
2477 if (FAILED(rc)) return rc;
2478# endif
2479
2480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 aUSBDeviceFilters = mUSBDeviceFilters;
2483 return rc;
2484#else
2485 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2486 * extended error info to indicate that USB is simply not available
2487 * (w/o treating it as a failure), for example, as in OSE */
2488 NOREF(aUSBDeviceFilters);
2489 ReturnComNotImplemented();
2490#endif /* VBOX_WITH_VUSB */
2491}
2492
2493HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 aSettingsFilePath = mData->m_strConfigFileFull;
2498
2499 return S_OK;
2500}
2501
2502HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2503{
2504 RT_NOREF(aSettingsFilePath);
2505 ReturnComNotImplemented();
2506}
2507
2508HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2509{
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2513 if (FAILED(rc)) return rc;
2514
2515 if (!mData->pMachineConfigFile->fileExists())
2516 // this is a new machine, and no config file exists yet:
2517 *aSettingsModified = TRUE;
2518 else
2519 *aSettingsModified = (mData->flModifications != 0);
2520
2521 return S_OK;
2522}
2523
2524HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2525{
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 *aSessionState = mData->mSession.mState;
2529
2530 return S_OK;
2531}
2532
2533HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 aSessionName = mData->mSession.mName;
2538
2539 return S_OK;
2540}
2541
2542HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2543{
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 *aSessionPID = mData->mSession.mPID;
2547
2548 return S_OK;
2549}
2550
2551HRESULT Machine::getState(MachineState_T *aState)
2552{
2553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555 *aState = mData->mMachineState;
2556 Assert(mData->mMachineState != MachineState_Null);
2557
2558 return S_OK;
2559}
2560
2561HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2562{
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 aStateFilePath = mSSData->strStateFilePath;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 i_getLogFolder(aLogFolder);
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aCurrentSnapshot = mData->mCurrentSnapshot;
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2602 ? 0
2603 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2604
2605 return S_OK;
2606}
2607
2608HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 /* Note: for machines with no snapshots, we always return FALSE
2613 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2614 * reasons :) */
2615
2616 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2617 ? FALSE
2618 : mData->mCurrentStateModified;
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2624{
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aSharedFolders.resize(mHWData->mSharedFolders.size());
2628 size_t i = 0;
2629 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2630 it = mHWData->mSharedFolders.begin();
2631 it != mHWData->mSharedFolders.end();
2632 ++it, ++i)
2633 aSharedFolders[i] = *it;
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 *aClipboardMode = mHWData->mClipboardMode;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2648{
2649 HRESULT rc = S_OK;
2650
2651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 alock.release();
2654 rc = i_onClipboardModeChange(aClipboardMode);
2655 alock.acquire();
2656 if (FAILED(rc)) return rc;
2657
2658 i_setModified(IsModified_MachineData);
2659 mHWData.backup();
2660 mHWData->mClipboardMode = aClipboardMode;
2661
2662 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2663 if (Global::IsOnline(mData->mMachineState))
2664 i_saveSettings(NULL, alock);
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2679{
2680 HRESULT rc = S_OK;
2681
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 alock.release();
2685 rc = i_onClipboardFileTransferModeChange(aEnabled);
2686 alock.acquire();
2687 if (FAILED(rc)) return rc;
2688
2689 i_setModified(IsModified_MachineData);
2690 mHWData.backup();
2691 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2692
2693 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2694 if (Global::IsOnline(mData->mMachineState))
2695 i_saveSettings(NULL, alock);
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aDnDMode = mHWData->mDnDMode;
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2710{
2711 HRESULT rc = S_OK;
2712
2713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 alock.release();
2716 rc = i_onDnDModeChange(aDnDMode);
2717
2718 alock.acquire();
2719 if (FAILED(rc)) return rc;
2720
2721 i_setModified(IsModified_MachineData);
2722 mHWData.backup();
2723 mHWData->mDnDMode = aDnDMode;
2724
2725 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2726 if (Global::IsOnline(mData->mMachineState))
2727 i_saveSettings(NULL, alock);
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2733{
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 aStorageControllers.resize(mStorageControllers->size());
2737 size_t i = 0;
2738 for (StorageControllerList::const_iterator
2739 it = mStorageControllers->begin();
2740 it != mStorageControllers->end();
2741 ++it, ++i)
2742 aStorageControllers[i] = *it;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aEnabled = mUserData->s.fTeleporterEnabled;
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2757{
2758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 /* Only allow it to be set to true when PoweredOff or Aborted.
2761 (Clearing it is always permitted.) */
2762 if ( aTeleporterEnabled
2763 && mData->mRegistered
2764 && ( !i_isSessionMachine()
2765 || ( mData->mMachineState != MachineState_PoweredOff
2766 && mData->mMachineState != MachineState_Teleported
2767 && mData->mMachineState != MachineState_Aborted
2768 )
2769 )
2770 )
2771 return setError(VBOX_E_INVALID_VM_STATE,
2772 tr("The machine is not powered off (state is %s)"),
2773 Global::stringifyMachineState(mData->mMachineState));
2774
2775 i_setModified(IsModified_MachineData);
2776 mUserData.backup();
2777 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2778
2779 return S_OK;
2780}
2781
2782HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2783{
2784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2787
2788 return S_OK;
2789}
2790
2791HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2792{
2793 if (aTeleporterPort >= _64K)
2794 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2795
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2799 if (FAILED(rc)) return rc;
2800
2801 i_setModified(IsModified_MachineData);
2802 mUserData.backup();
2803 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2818{
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2822 if (FAILED(rc)) return rc;
2823
2824 i_setModified(IsModified_MachineData);
2825 mUserData.backup();
2826 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2827
2828 return S_OK;
2829}
2830
2831HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2840{
2841 /*
2842 * Hash the password first.
2843 */
2844 com::Utf8Str aT = aTeleporterPassword;
2845
2846 if (!aT.isEmpty())
2847 {
2848 if (VBoxIsPasswordHashed(&aT))
2849 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2850 VBoxHashPassword(&aT);
2851 }
2852
2853 /*
2854 * Do the update.
2855 */
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2858 if (SUCCEEDED(hrc))
2859 {
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.strTeleporterPassword = aT;
2863 }
2864
2865 return hrc;
2866}
2867
2868HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2869{
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /* Only allow it to be set to true when PoweredOff or Aborted.
2882 (Clearing it is always permitted.) */
2883 if ( aRTCUseUTC
2884 && mData->mRegistered
2885 && ( !i_isSessionMachine()
2886 || ( mData->mMachineState != MachineState_PoweredOff
2887 && mData->mMachineState != MachineState_Teleported
2888 && mData->mMachineState != MachineState_Aborted
2889 )
2890 )
2891 )
2892 return setError(VBOX_E_INVALID_VM_STATE,
2893 tr("The machine is not powered off (state is %s)"),
2894 Global::stringifyMachineState(mData->mMachineState));
2895
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2913{
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 HRESULT rc = i_checkStateDependency(MutableStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mHWData.backup();
2921 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2922
2923 return S_OK;
2924}
2925
2926HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2927{
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 *aIOCacheSize = mHWData->mIOCacheSize;
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2936{
2937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 HRESULT rc = i_checkStateDependency(MutableStateDep);
2940 if (FAILED(rc)) return rc;
2941
2942 i_setModified(IsModified_MachineData);
2943 mHWData.backup();
2944 mHWData->mIOCacheSize = aIOCacheSize;
2945
2946 return S_OK;
2947}
2948
2949
2950/**
2951 * @note Locks objects!
2952 */
2953HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2954 LockType_T aLockType)
2955{
2956 /* check the session state */
2957 SessionState_T state;
2958 HRESULT rc = aSession->COMGETTER(State)(&state);
2959 if (FAILED(rc)) return rc;
2960
2961 if (state != SessionState_Unlocked)
2962 return setError(VBOX_E_INVALID_OBJECT_STATE,
2963 tr("The given session is busy"));
2964
2965 // get the client's IInternalSessionControl interface
2966 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2967 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2968 E_INVALIDARG);
2969
2970 // session name (only used in some code paths)
2971 Utf8Str strSessionName;
2972
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 if (!mData->mRegistered)
2976 return setError(E_UNEXPECTED,
2977 tr("The machine '%s' is not registered"),
2978 mUserData->s.strName.c_str());
2979
2980 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2981
2982 SessionState_T oldState = mData->mSession.mState;
2983 /* Hack: in case the session is closing and there is a progress object
2984 * which allows waiting for the session to be closed, take the opportunity
2985 * and do a limited wait (max. 1 second). This helps a lot when the system
2986 * is busy and thus session closing can take a little while. */
2987 if ( mData->mSession.mState == SessionState_Unlocking
2988 && mData->mSession.mProgress)
2989 {
2990 alock.release();
2991 mData->mSession.mProgress->WaitForCompletion(1000);
2992 alock.acquire();
2993 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
2994 }
2995
2996 // try again now
2997 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2998 // (i.e. session machine exists)
2999 && (aLockType == LockType_Shared) // caller wants a shared link to the
3000 // existing session that holds the write lock:
3001 )
3002 {
3003 // OK, share the session... we are now dealing with three processes:
3004 // 1) VBoxSVC (where this code runs);
3005 // 2) process C: the caller's client process (who wants a shared session);
3006 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3007
3008 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3009 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3010 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3011 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3012 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3013
3014 /*
3015 * Release the lock before calling the client process. It's safe here
3016 * since the only thing to do after we get the lock again is to add
3017 * the remote control to the list (which doesn't directly influence
3018 * anything).
3019 */
3020 alock.release();
3021
3022 // get the console of the session holding the write lock (this is a remote call)
3023 ComPtr<IConsole> pConsoleW;
3024 if (mData->mSession.mLockType == LockType_VM)
3025 {
3026 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3027 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3028 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3029 if (FAILED(rc))
3030 // the failure may occur w/o any error info (from RPC), so provide one
3031 return setError(VBOX_E_VM_ERROR,
3032 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3033 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3034 }
3035
3036 // share the session machine and W's console with the caller's session
3037 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3038 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3039 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3040
3041 if (FAILED(rc))
3042 // the failure may occur w/o any error info (from RPC), so provide one
3043 return setError(VBOX_E_VM_ERROR,
3044 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3045 alock.acquire();
3046
3047 // need to revalidate the state after acquiring the lock again
3048 if (mData->mSession.mState != SessionState_Locked)
3049 {
3050 pSessionControl->Uninitialize();
3051 return setError(VBOX_E_INVALID_SESSION_STATE,
3052 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3053 mUserData->s.strName.c_str());
3054 }
3055
3056 // add the caller's session to the list
3057 mData->mSession.mRemoteControls.push_back(pSessionControl);
3058 }
3059 else if ( mData->mSession.mState == SessionState_Locked
3060 || mData->mSession.mState == SessionState_Unlocking
3061 )
3062 {
3063 // sharing not permitted, or machine still unlocking:
3064 return setError(VBOX_E_INVALID_OBJECT_STATE,
3065 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3066 mUserData->s.strName.c_str());
3067 }
3068 else
3069 {
3070 // machine is not locked: then write-lock the machine (create the session machine)
3071
3072 // must not be busy
3073 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3074
3075 // get the caller's session PID
3076 RTPROCESS pid = NIL_RTPROCESS;
3077 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3078 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3079 Assert(pid != NIL_RTPROCESS);
3080
3081 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3082
3083 if (fLaunchingVMProcess)
3084 {
3085 if (mData->mSession.mPID == NIL_RTPROCESS)
3086 {
3087 // two or more clients racing for a lock, the one which set the
3088 // session state to Spawning will win, the others will get an
3089 // error as we can't decide here if waiting a little would help
3090 // (only for shared locks this would avoid an error)
3091 return setError(VBOX_E_INVALID_OBJECT_STATE,
3092 tr("The machine '%s' already has a lock request pending"),
3093 mUserData->s.strName.c_str());
3094 }
3095
3096 // this machine is awaiting for a spawning session to be opened:
3097 // then the calling process must be the one that got started by
3098 // LaunchVMProcess()
3099
3100 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3101 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3102
3103#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3104 /* Hardened windows builds spawns three processes when a VM is
3105 launched, the 3rd one is the one that will end up here. */
3106 RTPROCESS pidParent;
3107 int vrc = RTProcQueryParent(pid, &pidParent);
3108 if (RT_SUCCESS(vrc))
3109 vrc = RTProcQueryParent(pidParent, &pidParent);
3110 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3111 || vrc == VERR_ACCESS_DENIED)
3112 {
3113 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3114 mData->mSession.mPID = pid;
3115 }
3116#endif
3117
3118 if (mData->mSession.mPID != pid)
3119 return setError(E_ACCESSDENIED,
3120 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3121 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3122 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3123 }
3124
3125 // create the mutable SessionMachine from the current machine
3126 ComObjPtr<SessionMachine> sessionMachine;
3127 sessionMachine.createObject();
3128 rc = sessionMachine->init(this);
3129 AssertComRC(rc);
3130
3131 /* NOTE: doing return from this function after this point but
3132 * before the end is forbidden since it may call SessionMachine::uninit()
3133 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3134 * lock while still holding the Machine lock in alock so that a deadlock
3135 * is possible due to the wrong lock order. */
3136
3137 if (SUCCEEDED(rc))
3138 {
3139 /*
3140 * Set the session state to Spawning to protect against subsequent
3141 * attempts to open a session and to unregister the machine after
3142 * we release the lock.
3143 */
3144 SessionState_T origState = mData->mSession.mState;
3145 mData->mSession.mState = SessionState_Spawning;
3146
3147#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3148 /* Get the client token ID to be passed to the client process */
3149 Utf8Str strTokenId;
3150 sessionMachine->i_getTokenId(strTokenId);
3151 Assert(!strTokenId.isEmpty());
3152#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3153 /* Get the client token to be passed to the client process */
3154 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3155 /* The token is now "owned" by pToken, fix refcount */
3156 if (!pToken.isNull())
3157 pToken->Release();
3158#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3159
3160 /*
3161 * Release the lock before calling the client process -- it will call
3162 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3163 * because the state is Spawning, so that LaunchVMProcess() and
3164 * LockMachine() calls will fail. This method, called before we
3165 * acquire the lock again, will fail because of the wrong PID.
3166 *
3167 * Note that mData->mSession.mRemoteControls accessed outside
3168 * the lock may not be modified when state is Spawning, so it's safe.
3169 */
3170 alock.release();
3171
3172 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3173#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3174 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3175#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3176 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3177 /* Now the token is owned by the client process. */
3178 pToken.setNull();
3179#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3180 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3181
3182 /* The failure may occur w/o any error info (from RPC), so provide one */
3183 if (FAILED(rc))
3184 setError(VBOX_E_VM_ERROR,
3185 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3186
3187 // get session name, either to remember or to compare against
3188 // the already known session name.
3189 {
3190 Bstr bstrSessionName;
3191 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3192 if (SUCCEEDED(rc2))
3193 strSessionName = bstrSessionName;
3194 }
3195
3196 if ( SUCCEEDED(rc)
3197 && fLaunchingVMProcess
3198 )
3199 {
3200 /* complete the remote session initialization */
3201
3202 /* get the console from the direct session */
3203 ComPtr<IConsole> console;
3204 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3205 ComAssertComRC(rc);
3206
3207 if (SUCCEEDED(rc) && !console)
3208 {
3209 ComAssert(!!console);
3210 rc = E_FAIL;
3211 }
3212
3213 /* assign machine & console to the remote session */
3214 if (SUCCEEDED(rc))
3215 {
3216 /*
3217 * after LaunchVMProcess(), the first and the only
3218 * entry in remoteControls is that remote session
3219 */
3220 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3221 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3222 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3223
3224 /* The failure may occur w/o any error info (from RPC), so provide one */
3225 if (FAILED(rc))
3226 setError(VBOX_E_VM_ERROR,
3227 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3228 }
3229
3230 if (FAILED(rc))
3231 pSessionControl->Uninitialize();
3232 }
3233
3234 /* acquire the lock again */
3235 alock.acquire();
3236
3237 /* Restore the session state */
3238 mData->mSession.mState = origState;
3239 }
3240
3241 // finalize spawning anyway (this is why we don't return on errors above)
3242 if (fLaunchingVMProcess)
3243 {
3244 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3245 /* Note that the progress object is finalized later */
3246 /** @todo Consider checking mData->mSession.mProgress for cancellation
3247 * around here. */
3248
3249 /* We don't reset mSession.mPID here because it is necessary for
3250 * SessionMachine::uninit() to reap the child process later. */
3251
3252 if (FAILED(rc))
3253 {
3254 /* Close the remote session, remove the remote control from the list
3255 * and reset session state to Closed (@note keep the code in sync
3256 * with the relevant part in checkForSpawnFailure()). */
3257
3258 Assert(mData->mSession.mRemoteControls.size() == 1);
3259 if (mData->mSession.mRemoteControls.size() == 1)
3260 {
3261 ErrorInfoKeeper eik;
3262 mData->mSession.mRemoteControls.front()->Uninitialize();
3263 }
3264
3265 mData->mSession.mRemoteControls.clear();
3266 mData->mSession.mState = SessionState_Unlocked;
3267 }
3268 }
3269 else
3270 {
3271 /* memorize PID of the directly opened session */
3272 if (SUCCEEDED(rc))
3273 mData->mSession.mPID = pid;
3274 }
3275
3276 if (SUCCEEDED(rc))
3277 {
3278 mData->mSession.mLockType = aLockType;
3279 /* memorize the direct session control and cache IUnknown for it */
3280 mData->mSession.mDirectControl = pSessionControl;
3281 mData->mSession.mState = SessionState_Locked;
3282 if (!fLaunchingVMProcess)
3283 mData->mSession.mName = strSessionName;
3284 /* associate the SessionMachine with this Machine */
3285 mData->mSession.mMachine = sessionMachine;
3286
3287 /* request an IUnknown pointer early from the remote party for later
3288 * identity checks (it will be internally cached within mDirectControl
3289 * at least on XPCOM) */
3290 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3291 NOREF(unk);
3292 }
3293
3294 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3295 * would break the lock order */
3296 alock.release();
3297
3298 /* uninitialize the created session machine on failure */
3299 if (FAILED(rc))
3300 sessionMachine->uninit();
3301 }
3302
3303 if (SUCCEEDED(rc))
3304 {
3305 /*
3306 * tell the client watcher thread to update the set of
3307 * machines that have open sessions
3308 */
3309 mParent->i_updateClientWatcher();
3310
3311 if (oldState != SessionState_Locked)
3312 /* fire an event */
3313 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3314 }
3315
3316 return rc;
3317}
3318
3319/**
3320 * @note Locks objects!
3321 */
3322HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3323 const com::Utf8Str &aName,
3324 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3325 ComPtr<IProgress> &aProgress)
3326{
3327 Utf8Str strFrontend(aName);
3328 /* "emergencystop" doesn't need the session, so skip the checks/interface
3329 * retrieval. This code doesn't quite fit in here, but introducing a
3330 * special API method would be even more effort, and would require explicit
3331 * support by every API client. It's better to hide the feature a bit. */
3332 if (strFrontend != "emergencystop")
3333 CheckComArgNotNull(aSession);
3334
3335 HRESULT rc = S_OK;
3336 if (strFrontend.isEmpty())
3337 {
3338 Bstr bstrFrontend;
3339 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3340 if (FAILED(rc))
3341 return rc;
3342 strFrontend = bstrFrontend;
3343 if (strFrontend.isEmpty())
3344 {
3345 ComPtr<ISystemProperties> systemProperties;
3346 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3347 if (FAILED(rc))
3348 return rc;
3349 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3350 if (FAILED(rc))
3351 return rc;
3352 strFrontend = bstrFrontend;
3353 }
3354 /* paranoia - emergencystop is not a valid default */
3355 if (strFrontend == "emergencystop")
3356 strFrontend = Utf8Str::Empty;
3357 }
3358 /* default frontend: Qt GUI */
3359 if (strFrontend.isEmpty())
3360 strFrontend = "GUI/Qt";
3361
3362 if (strFrontend != "emergencystop")
3363 {
3364 /* check the session state */
3365 SessionState_T state;
3366 rc = aSession->COMGETTER(State)(&state);
3367 if (FAILED(rc))
3368 return rc;
3369
3370 if (state != SessionState_Unlocked)
3371 return setError(VBOX_E_INVALID_OBJECT_STATE,
3372 tr("The given session is busy"));
3373
3374 /* get the IInternalSessionControl interface */
3375 ComPtr<IInternalSessionControl> control(aSession);
3376 ComAssertMsgRet(!control.isNull(),
3377 ("No IInternalSessionControl interface"),
3378 E_INVALIDARG);
3379
3380 /* get the teleporter enable state for the progress object init. */
3381 BOOL fTeleporterEnabled;
3382 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3383 if (FAILED(rc))
3384 return rc;
3385
3386 /* create a progress object */
3387 ComObjPtr<ProgressProxy> progress;
3388 progress.createObject();
3389 rc = progress->init(mParent,
3390 static_cast<IMachine*>(this),
3391 Bstr(tr("Starting VM")).raw(),
3392 TRUE /* aCancelable */,
3393 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3394 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3395 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3396 2 /* uFirstOperationWeight */,
3397 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3398
3399 if (SUCCEEDED(rc))
3400 {
3401 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3402 if (SUCCEEDED(rc))
3403 {
3404 aProgress = progress;
3405
3406 /* signal the client watcher thread */
3407 mParent->i_updateClientWatcher();
3408
3409 /* fire an event */
3410 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3411 }
3412 }
3413 }
3414 else
3415 {
3416 /* no progress object - either instant success or failure */
3417 aProgress = NULL;
3418
3419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3420
3421 if (mData->mSession.mState != SessionState_Locked)
3422 return setError(VBOX_E_INVALID_OBJECT_STATE,
3423 tr("The machine '%s' is not locked by a session"),
3424 mUserData->s.strName.c_str());
3425
3426 /* must have a VM process associated - do not kill normal API clients
3427 * with an open session */
3428 if (!Global::IsOnline(mData->mMachineState))
3429 return setError(VBOX_E_INVALID_OBJECT_STATE,
3430 tr("The machine '%s' does not have a VM process"),
3431 mUserData->s.strName.c_str());
3432
3433 /* forcibly terminate the VM process */
3434 if (mData->mSession.mPID != NIL_RTPROCESS)
3435 RTProcTerminate(mData->mSession.mPID);
3436
3437 /* signal the client watcher thread, as most likely the client has
3438 * been terminated */
3439 mParent->i_updateClientWatcher();
3440 }
3441
3442 return rc;
3443}
3444
3445HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3446{
3447 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3448 return setError(E_INVALIDARG,
3449 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3450 aPosition, SchemaDefs::MaxBootPosition);
3451
3452 if (aDevice == DeviceType_USB)
3453 return setError(E_NOTIMPL,
3454 tr("Booting from USB device is currently not supported"));
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 HRESULT rc = i_checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 i_setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mBootOrder[aPosition - 1] = aDevice;
3464
3465 return S_OK;
3466}
3467
3468HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3469{
3470 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3471 return setError(E_INVALIDARG,
3472 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3473 aPosition, SchemaDefs::MaxBootPosition);
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aDevice = mHWData->mBootOrder[aPosition - 1];
3478
3479 return S_OK;
3480}
3481
3482HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3483 LONG aControllerPort,
3484 LONG aDevice,
3485 DeviceType_T aType,
3486 const ComPtr<IMedium> &aMedium)
3487{
3488 IMedium *aM = aMedium;
3489 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3490 aName.c_str(), aControllerPort, aDevice, aType, aM));
3491
3492 // request the host lock first, since might be calling Host methods for getting host drives;
3493 // next, protect the media tree all the while we're in here, as well as our member variables
3494 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3495 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3496
3497 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3498 if (FAILED(rc)) return rc;
3499
3500 /// @todo NEWMEDIA implicit machine registration
3501 if (!mData->mRegistered)
3502 return setError(VBOX_E_INVALID_OBJECT_STATE,
3503 tr("Cannot attach storage devices to an unregistered machine"));
3504
3505 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3506
3507 /* Check for an existing controller. */
3508 ComObjPtr<StorageController> ctl;
3509 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3510 if (FAILED(rc)) return rc;
3511
3512 StorageControllerType_T ctrlType;
3513 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3514 if (FAILED(rc))
3515 return setError(E_FAIL,
3516 tr("Could not get type of controller '%s'"),
3517 aName.c_str());
3518
3519 bool fSilent = false;
3520 Utf8Str strReconfig;
3521
3522 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3523 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3524 if ( mData->mMachineState == MachineState_Paused
3525 && strReconfig == "1")
3526 fSilent = true;
3527
3528 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3529 bool fHotplug = false;
3530 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3531 fHotplug = true;
3532
3533 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3534 return setError(VBOX_E_INVALID_VM_STATE,
3535 tr("Controller '%s' does not support hotplugging"),
3536 aName.c_str());
3537
3538 // check that the port and device are not out of range
3539 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3540 if (FAILED(rc)) return rc;
3541
3542 /* check if the device slot is already busy */
3543 MediumAttachment *pAttachTemp;
3544 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3545 aName,
3546 aControllerPort,
3547 aDevice)))
3548 {
3549 Medium *pMedium = pAttachTemp->i_getMedium();
3550 if (pMedium)
3551 {
3552 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3553 return setError(VBOX_E_OBJECT_IN_USE,
3554 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3555 pMedium->i_getLocationFull().c_str(),
3556 aControllerPort,
3557 aDevice,
3558 aName.c_str());
3559 }
3560 else
3561 return setError(VBOX_E_OBJECT_IN_USE,
3562 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3563 aControllerPort, aDevice, aName.c_str());
3564 }
3565
3566 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3567 if (aMedium && medium.isNull())
3568 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3569
3570 AutoCaller mediumCaller(medium);
3571 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3572
3573 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3574
3575 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3576 && !medium.isNull()
3577 && ( medium->i_getType() != MediumType_Readonly
3578 || medium->i_getDeviceType() != DeviceType_DVD)
3579 )
3580 return setError(VBOX_E_OBJECT_IN_USE,
3581 tr("Medium '%s' is already attached to this virtual machine"),
3582 medium->i_getLocationFull().c_str());
3583
3584 if (!medium.isNull())
3585 {
3586 MediumType_T mtype = medium->i_getType();
3587 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3588 // For DVDs it's not written to the config file, so needs no global config
3589 // version bump. For floppies it's a new attribute "type", which is ignored
3590 // by older VirtualBox version, so needs no global config version bump either.
3591 // For hard disks this type is not accepted.
3592 if (mtype == MediumType_MultiAttach)
3593 {
3594 // This type is new with VirtualBox 4.0 and therefore requires settings
3595 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3596 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3597 // two reasons: The medium type is a property of the media registry tree, which
3598 // can reside in the global config file (for pre-4.0 media); we would therefore
3599 // possibly need to bump the global config version. We don't want to do that though
3600 // because that might make downgrading to pre-4.0 impossible.
3601 // As a result, we can only use these two new types if the medium is NOT in the
3602 // global registry:
3603 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3604 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3605 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3606 )
3607 return setError(VBOX_E_INVALID_OBJECT_STATE,
3608 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3609 "to machines that were created with VirtualBox 4.0 or later"),
3610 medium->i_getLocationFull().c_str());
3611 }
3612 }
3613
3614 bool fIndirect = false;
3615 if (!medium.isNull())
3616 fIndirect = medium->i_isReadOnly();
3617 bool associate = true;
3618
3619 do
3620 {
3621 if ( aType == DeviceType_HardDisk
3622 && mMediumAttachments.isBackedUp())
3623 {
3624 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3625
3626 /* check if the medium was attached to the VM before we started
3627 * changing attachments in which case the attachment just needs to
3628 * be restored */
3629 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3630 {
3631 AssertReturn(!fIndirect, E_FAIL);
3632
3633 /* see if it's the same bus/channel/device */
3634 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3635 {
3636 /* the simplest case: restore the whole attachment
3637 * and return, nothing else to do */
3638 mMediumAttachments->push_back(pAttachTemp);
3639
3640 /* Reattach the medium to the VM. */
3641 if (fHotplug || fSilent)
3642 {
3643 mediumLock.release();
3644 treeLock.release();
3645 alock.release();
3646
3647 MediumLockList *pMediumLockList(new MediumLockList());
3648
3649 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3650 medium /* pToLockWrite */,
3651 false /* fMediumLockWriteAll */,
3652 NULL,
3653 *pMediumLockList);
3654 alock.acquire();
3655 if (FAILED(rc))
3656 delete pMediumLockList;
3657 else
3658 {
3659 mData->mSession.mLockedMedia.Unlock();
3660 alock.release();
3661 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3662 mData->mSession.mLockedMedia.Lock();
3663 alock.acquire();
3664 }
3665 alock.release();
3666
3667 if (SUCCEEDED(rc))
3668 {
3669 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3670 /* Remove lock list in case of error. */
3671 if (FAILED(rc))
3672 {
3673 mData->mSession.mLockedMedia.Unlock();
3674 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3675 mData->mSession.mLockedMedia.Lock();
3676 }
3677 }
3678 }
3679
3680 return S_OK;
3681 }
3682
3683 /* bus/channel/device differ; we need a new attachment object,
3684 * but don't try to associate it again */
3685 associate = false;
3686 break;
3687 }
3688 }
3689
3690 /* go further only if the attachment is to be indirect */
3691 if (!fIndirect)
3692 break;
3693
3694 /* perform the so called smart attachment logic for indirect
3695 * attachments. Note that smart attachment is only applicable to base
3696 * hard disks. */
3697
3698 if (medium->i_getParent().isNull())
3699 {
3700 /* first, investigate the backup copy of the current hard disk
3701 * attachments to make it possible to re-attach existing diffs to
3702 * another device slot w/o losing their contents */
3703 if (mMediumAttachments.isBackedUp())
3704 {
3705 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3706
3707 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3708 uint32_t foundLevel = 0;
3709
3710 for (MediumAttachmentList::const_iterator
3711 it = oldAtts.begin();
3712 it != oldAtts.end();
3713 ++it)
3714 {
3715 uint32_t level = 0;
3716 MediumAttachment *pAttach = *it;
3717 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3718 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3719 if (pMedium.isNull())
3720 continue;
3721
3722 if (pMedium->i_getBase(&level) == medium)
3723 {
3724 /* skip the hard disk if its currently attached (we
3725 * cannot attach the same hard disk twice) */
3726 if (i_findAttachment(*mMediumAttachments.data(),
3727 pMedium))
3728 continue;
3729
3730 /* matched device, channel and bus (i.e. attached to the
3731 * same place) will win and immediately stop the search;
3732 * otherwise the attachment that has the youngest
3733 * descendant of medium will be used
3734 */
3735 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3736 {
3737 /* the simplest case: restore the whole attachment
3738 * and return, nothing else to do */
3739 mMediumAttachments->push_back(*it);
3740
3741 /* Reattach the medium to the VM. */
3742 if (fHotplug || fSilent)
3743 {
3744 mediumLock.release();
3745 treeLock.release();
3746 alock.release();
3747
3748 MediumLockList *pMediumLockList(new MediumLockList());
3749
3750 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3751 medium /* pToLockWrite */,
3752 false /* fMediumLockWriteAll */,
3753 NULL,
3754 *pMediumLockList);
3755 alock.acquire();
3756 if (FAILED(rc))
3757 delete pMediumLockList;
3758 else
3759 {
3760 mData->mSession.mLockedMedia.Unlock();
3761 alock.release();
3762 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3763 mData->mSession.mLockedMedia.Lock();
3764 alock.acquire();
3765 }
3766 alock.release();
3767
3768 if (SUCCEEDED(rc))
3769 {
3770 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3771 /* Remove lock list in case of error. */
3772 if (FAILED(rc))
3773 {
3774 mData->mSession.mLockedMedia.Unlock();
3775 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3776 mData->mSession.mLockedMedia.Lock();
3777 }
3778 }
3779 }
3780
3781 return S_OK;
3782 }
3783 else if ( foundIt == oldAtts.end()
3784 || level > foundLevel /* prefer younger */
3785 )
3786 {
3787 foundIt = it;
3788 foundLevel = level;
3789 }
3790 }
3791 }
3792
3793 if (foundIt != oldAtts.end())
3794 {
3795 /* use the previously attached hard disk */
3796 medium = (*foundIt)->i_getMedium();
3797 mediumCaller.attach(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799 mediumLock.attach(medium);
3800 /* not implicit, doesn't require association with this VM */
3801 fIndirect = false;
3802 associate = false;
3803 /* go right to the MediumAttachment creation */
3804 break;
3805 }
3806 }
3807
3808 /* must give up the medium lock and medium tree lock as below we
3809 * go over snapshots, which needs a lock with higher lock order. */
3810 mediumLock.release();
3811 treeLock.release();
3812
3813 /* then, search through snapshots for the best diff in the given
3814 * hard disk's chain to base the new diff on */
3815
3816 ComObjPtr<Medium> base;
3817 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3818 while (snap)
3819 {
3820 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3821
3822 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3823
3824 MediumAttachment *pAttachFound = NULL;
3825 uint32_t foundLevel = 0;
3826
3827 for (MediumAttachmentList::const_iterator
3828 it = snapAtts.begin();
3829 it != snapAtts.end();
3830 ++it)
3831 {
3832 MediumAttachment *pAttach = *it;
3833 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3834 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3835 if (pMedium.isNull())
3836 continue;
3837
3838 uint32_t level = 0;
3839 if (pMedium->i_getBase(&level) == medium)
3840 {
3841 /* matched device, channel and bus (i.e. attached to the
3842 * same place) will win and immediately stop the search;
3843 * otherwise the attachment that has the youngest
3844 * descendant of medium will be used
3845 */
3846 if ( pAttach->i_getDevice() == aDevice
3847 && pAttach->i_getPort() == aControllerPort
3848 && pAttach->i_getControllerName() == aName
3849 )
3850 {
3851 pAttachFound = pAttach;
3852 break;
3853 }
3854 else if ( !pAttachFound
3855 || level > foundLevel /* prefer younger */
3856 )
3857 {
3858 pAttachFound = pAttach;
3859 foundLevel = level;
3860 }
3861 }
3862 }
3863
3864 if (pAttachFound)
3865 {
3866 base = pAttachFound->i_getMedium();
3867 break;
3868 }
3869
3870 snap = snap->i_getParent();
3871 }
3872
3873 /* re-lock medium tree and the medium, as we need it below */
3874 treeLock.acquire();
3875 mediumLock.acquire();
3876
3877 /* found a suitable diff, use it as a base */
3878 if (!base.isNull())
3879 {
3880 medium = base;
3881 mediumCaller.attach(medium);
3882 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3883 mediumLock.attach(medium);
3884 }
3885 }
3886
3887 Utf8Str strFullSnapshotFolder;
3888 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3889
3890 ComObjPtr<Medium> diff;
3891 diff.createObject();
3892 // store this diff in the same registry as the parent
3893 Guid uuidRegistryParent;
3894 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3895 {
3896 // parent image has no registry: this can happen if we're attaching a new immutable
3897 // image that has not yet been attached (medium then points to the base and we're
3898 // creating the diff image for the immutable, and the parent is not yet registered);
3899 // put the parent in the machine registry then
3900 mediumLock.release();
3901 treeLock.release();
3902 alock.release();
3903 i_addMediumToRegistry(medium);
3904 alock.acquire();
3905 treeLock.acquire();
3906 mediumLock.acquire();
3907 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3908 }
3909 rc = diff->init(mParent,
3910 medium->i_getPreferredDiffFormat(),
3911 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3912 uuidRegistryParent,
3913 DeviceType_HardDisk);
3914 if (FAILED(rc)) return rc;
3915
3916 /* Apply the normal locking logic to the entire chain. */
3917 MediumLockList *pMediumLockList(new MediumLockList());
3918 mediumLock.release();
3919 treeLock.release();
3920 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3921 diff /* pToLockWrite */,
3922 false /* fMediumLockWriteAll */,
3923 medium,
3924 *pMediumLockList);
3925 treeLock.acquire();
3926 mediumLock.acquire();
3927 if (SUCCEEDED(rc))
3928 {
3929 mediumLock.release();
3930 treeLock.release();
3931 rc = pMediumLockList->Lock();
3932 treeLock.acquire();
3933 mediumLock.acquire();
3934 if (FAILED(rc))
3935 setError(rc,
3936 tr("Could not lock medium when creating diff '%s'"),
3937 diff->i_getLocationFull().c_str());
3938 else
3939 {
3940 /* will release the lock before the potentially lengthy
3941 * operation, so protect with the special state */
3942 MachineState_T oldState = mData->mMachineState;
3943 i_setMachineState(MachineState_SettingUp);
3944
3945 mediumLock.release();
3946 treeLock.release();
3947 alock.release();
3948
3949 rc = medium->i_createDiffStorage(diff,
3950 medium->i_getPreferredDiffVariant(),
3951 pMediumLockList,
3952 NULL /* aProgress */,
3953 true /* aWait */,
3954 false /* aNotify */);
3955
3956 alock.acquire();
3957 treeLock.acquire();
3958 mediumLock.acquire();
3959
3960 i_setMachineState(oldState);
3961 }
3962 }
3963
3964 /* Unlock the media and free the associated memory. */
3965 delete pMediumLockList;
3966
3967 if (FAILED(rc)) return rc;
3968
3969 /* use the created diff for the actual attachment */
3970 medium = diff;
3971 mediumCaller.attach(medium);
3972 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3973 mediumLock.attach(medium);
3974 }
3975 while (0);
3976
3977 ComObjPtr<MediumAttachment> attachment;
3978 attachment.createObject();
3979 rc = attachment->init(this,
3980 medium,
3981 aName,
3982 aControllerPort,
3983 aDevice,
3984 aType,
3985 fIndirect,
3986 false /* fPassthrough */,
3987 false /* fTempEject */,
3988 false /* fNonRotational */,
3989 false /* fDiscard */,
3990 fHotplug /* fHotPluggable */,
3991 Utf8Str::Empty);
3992 if (FAILED(rc)) return rc;
3993
3994 if (associate && !medium.isNull())
3995 {
3996 // as the last step, associate the medium to the VM
3997 rc = medium->i_addBackReference(mData->mUuid);
3998 // here we can fail because of Deleting, or being in process of creating a Diff
3999 if (FAILED(rc)) return rc;
4000
4001 mediumLock.release();
4002 treeLock.release();
4003 alock.release();
4004 i_addMediumToRegistry(medium);
4005 alock.acquire();
4006 treeLock.acquire();
4007 mediumLock.acquire();
4008 }
4009
4010 /* success: finally remember the attachment */
4011 i_setModified(IsModified_Storage);
4012 mMediumAttachments.backup();
4013 mMediumAttachments->push_back(attachment);
4014
4015 mediumLock.release();
4016 treeLock.release();
4017 alock.release();
4018
4019 if (fHotplug || fSilent)
4020 {
4021 if (!medium.isNull())
4022 {
4023 MediumLockList *pMediumLockList(new MediumLockList());
4024
4025 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4026 medium /* pToLockWrite */,
4027 false /* fMediumLockWriteAll */,
4028 NULL,
4029 *pMediumLockList);
4030 alock.acquire();
4031 if (FAILED(rc))
4032 delete pMediumLockList;
4033 else
4034 {
4035 mData->mSession.mLockedMedia.Unlock();
4036 alock.release();
4037 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4038 mData->mSession.mLockedMedia.Lock();
4039 alock.acquire();
4040 }
4041 alock.release();
4042 }
4043
4044 if (SUCCEEDED(rc))
4045 {
4046 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4047 /* Remove lock list in case of error. */
4048 if (FAILED(rc))
4049 {
4050 mData->mSession.mLockedMedia.Unlock();
4051 mData->mSession.mLockedMedia.Remove(attachment);
4052 mData->mSession.mLockedMedia.Lock();
4053 }
4054 }
4055 }
4056
4057 /* Save modified registries, but skip this machine as it's the caller's
4058 * job to save its settings like all other settings changes. */
4059 mParent->i_unmarkRegistryModified(i_getId());
4060 mParent->i_saveModifiedRegistries();
4061
4062 if (SUCCEEDED(rc))
4063 {
4064 if (fIndirect && medium != aM)
4065 mParent->i_onMediumConfigChanged(medium);
4066 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4067 }
4068
4069 return rc;
4070}
4071
4072HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4073 LONG aDevice)
4074{
4075 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4076 aName.c_str(), aControllerPort, aDevice));
4077
4078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4079
4080 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4081 if (FAILED(rc)) return rc;
4082
4083 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4084
4085 /* Check for an existing controller. */
4086 ComObjPtr<StorageController> ctl;
4087 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4088 if (FAILED(rc)) return rc;
4089
4090 StorageControllerType_T ctrlType;
4091 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4092 if (FAILED(rc))
4093 return setError(E_FAIL,
4094 tr("Could not get type of controller '%s'"),
4095 aName.c_str());
4096
4097 bool fSilent = false;
4098 Utf8Str strReconfig;
4099
4100 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4101 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4102 if ( mData->mMachineState == MachineState_Paused
4103 && strReconfig == "1")
4104 fSilent = true;
4105
4106 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4107 bool fHotplug = false;
4108 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4109 fHotplug = true;
4110
4111 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4112 return setError(VBOX_E_INVALID_VM_STATE,
4113 tr("Controller '%s' does not support hotplugging"),
4114 aName.c_str());
4115
4116 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4117 aName,
4118 aControllerPort,
4119 aDevice);
4120 if (!pAttach)
4121 return setError(VBOX_E_OBJECT_NOT_FOUND,
4122 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4123 aDevice, aControllerPort, aName.c_str());
4124
4125 if (fHotplug && !pAttach->i_getHotPluggable())
4126 return setError(VBOX_E_NOT_SUPPORTED,
4127 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4128 aDevice, aControllerPort, aName.c_str());
4129
4130 /*
4131 * The VM has to detach the device before we delete any implicit diffs.
4132 * If this fails we can roll back without loosing data.
4133 */
4134 if (fHotplug || fSilent)
4135 {
4136 alock.release();
4137 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4138 alock.acquire();
4139 }
4140 if (FAILED(rc)) return rc;
4141
4142 /* If we are here everything went well and we can delete the implicit now. */
4143 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4144
4145 alock.release();
4146
4147 /* Save modified registries, but skip this machine as it's the caller's
4148 * job to save its settings like all other settings changes. */
4149 mParent->i_unmarkRegistryModified(i_getId());
4150 mParent->i_saveModifiedRegistries();
4151
4152 if (SUCCEEDED(rc))
4153 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4154
4155 return rc;
4156}
4157
4158HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4159 LONG aDevice, BOOL aPassthrough)
4160{
4161 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4162 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4163
4164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4165
4166 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4167 if (FAILED(rc)) return rc;
4168
4169 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4170
4171 /* Check for an existing controller. */
4172 ComObjPtr<StorageController> ctl;
4173 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4174 if (FAILED(rc)) return rc;
4175
4176 StorageControllerType_T ctrlType;
4177 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4178 if (FAILED(rc))
4179 return setError(E_FAIL,
4180 tr("Could not get type of controller '%s'"),
4181 aName.c_str());
4182
4183 bool fSilent = false;
4184 Utf8Str strReconfig;
4185
4186 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4187 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4188 if ( mData->mMachineState == MachineState_Paused
4189 && strReconfig == "1")
4190 fSilent = true;
4191
4192 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4193 bool fHotplug = false;
4194 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4195 fHotplug = true;
4196
4197 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4198 return setError(VBOX_E_INVALID_VM_STATE,
4199 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4200 aName.c_str());
4201
4202 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4203 aName,
4204 aControllerPort,
4205 aDevice);
4206 if (!pAttach)
4207 return setError(VBOX_E_OBJECT_NOT_FOUND,
4208 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4209 aDevice, aControllerPort, aName.c_str());
4210
4211
4212 i_setModified(IsModified_Storage);
4213 mMediumAttachments.backup();
4214
4215 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4216
4217 if (pAttach->i_getType() != DeviceType_DVD)
4218 return setError(E_INVALIDARG,
4219 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4220 aDevice, aControllerPort, aName.c_str());
4221
4222 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4223
4224 pAttach->i_updatePassthrough(!!aPassthrough);
4225
4226 attLock.release();
4227 alock.release();
4228 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4229 if (SUCCEEDED(rc) && fValueChanged)
4230 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4231
4232 return rc;
4233}
4234
4235HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4236 LONG aDevice, BOOL aTemporaryEject)
4237{
4238
4239 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4240 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4241
4242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4243
4244 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4245 if (FAILED(rc)) return rc;
4246
4247 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4248 aName,
4249 aControllerPort,
4250 aDevice);
4251 if (!pAttach)
4252 return setError(VBOX_E_OBJECT_NOT_FOUND,
4253 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4254 aDevice, aControllerPort, aName.c_str());
4255
4256
4257 i_setModified(IsModified_Storage);
4258 mMediumAttachments.backup();
4259
4260 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4261
4262 if (pAttach->i_getType() != DeviceType_DVD)
4263 return setError(E_INVALIDARG,
4264 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4265 aDevice, aControllerPort, aName.c_str());
4266 pAttach->i_updateTempEject(!!aTemporaryEject);
4267
4268 return S_OK;
4269}
4270
4271HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4272 LONG aDevice, BOOL aNonRotational)
4273{
4274
4275 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4276 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4277
4278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4279
4280 HRESULT rc = i_checkStateDependency(MutableStateDep);
4281 if (FAILED(rc)) return rc;
4282
4283 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4284
4285 if (Global::IsOnlineOrTransient(mData->mMachineState))
4286 return setError(VBOX_E_INVALID_VM_STATE,
4287 tr("Invalid machine state: %s"),
4288 Global::stringifyMachineState(mData->mMachineState));
4289
4290 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4291 aName,
4292 aControllerPort,
4293 aDevice);
4294 if (!pAttach)
4295 return setError(VBOX_E_OBJECT_NOT_FOUND,
4296 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4297 aDevice, aControllerPort, aName.c_str());
4298
4299
4300 i_setModified(IsModified_Storage);
4301 mMediumAttachments.backup();
4302
4303 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4304
4305 if (pAttach->i_getType() != DeviceType_HardDisk)
4306 return setError(E_INVALIDARG,
4307 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"),
4308 aDevice, aControllerPort, aName.c_str());
4309 pAttach->i_updateNonRotational(!!aNonRotational);
4310
4311 return S_OK;
4312}
4313
4314HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4315 LONG aDevice, BOOL aDiscard)
4316{
4317
4318 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4319 aName.c_str(), aControllerPort, aDevice, aDiscard));
4320
4321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4322
4323 HRESULT rc = i_checkStateDependency(MutableStateDep);
4324 if (FAILED(rc)) return rc;
4325
4326 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4327
4328 if (Global::IsOnlineOrTransient(mData->mMachineState))
4329 return setError(VBOX_E_INVALID_VM_STATE,
4330 tr("Invalid machine state: %s"),
4331 Global::stringifyMachineState(mData->mMachineState));
4332
4333 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4334 aName,
4335 aControllerPort,
4336 aDevice);
4337 if (!pAttach)
4338 return setError(VBOX_E_OBJECT_NOT_FOUND,
4339 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4340 aDevice, aControllerPort, aName.c_str());
4341
4342
4343 i_setModified(IsModified_Storage);
4344 mMediumAttachments.backup();
4345
4346 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4347
4348 if (pAttach->i_getType() != DeviceType_HardDisk)
4349 return setError(E_INVALIDARG,
4350 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"),
4351 aDevice, aControllerPort, aName.c_str());
4352 pAttach->i_updateDiscard(!!aDiscard);
4353
4354 return S_OK;
4355}
4356
4357HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4358 LONG aDevice, BOOL aHotPluggable)
4359{
4360 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4361 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4362
4363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4364
4365 HRESULT rc = i_checkStateDependency(MutableStateDep);
4366 if (FAILED(rc)) return rc;
4367
4368 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4369
4370 if (Global::IsOnlineOrTransient(mData->mMachineState))
4371 return setError(VBOX_E_INVALID_VM_STATE,
4372 tr("Invalid machine state: %s"),
4373 Global::stringifyMachineState(mData->mMachineState));
4374
4375 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4376 aName,
4377 aControllerPort,
4378 aDevice);
4379 if (!pAttach)
4380 return setError(VBOX_E_OBJECT_NOT_FOUND,
4381 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4382 aDevice, aControllerPort, aName.c_str());
4383
4384 /* Check for an existing controller. */
4385 ComObjPtr<StorageController> ctl;
4386 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4387 if (FAILED(rc)) return rc;
4388
4389 StorageControllerType_T ctrlType;
4390 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4391 if (FAILED(rc))
4392 return setError(E_FAIL,
4393 tr("Could not get type of controller '%s'"),
4394 aName.c_str());
4395
4396 if (!i_isControllerHotplugCapable(ctrlType))
4397 return setError(VBOX_E_NOT_SUPPORTED,
4398 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4399 aName.c_str());
4400
4401 i_setModified(IsModified_Storage);
4402 mMediumAttachments.backup();
4403
4404 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4405
4406 if (pAttach->i_getType() == DeviceType_Floppy)
4407 return setError(E_INVALIDARG,
4408 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"),
4409 aDevice, aControllerPort, aName.c_str());
4410 pAttach->i_updateHotPluggable(!!aHotPluggable);
4411
4412 return S_OK;
4413}
4414
4415HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4416 LONG aDevice)
4417{
4418 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4419 aName.c_str(), aControllerPort, aDevice));
4420
4421 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4422}
4423
4424HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4425 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4426{
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4428 aName.c_str(), aControllerPort, aDevice));
4429
4430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4431
4432 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4433 if (FAILED(rc)) return rc;
4434
4435 if (Global::IsOnlineOrTransient(mData->mMachineState))
4436 return setError(VBOX_E_INVALID_VM_STATE,
4437 tr("Invalid machine state: %s"),
4438 Global::stringifyMachineState(mData->mMachineState));
4439
4440 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4441 aName,
4442 aControllerPort,
4443 aDevice);
4444 if (!pAttach)
4445 return setError(VBOX_E_OBJECT_NOT_FOUND,
4446 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4447 aDevice, aControllerPort, aName.c_str());
4448
4449
4450 i_setModified(IsModified_Storage);
4451 mMediumAttachments.backup();
4452
4453 IBandwidthGroup *iB = aBandwidthGroup;
4454 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4455 if (aBandwidthGroup && group.isNull())
4456 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4457
4458 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4459
4460 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4461 if (strBandwidthGroupOld.isNotEmpty())
4462 {
4463 /* Get the bandwidth group object and release it - this must not fail. */
4464 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4465 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4466 Assert(SUCCEEDED(rc));
4467
4468 pBandwidthGroupOld->i_release();
4469 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4470 }
4471
4472 if (!group.isNull())
4473 {
4474 group->i_reference();
4475 pAttach->i_updateBandwidthGroup(group->i_getName());
4476 }
4477
4478 return S_OK;
4479}
4480
4481HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 DeviceType_T aType)
4485{
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aType));
4488
4489 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4490}
4491
4492
4493HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4494 LONG aControllerPort,
4495 LONG aDevice,
4496 BOOL aForce)
4497{
4498 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4499 aName.c_str(), aControllerPort, aForce));
4500
4501 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4502}
4503
4504HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4505 LONG aControllerPort,
4506 LONG aDevice,
4507 const ComPtr<IMedium> &aMedium,
4508 BOOL aForce)
4509{
4510 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4511 aName.c_str(), aControllerPort, aDevice, aForce));
4512
4513 // request the host lock first, since might be calling Host methods for getting host drives;
4514 // next, protect the media tree all the while we're in here, as well as our member variables
4515 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4516 this->lockHandle(),
4517 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4518
4519 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4520 aName,
4521 aControllerPort,
4522 aDevice);
4523 if (pAttach.isNull())
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528 /* Remember previously mounted medium. The medium before taking the
4529 * backup is not necessarily the same thing. */
4530 ComObjPtr<Medium> oldmedium;
4531 oldmedium = pAttach->i_getMedium();
4532
4533 IMedium *iM = aMedium;
4534 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4535 if (aMedium && pMedium.isNull())
4536 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4537
4538 AutoCaller mediumCaller(pMedium);
4539 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4540
4541 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4542 if (pMedium)
4543 {
4544 DeviceType_T mediumType = pAttach->i_getType();
4545 switch (mediumType)
4546 {
4547 case DeviceType_DVD:
4548 case DeviceType_Floppy:
4549 break;
4550
4551 default:
4552 return setError(VBOX_E_INVALID_OBJECT_STATE,
4553 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4554 aControllerPort,
4555 aDevice,
4556 aName.c_str());
4557 }
4558 }
4559
4560 i_setModified(IsModified_Storage);
4561 mMediumAttachments.backup();
4562
4563 {
4564 // The backup operation makes the pAttach reference point to the
4565 // old settings. Re-get the correct reference.
4566 pAttach = i_findAttachment(*mMediumAttachments.data(),
4567 aName,
4568 aControllerPort,
4569 aDevice);
4570 if (!oldmedium.isNull())
4571 oldmedium->i_removeBackReference(mData->mUuid);
4572 if (!pMedium.isNull())
4573 {
4574 pMedium->i_addBackReference(mData->mUuid);
4575
4576 mediumLock.release();
4577 multiLock.release();
4578 i_addMediumToRegistry(pMedium);
4579 multiLock.acquire();
4580 mediumLock.acquire();
4581 }
4582
4583 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4584 pAttach->i_updateMedium(pMedium);
4585 }
4586
4587 i_setModified(IsModified_Storage);
4588
4589 mediumLock.release();
4590 multiLock.release();
4591 HRESULT rc = i_onMediumChange(pAttach, aForce);
4592 multiLock.acquire();
4593 mediumLock.acquire();
4594
4595 /* On error roll back this change only. */
4596 if (FAILED(rc))
4597 {
4598 if (!pMedium.isNull())
4599 pMedium->i_removeBackReference(mData->mUuid);
4600 pAttach = i_findAttachment(*mMediumAttachments.data(),
4601 aName,
4602 aControllerPort,
4603 aDevice);
4604 /* If the attachment is gone in the meantime, bail out. */
4605 if (pAttach.isNull())
4606 return rc;
4607 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4608 if (!oldmedium.isNull())
4609 oldmedium->i_addBackReference(mData->mUuid);
4610 pAttach->i_updateMedium(oldmedium);
4611 }
4612
4613 mediumLock.release();
4614 multiLock.release();
4615
4616 /* Save modified registries, but skip this machine as it's the caller's
4617 * job to save its settings like all other settings changes. */
4618 mParent->i_unmarkRegistryModified(i_getId());
4619 mParent->i_saveModifiedRegistries();
4620
4621 return rc;
4622}
4623HRESULT Machine::getMedium(const com::Utf8Str &aName,
4624 LONG aControllerPort,
4625 LONG aDevice,
4626 ComPtr<IMedium> &aMedium)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4629 aName.c_str(), aControllerPort, aDevice));
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 aMedium = NULL;
4634
4635 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4636 aName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4642 aDevice, aControllerPort, aName.c_str());
4643
4644 aMedium = pAttach->i_getMedium();
4645
4646 return S_OK;
4647}
4648
4649HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4650{
4651 if (aSlot < RT_ELEMENTS(mSerialPorts))
4652 {
4653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4654 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4655 return S_OK;
4656 }
4657 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4658}
4659
4660HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4661{
4662 if (aSlot < RT_ELEMENTS(mParallelPorts))
4663 {
4664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4665 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4666 return S_OK;
4667 }
4668 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4669}
4670
4671
4672HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4673{
4674 /* Do not assert if slot is out of range, just return the advertised
4675 status. testdriver/vbox.py triggers this in logVmInfo. */
4676 if (aSlot >= mNetworkAdapters.size())
4677 return setError(E_INVALIDARG,
4678 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4679 aSlot, mNetworkAdapters.size());
4680
4681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4684
4685 return S_OK;
4686}
4687
4688HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4689{
4690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4691
4692 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4693 size_t i = 0;
4694 for (settings::StringsMap::const_iterator
4695 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4696 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4697 ++it, ++i)
4698 aKeys[i] = it->first;
4699
4700 return S_OK;
4701}
4702
4703 /**
4704 * @note Locks this object for reading.
4705 */
4706HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4707 com::Utf8Str &aValue)
4708{
4709 /* start with nothing found */
4710 aValue = "";
4711
4712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4713
4714 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4715 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4716 // found:
4717 aValue = it->second; // source is a Utf8Str
4718
4719 /* return the result to caller (may be empty) */
4720 return S_OK;
4721}
4722
4723 /**
4724 * @note Locks mParent for writing + this object for writing.
4725 */
4726HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4727{
4728 /* Because control characters in aKey have caused problems in the settings
4729 * they are rejected unless the key should be deleted. */
4730 if (!aValue.isEmpty())
4731 {
4732 for (size_t i = 0; i < aKey.length(); ++i)
4733 {
4734 char ch = aKey[i];
4735 if (RTLocCIsCntrl(ch))
4736 return E_INVALIDARG;
4737 }
4738 }
4739
4740 Utf8Str strOldValue; // empty
4741
4742 // locking note: we only hold the read lock briefly to look up the old value,
4743 // then release it and call the onExtraCanChange callbacks. There is a small
4744 // chance of a race insofar as the callback might be called twice if two callers
4745 // change the same key at the same time, but that's a much better solution
4746 // than the deadlock we had here before. The actual changing of the extradata
4747 // is then performed under the write lock and race-free.
4748
4749 // look up the old value first; if nothing has changed then we need not do anything
4750 {
4751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4752
4753 // For snapshots don't even think about allowing changes, extradata
4754 // is global for a machine, so there is nothing snapshot specific.
4755 if (i_isSnapshotMachine())
4756 return setError(VBOX_E_INVALID_VM_STATE,
4757 tr("Cannot set extradata for a snapshot"));
4758
4759 // check if the right IMachine instance is used
4760 if (mData->mRegistered && !i_isSessionMachine())
4761 return setError(VBOX_E_INVALID_VM_STATE,
4762 tr("Cannot set extradata for an immutable machine"));
4763
4764 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4765 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4766 strOldValue = it->second;
4767 }
4768
4769 bool fChanged;
4770 if ((fChanged = (strOldValue != aValue)))
4771 {
4772 // ask for permission from all listeners outside the locks;
4773 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4774 // lock to copy the list of callbacks to invoke
4775 Bstr bstrError;
4776 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4777 {
4778 const char *sep = bstrError.isEmpty() ? "" : ": ";
4779 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4780 return setError(E_ACCESSDENIED,
4781 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4782 aKey.c_str(),
4783 aValue.c_str(),
4784 sep,
4785 bstrError.raw());
4786 }
4787
4788 // data is changing and change not vetoed: then write it out under the lock
4789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4790
4791 if (aValue.isEmpty())
4792 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4793 else
4794 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4795 // creates a new key if needed
4796
4797 bool fNeedsGlobalSaveSettings = false;
4798 // This saving of settings is tricky: there is no "old state" for the
4799 // extradata items at all (unlike all other settings), so the old/new
4800 // settings comparison would give a wrong result!
4801 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4802
4803 if (fNeedsGlobalSaveSettings)
4804 {
4805 // save the global settings; for that we should hold only the VirtualBox lock
4806 alock.release();
4807 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4808 mParent->i_saveSettings();
4809 }
4810 }
4811
4812 // fire notification outside the lock
4813 if (fChanged)
4814 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4815
4816 return S_OK;
4817}
4818
4819HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4820{
4821 aProgress = NULL;
4822 NOREF(aSettingsFilePath);
4823 ReturnComNotImplemented();
4824}
4825
4826HRESULT Machine::saveSettings()
4827{
4828 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4831 if (FAILED(rc)) return rc;
4832
4833 /* the settings file path may never be null */
4834 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4835
4836 /* save all VM data excluding snapshots */
4837 bool fNeedsGlobalSaveSettings = false;
4838 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4839 mlock.release();
4840
4841 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4842 {
4843 // save the global settings; for that we should hold only the VirtualBox lock
4844 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4845 rc = mParent->i_saveSettings();
4846 }
4847
4848 return rc;
4849}
4850
4851
4852HRESULT Machine::discardSettings()
4853{
4854 /*
4855 * We need to take the machine list lock here as well as the machine one
4856 * or we'll get into trouble should any media stuff require rolling back.
4857 *
4858 * Details:
4859 *
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4861 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4862 * 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]
4863 * 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
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4867 * 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
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4869 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4870 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4872 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4873 * 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]
4874 * 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] (*)
4875 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4876 * 0:005> k
4877 * # Child-SP RetAddr Call Site
4878 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4879 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4880 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4881 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4882 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4883 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4884 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4885 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4886 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4887 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4888 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4889 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4890 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4891 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4892 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4893 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4894 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4895 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4896 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4897 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4898 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4899 *
4900 */
4901 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4903
4904 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4905 if (FAILED(rc)) return rc;
4906
4907 /*
4908 * during this rollback, the session will be notified if data has
4909 * been actually changed
4910 */
4911 i_rollback(true /* aNotify */);
4912
4913 return S_OK;
4914}
4915
4916/** @note Locks objects! */
4917HRESULT Machine::unregister(AutoCaller &autoCaller,
4918 CleanupMode_T aCleanupMode,
4919 std::vector<ComPtr<IMedium> > &aMedia)
4920{
4921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 Guid id(i_getId());
4924
4925 if (mData->mSession.mState != SessionState_Unlocked)
4926 return setError(VBOX_E_INVALID_OBJECT_STATE,
4927 tr("Cannot unregister the machine '%s' while it is locked"),
4928 mUserData->s.strName.c_str());
4929
4930 // wait for state dependents to drop to zero
4931 i_ensureNoStateDependencies(alock);
4932
4933 if (!mData->mAccessible)
4934 {
4935 // inaccessible machines can only be unregistered; uninitialize ourselves
4936 // here because currently there may be no unregistered that are inaccessible
4937 // (this state combination is not supported). Note releasing the caller and
4938 // leaving the lock before calling uninit()
4939 alock.release();
4940 autoCaller.release();
4941
4942 uninit();
4943
4944 mParent->i_unregisterMachine(this, id);
4945 // calls VirtualBox::i_saveSettings()
4946
4947 return S_OK;
4948 }
4949
4950 HRESULT rc = S_OK;
4951 mData->llFilesToDelete.clear();
4952
4953 if (!mSSData->strStateFilePath.isEmpty())
4954 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4955
4956 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4957 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4958 mData->llFilesToDelete.push_back(strNVRAMFile);
4959
4960 // This list collects the medium objects from all medium attachments
4961 // which we will detach from the machine and its snapshots, in a specific
4962 // order which allows for closing all media without getting "media in use"
4963 // errors, simply by going through the list from the front to the back:
4964 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4965 // and must be closed before the parent media from the snapshots, or closing the parents
4966 // will fail because they still have children);
4967 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4968 // the root ("first") snapshot of the machine.
4969 MediaList llMedia;
4970
4971 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4972 && mMediumAttachments->size()
4973 )
4974 {
4975 // we have media attachments: detach them all and add the Medium objects to our list
4976 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4977 }
4978
4979 if (mData->mFirstSnapshot)
4980 {
4981 // add the media from the medium attachments of the snapshots to llMedia
4982 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4983 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4984 // into the children first
4985
4986 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4987 MachineState_T oldState = mData->mMachineState;
4988 mData->mMachineState = MachineState_DeletingSnapshot;
4989
4990 // make a copy of the first snapshot reference so the refcount does not
4991 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4992 // (would hang due to the AutoCaller voodoo)
4993 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4994
4995 // GO!
4996 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4997
4998 mData->mMachineState = oldState;
4999 }
5000
5001 if (FAILED(rc))
5002 {
5003 i_rollbackMedia();
5004 return rc;
5005 }
5006
5007 // commit all the media changes made above
5008 i_commitMedia();
5009
5010 mData->mRegistered = false;
5011
5012 // machine lock no longer needed
5013 alock.release();
5014
5015 /* Make sure that the settings of the current VM are not saved, because
5016 * they are rather crippled at this point to meet the cleanup expectations
5017 * and there's no point destroying the VM config on disk just because. */
5018 mParent->i_unmarkRegistryModified(id);
5019
5020 // return media to caller
5021 aMedia.resize(llMedia.size());
5022 size_t i = 0;
5023 for (MediaList::const_iterator
5024 it = llMedia.begin();
5025 it != llMedia.end();
5026 ++it, ++i)
5027 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5028
5029 mParent->i_unregisterMachine(this, id);
5030 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5031
5032 return S_OK;
5033}
5034
5035/**
5036 * Task record for deleting a machine config.
5037 */
5038class Machine::DeleteConfigTask
5039 : public Machine::Task
5040{
5041public:
5042 DeleteConfigTask(Machine *m,
5043 Progress *p,
5044 const Utf8Str &t,
5045 const RTCList<ComPtr<IMedium> > &llMediums,
5046 const StringsList &llFilesToDelete)
5047 : Task(m, p, t),
5048 m_llMediums(llMediums),
5049 m_llFilesToDelete(llFilesToDelete)
5050 {}
5051
5052private:
5053 void handler()
5054 {
5055 try
5056 {
5057 m_pMachine->i_deleteConfigHandler(*this);
5058 }
5059 catch (...)
5060 {
5061 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5062 }
5063 }
5064
5065 RTCList<ComPtr<IMedium> > m_llMediums;
5066 StringsList m_llFilesToDelete;
5067
5068 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5069};
5070
5071/**
5072 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5073 * SessionMachine::taskHandler().
5074 *
5075 * @note Locks this object for writing.
5076 *
5077 * @param task
5078 * @return
5079 */
5080void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5081{
5082 LogFlowThisFuncEnter();
5083
5084 AutoCaller autoCaller(this);
5085 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5086 if (FAILED(autoCaller.rc()))
5087 {
5088 /* we might have been uninitialized because the session was accidentally
5089 * closed by the client, so don't assert */
5090 HRESULT rc = setError(E_FAIL,
5091 tr("The session has been accidentally closed"));
5092 task.m_pProgress->i_notifyComplete(rc);
5093 LogFlowThisFuncLeave();
5094 return;
5095 }
5096
5097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5098
5099 HRESULT rc = S_OK;
5100
5101 try
5102 {
5103 ULONG uLogHistoryCount = 3;
5104 ComPtr<ISystemProperties> systemProperties;
5105 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5106 if (FAILED(rc)) throw rc;
5107
5108 if (!systemProperties.isNull())
5109 {
5110 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5111 if (FAILED(rc)) throw rc;
5112 }
5113
5114 MachineState_T oldState = mData->mMachineState;
5115 i_setMachineState(MachineState_SettingUp);
5116 alock.release();
5117 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5118 {
5119 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5120 {
5121 AutoCaller mac(pMedium);
5122 if (FAILED(mac.rc())) throw mac.rc();
5123 Utf8Str strLocation = pMedium->i_getLocationFull();
5124 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5125 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5126 if (FAILED(rc)) throw rc;
5127 }
5128 if (pMedium->i_isMediumFormatFile())
5129 {
5130 ComPtr<IProgress> pProgress2;
5131 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5132 if (FAILED(rc)) throw rc;
5133 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5134 if (FAILED(rc)) throw rc;
5135 }
5136
5137 /* Close the medium, deliberately without checking the return
5138 * code, and without leaving any trace in the error info, as
5139 * a failure here is a very minor issue, which shouldn't happen
5140 * as above we even managed to delete the medium. */
5141 {
5142 ErrorInfoKeeper eik;
5143 pMedium->Close();
5144 }
5145 }
5146 i_setMachineState(oldState);
5147 alock.acquire();
5148
5149 // delete the files pushed on the task list by Machine::Delete()
5150 // (this includes saved states of the machine and snapshots and
5151 // medium storage files from the IMedium list passed in, and the
5152 // machine XML file)
5153 for (StringsList::const_iterator
5154 it = task.m_llFilesToDelete.begin();
5155 it != task.m_llFilesToDelete.end();
5156 ++it)
5157 {
5158 const Utf8Str &strFile = *it;
5159 LogFunc(("Deleting file %s\n", strFile.c_str()));
5160 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5161 if (FAILED(rc)) throw rc;
5162
5163 int vrc = RTFileDelete(strFile.c_str());
5164 if (RT_FAILURE(vrc))
5165 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5166 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5167 }
5168
5169 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5170 if (FAILED(rc)) throw rc;
5171
5172 /* delete the settings only when the file actually exists */
5173 if (mData->pMachineConfigFile->fileExists())
5174 {
5175 /* Delete any backup or uncommitted XML files. Ignore failures.
5176 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5177 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5178 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5179 RTFileDelete(otherXml.c_str());
5180 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5181 RTFileDelete(otherXml.c_str());
5182
5183 /* delete the Logs folder, nothing important should be left
5184 * there (we don't check for errors because the user might have
5185 * some private files there that we don't want to delete) */
5186 Utf8Str logFolder;
5187 getLogFolder(logFolder);
5188 Assert(logFolder.length());
5189 if (RTDirExists(logFolder.c_str()))
5190 {
5191 /* Delete all VBox.log[.N] files from the Logs folder
5192 * (this must be in sync with the rotation logic in
5193 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5194 * files that may have been created by the GUI. */
5195 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5196 RTFileDelete(log.c_str());
5197 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5198 RTFileDelete(log.c_str());
5199 for (ULONG i = uLogHistoryCount; i > 0; i--)
5200 {
5201 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5202 RTFileDelete(log.c_str());
5203 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5204 RTFileDelete(log.c_str());
5205 }
5206 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5207 RTFileDelete(log.c_str());
5208#if defined(RT_OS_WINDOWS)
5209 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5210 RTFileDelete(log.c_str());
5211 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5212 RTFileDelete(log.c_str());
5213#endif
5214
5215 RTDirRemove(logFolder.c_str());
5216 }
5217
5218 /* delete the Snapshots folder, nothing important should be left
5219 * there (we don't check for errors because the user might have
5220 * some private files there that we don't want to delete) */
5221 Utf8Str strFullSnapshotFolder;
5222 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5223 Assert(!strFullSnapshotFolder.isEmpty());
5224 if (RTDirExists(strFullSnapshotFolder.c_str()))
5225 RTDirRemove(strFullSnapshotFolder.c_str());
5226
5227 // delete the directory that contains the settings file, but only
5228 // if it matches the VM name
5229 Utf8Str settingsDir;
5230 if (i_isInOwnDir(&settingsDir))
5231 RTDirRemove(settingsDir.c_str());
5232 }
5233
5234 alock.release();
5235
5236 mParent->i_saveModifiedRegistries();
5237 }
5238 catch (HRESULT aRC) { rc = aRC; }
5239
5240 task.m_pProgress->i_notifyComplete(rc);
5241
5242 LogFlowThisFuncLeave();
5243}
5244
5245HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5246{
5247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5248
5249 HRESULT rc = i_checkStateDependency(MutableStateDep);
5250 if (FAILED(rc)) return rc;
5251
5252 if (mData->mRegistered)
5253 return setError(VBOX_E_INVALID_VM_STATE,
5254 tr("Cannot delete settings of a registered machine"));
5255
5256 // collect files to delete
5257 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5258 // machine config file
5259 if (mData->pMachineConfigFile->fileExists())
5260 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5261 // backup of machine config file
5262 Utf8Str strTmp(mData->m_strConfigFileFull);
5263 strTmp.append("-prev");
5264 if (RTFileExists(strTmp.c_str()))
5265 llFilesToDelete.push_back(strTmp);
5266
5267 RTCList<ComPtr<IMedium> > llMediums;
5268 for (size_t i = 0; i < aMedia.size(); ++i)
5269 {
5270 IMedium *pIMedium(aMedia[i]);
5271 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5272 if (pMedium.isNull())
5273 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5274 SafeArray<BSTR> ids;
5275 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5276 if (FAILED(rc)) return rc;
5277 /* At this point the medium should not have any back references
5278 * anymore. If it has it is attached to another VM and *must* not
5279 * deleted. */
5280 if (ids.size() < 1)
5281 llMediums.append(pMedium);
5282 }
5283
5284 ComObjPtr<Progress> pProgress;
5285 pProgress.createObject();
5286 rc = pProgress->init(i_getVirtualBox(),
5287 static_cast<IMachine*>(this) /* aInitiator */,
5288 tr("Deleting files"),
5289 true /* fCancellable */,
5290 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5291 tr("Collecting file inventory"));
5292 if (FAILED(rc))
5293 return rc;
5294
5295 /* create and start the task on a separate thread (note that it will not
5296 * start working until we release alock) */
5297 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5298 rc = pTask->createThread();
5299 pTask = NULL;
5300 if (FAILED(rc))
5301 return rc;
5302
5303 pProgress.queryInterfaceTo(aProgress.asOutParam());
5304
5305 LogFlowFuncLeave();
5306
5307 return S_OK;
5308}
5309
5310HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5311{
5312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5313
5314 ComObjPtr<Snapshot> pSnapshot;
5315 HRESULT rc;
5316
5317 if (aNameOrId.isEmpty())
5318 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5319 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5320 else
5321 {
5322 Guid uuid(aNameOrId);
5323 if (uuid.isValid())
5324 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5325 else
5326 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5327 }
5328 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5329
5330 return rc;
5331}
5332
5333HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5334 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5335{
5336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5337
5338 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5339 if (FAILED(rc)) return rc;
5340
5341 ComObjPtr<SharedFolder> sharedFolder;
5342 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5343 if (SUCCEEDED(rc))
5344 return setError(VBOX_E_OBJECT_IN_USE,
5345 tr("Shared folder named '%s' already exists"),
5346 aName.c_str());
5347
5348 sharedFolder.createObject();
5349 rc = sharedFolder->init(i_getMachine(),
5350 aName,
5351 aHostPath,
5352 !!aWritable,
5353 !!aAutomount,
5354 aAutoMountPoint,
5355 true /* fFailOnError */);
5356 if (FAILED(rc)) return rc;
5357
5358 i_setModified(IsModified_SharedFolders);
5359 mHWData.backup();
5360 mHWData->mSharedFolders.push_back(sharedFolder);
5361
5362 /* inform the direct session if any */
5363 alock.release();
5364 i_onSharedFolderChange();
5365
5366 return S_OK;
5367}
5368
5369HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5370{
5371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5372
5373 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5374 if (FAILED(rc)) return rc;
5375
5376 ComObjPtr<SharedFolder> sharedFolder;
5377 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5378 if (FAILED(rc)) return rc;
5379
5380 i_setModified(IsModified_SharedFolders);
5381 mHWData.backup();
5382 mHWData->mSharedFolders.remove(sharedFolder);
5383
5384 /* inform the direct session if any */
5385 alock.release();
5386 i_onSharedFolderChange();
5387
5388 return S_OK;
5389}
5390
5391HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5392{
5393 /* start with No */
5394 *aCanShow = FALSE;
5395
5396 ComPtr<IInternalSessionControl> directControl;
5397 {
5398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5399
5400 if (mData->mSession.mState != SessionState_Locked)
5401 return setError(VBOX_E_INVALID_VM_STATE,
5402 tr("Machine is not locked for session (session state: %s)"),
5403 Global::stringifySessionState(mData->mSession.mState));
5404
5405 if (mData->mSession.mLockType == LockType_VM)
5406 directControl = mData->mSession.mDirectControl;
5407 }
5408
5409 /* ignore calls made after #OnSessionEnd() is called */
5410 if (!directControl)
5411 return S_OK;
5412
5413 LONG64 dummy;
5414 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5415}
5416
5417HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5418{
5419 ComPtr<IInternalSessionControl> directControl;
5420 {
5421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5422
5423 if (mData->mSession.mState != SessionState_Locked)
5424 return setError(E_FAIL,
5425 tr("Machine is not locked for session (session state: %s)"),
5426 Global::stringifySessionState(mData->mSession.mState));
5427
5428 if (mData->mSession.mLockType == LockType_VM)
5429 directControl = mData->mSession.mDirectControl;
5430 }
5431
5432 /* ignore calls made after #OnSessionEnd() is called */
5433 if (!directControl)
5434 return S_OK;
5435
5436 BOOL dummy;
5437 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5438}
5439
5440#ifdef VBOX_WITH_GUEST_PROPS
5441/**
5442 * Look up a guest property in VBoxSVC's internal structures.
5443 */
5444HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5445 com::Utf8Str &aValue,
5446 LONG64 *aTimestamp,
5447 com::Utf8Str &aFlags) const
5448{
5449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5450
5451 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5452 if (it != mHWData->mGuestProperties.end())
5453 {
5454 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5455 aValue = it->second.strValue;
5456 *aTimestamp = it->second.mTimestamp;
5457 GuestPropWriteFlags(it->second.mFlags, szFlags);
5458 aFlags = Utf8Str(szFlags);
5459 }
5460
5461 return S_OK;
5462}
5463
5464/**
5465 * Query the VM that a guest property belongs to for the property.
5466 * @returns E_ACCESSDENIED if the VM process is not available or not
5467 * currently handling queries and the lookup should then be done in
5468 * VBoxSVC.
5469 */
5470HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5471 com::Utf8Str &aValue,
5472 LONG64 *aTimestamp,
5473 com::Utf8Str &aFlags) const
5474{
5475 HRESULT rc = S_OK;
5476 Bstr bstrValue;
5477 Bstr bstrFlags;
5478
5479 ComPtr<IInternalSessionControl> directControl;
5480 {
5481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5482 if (mData->mSession.mLockType == LockType_VM)
5483 directControl = mData->mSession.mDirectControl;
5484 }
5485
5486 /* ignore calls made after #OnSessionEnd() is called */
5487 if (!directControl)
5488 rc = E_ACCESSDENIED;
5489 else
5490 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5491 0 /* accessMode */,
5492 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5493
5494 aValue = bstrValue;
5495 aFlags = bstrFlags;
5496
5497 return rc;
5498}
5499#endif // VBOX_WITH_GUEST_PROPS
5500
5501HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5502 com::Utf8Str &aValue,
5503 LONG64 *aTimestamp,
5504 com::Utf8Str &aFlags)
5505{
5506#ifndef VBOX_WITH_GUEST_PROPS
5507 ReturnComNotImplemented();
5508#else // VBOX_WITH_GUEST_PROPS
5509
5510 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5511
5512 if (rc == E_ACCESSDENIED)
5513 /* The VM is not running or the service is not (yet) accessible */
5514 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5515 return rc;
5516#endif // VBOX_WITH_GUEST_PROPS
5517}
5518
5519HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5520{
5521 LONG64 dummyTimestamp;
5522 com::Utf8Str dummyFlags;
5523 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5524 return rc;
5525
5526}
5527HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5528{
5529 com::Utf8Str dummyFlags;
5530 com::Utf8Str dummyValue;
5531 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5532 return rc;
5533}
5534
5535#ifdef VBOX_WITH_GUEST_PROPS
5536/**
5537 * Set a guest property in VBoxSVC's internal structures.
5538 */
5539HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5540 const com::Utf8Str &aFlags, bool fDelete)
5541{
5542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5543 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 try
5547 {
5548 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5549 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5550 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5551
5552 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5553 if (it == mHWData->mGuestProperties.end())
5554 {
5555 if (!fDelete)
5556 {
5557 i_setModified(IsModified_MachineData);
5558 mHWData.backupEx();
5559
5560 RTTIMESPEC time;
5561 HWData::GuestProperty prop;
5562 prop.strValue = aValue;
5563 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5564 prop.mFlags = fFlags;
5565 mHWData->mGuestProperties[aName] = prop;
5566 }
5567 }
5568 else
5569 {
5570 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5571 {
5572 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5573 }
5574 else
5575 {
5576 i_setModified(IsModified_MachineData);
5577 mHWData.backupEx();
5578
5579 /* The backupEx() operation invalidates our iterator,
5580 * so get a new one. */
5581 it = mHWData->mGuestProperties.find(aName);
5582 Assert(it != mHWData->mGuestProperties.end());
5583
5584 if (!fDelete)
5585 {
5586 RTTIMESPEC time;
5587 it->second.strValue = aValue;
5588 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5589 it->second.mFlags = fFlags;
5590 }
5591 else
5592 mHWData->mGuestProperties.erase(it);
5593 }
5594 }
5595
5596 if (SUCCEEDED(rc))
5597 {
5598 alock.release();
5599
5600 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5601 }
5602 }
5603 catch (std::bad_alloc &)
5604 {
5605 rc = E_OUTOFMEMORY;
5606 }
5607
5608 return rc;
5609}
5610
5611/**
5612 * Set a property on the VM that that property belongs to.
5613 * @returns E_ACCESSDENIED if the VM process is not available or not
5614 * currently handling queries and the setting should then be done in
5615 * VBoxSVC.
5616 */
5617HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5618 const com::Utf8Str &aFlags, bool fDelete)
5619{
5620 HRESULT rc;
5621
5622 try
5623 {
5624 ComPtr<IInternalSessionControl> directControl;
5625 {
5626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5627 if (mData->mSession.mLockType == LockType_VM)
5628 directControl = mData->mSession.mDirectControl;
5629 }
5630
5631 Bstr dummy1; /* will not be changed (setter) */
5632 Bstr dummy2; /* will not be changed (setter) */
5633 LONG64 dummy64;
5634 if (!directControl)
5635 rc = E_ACCESSDENIED;
5636 else
5637 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5638 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5639 fDelete ? 2 : 1 /* accessMode */,
5640 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5641 }
5642 catch (std::bad_alloc &)
5643 {
5644 rc = E_OUTOFMEMORY;
5645 }
5646
5647 return rc;
5648}
5649#endif // VBOX_WITH_GUEST_PROPS
5650
5651HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5652 const com::Utf8Str &aFlags)
5653{
5654#ifndef VBOX_WITH_GUEST_PROPS
5655 ReturnComNotImplemented();
5656#else // VBOX_WITH_GUEST_PROPS
5657 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5658 if (rc == E_ACCESSDENIED)
5659 /* The VM is not running or the service is not (yet) accessible */
5660 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5661 return rc;
5662#endif // VBOX_WITH_GUEST_PROPS
5663}
5664
5665HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5666{
5667 return setGuestProperty(aProperty, aValue, "");
5668}
5669
5670HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5671{
5672#ifndef VBOX_WITH_GUEST_PROPS
5673 ReturnComNotImplemented();
5674#else // VBOX_WITH_GUEST_PROPS
5675 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5676 if (rc == E_ACCESSDENIED)
5677 /* The VM is not running or the service is not (yet) accessible */
5678 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5679 return rc;
5680#endif // VBOX_WITH_GUEST_PROPS
5681}
5682
5683#ifdef VBOX_WITH_GUEST_PROPS
5684/**
5685 * Enumerate the guest properties in VBoxSVC's internal structures.
5686 */
5687HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5688 std::vector<com::Utf8Str> &aNames,
5689 std::vector<com::Utf8Str> &aValues,
5690 std::vector<LONG64> &aTimestamps,
5691 std::vector<com::Utf8Str> &aFlags)
5692{
5693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5694 Utf8Str strPatterns(aPatterns);
5695
5696 /*
5697 * Look for matching patterns and build up a list.
5698 */
5699 HWData::GuestPropertyMap propMap;
5700 for (HWData::GuestPropertyMap::const_iterator
5701 it = mHWData->mGuestProperties.begin();
5702 it != mHWData->mGuestProperties.end();
5703 ++it)
5704 {
5705 if ( strPatterns.isEmpty()
5706 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5707 RTSTR_MAX,
5708 it->first.c_str(),
5709 RTSTR_MAX,
5710 NULL)
5711 )
5712 propMap.insert(*it);
5713 }
5714
5715 alock.release();
5716
5717 /*
5718 * And build up the arrays for returning the property information.
5719 */
5720 size_t cEntries = propMap.size();
5721
5722 aNames.resize(cEntries);
5723 aValues.resize(cEntries);
5724 aTimestamps.resize(cEntries);
5725 aFlags.resize(cEntries);
5726
5727 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5728 size_t i = 0;
5729 for (HWData::GuestPropertyMap::const_iterator
5730 it = propMap.begin();
5731 it != propMap.end();
5732 ++it, ++i)
5733 {
5734 aNames[i] = it->first;
5735 aValues[i] = it->second.strValue;
5736 aTimestamps[i] = it->second.mTimestamp;
5737 GuestPropWriteFlags(it->second.mFlags, szFlags);
5738 aFlags[i] = Utf8Str(szFlags);
5739 }
5740
5741 return S_OK;
5742}
5743
5744/**
5745 * Enumerate the properties managed by a VM.
5746 * @returns E_ACCESSDENIED if the VM process is not available or not
5747 * currently handling queries and the setting should then be done in
5748 * VBoxSVC.
5749 */
5750HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5751 std::vector<com::Utf8Str> &aNames,
5752 std::vector<com::Utf8Str> &aValues,
5753 std::vector<LONG64> &aTimestamps,
5754 std::vector<com::Utf8Str> &aFlags)
5755{
5756 HRESULT rc;
5757 ComPtr<IInternalSessionControl> directControl;
5758 {
5759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5760 if (mData->mSession.mLockType == LockType_VM)
5761 directControl = mData->mSession.mDirectControl;
5762 }
5763
5764 com::SafeArray<BSTR> bNames;
5765 com::SafeArray<BSTR> bValues;
5766 com::SafeArray<LONG64> bTimestamps;
5767 com::SafeArray<BSTR> bFlags;
5768
5769 if (!directControl)
5770 rc = E_ACCESSDENIED;
5771 else
5772 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5773 ComSafeArrayAsOutParam(bNames),
5774 ComSafeArrayAsOutParam(bValues),
5775 ComSafeArrayAsOutParam(bTimestamps),
5776 ComSafeArrayAsOutParam(bFlags));
5777 size_t i;
5778 aNames.resize(bNames.size());
5779 for (i = 0; i < bNames.size(); ++i)
5780 aNames[i] = Utf8Str(bNames[i]);
5781 aValues.resize(bValues.size());
5782 for (i = 0; i < bValues.size(); ++i)
5783 aValues[i] = Utf8Str(bValues[i]);
5784 aTimestamps.resize(bTimestamps.size());
5785 for (i = 0; i < bTimestamps.size(); ++i)
5786 aTimestamps[i] = bTimestamps[i];
5787 aFlags.resize(bFlags.size());
5788 for (i = 0; i < bFlags.size(); ++i)
5789 aFlags[i] = Utf8Str(bFlags[i]);
5790
5791 return rc;
5792}
5793#endif // VBOX_WITH_GUEST_PROPS
5794HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5795 std::vector<com::Utf8Str> &aNames,
5796 std::vector<com::Utf8Str> &aValues,
5797 std::vector<LONG64> &aTimestamps,
5798 std::vector<com::Utf8Str> &aFlags)
5799{
5800#ifndef VBOX_WITH_GUEST_PROPS
5801 ReturnComNotImplemented();
5802#else // VBOX_WITH_GUEST_PROPS
5803
5804 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5805
5806 if (rc == E_ACCESSDENIED)
5807 /* The VM is not running or the service is not (yet) accessible */
5808 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5809 return rc;
5810#endif // VBOX_WITH_GUEST_PROPS
5811}
5812
5813HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5814 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5815{
5816 MediumAttachmentList atts;
5817
5818 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5819 if (FAILED(rc)) return rc;
5820
5821 aMediumAttachments.resize(atts.size());
5822 size_t i = 0;
5823 for (MediumAttachmentList::const_iterator
5824 it = atts.begin();
5825 it != atts.end();
5826 ++it, ++i)
5827 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5828
5829 return S_OK;
5830}
5831
5832HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5833 LONG aControllerPort,
5834 LONG aDevice,
5835 ComPtr<IMediumAttachment> &aAttachment)
5836{
5837 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5838 aName.c_str(), aControllerPort, aDevice));
5839
5840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5841
5842 aAttachment = NULL;
5843
5844 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5845 aName,
5846 aControllerPort,
5847 aDevice);
5848 if (pAttach.isNull())
5849 return setError(VBOX_E_OBJECT_NOT_FOUND,
5850 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5851 aDevice, aControllerPort, aName.c_str());
5852
5853 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5854
5855 return S_OK;
5856}
5857
5858
5859HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5860 StorageBus_T aConnectionType,
5861 ComPtr<IStorageController> &aController)
5862{
5863 if ( (aConnectionType <= StorageBus_Null)
5864 || (aConnectionType > StorageBus_VirtioSCSI))
5865 return setError(E_INVALIDARG,
5866 tr("Invalid connection type: %d"),
5867 aConnectionType);
5868
5869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5870
5871 HRESULT rc = i_checkStateDependency(MutableStateDep);
5872 if (FAILED(rc)) return rc;
5873
5874 /* try to find one with the name first. */
5875 ComObjPtr<StorageController> ctrl;
5876
5877 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5878 if (SUCCEEDED(rc))
5879 return setError(VBOX_E_OBJECT_IN_USE,
5880 tr("Storage controller named '%s' already exists"),
5881 aName.c_str());
5882
5883 ctrl.createObject();
5884
5885 /* get a new instance number for the storage controller */
5886 ULONG ulInstance = 0;
5887 bool fBootable = true;
5888 for (StorageControllerList::const_iterator
5889 it = mStorageControllers->begin();
5890 it != mStorageControllers->end();
5891 ++it)
5892 {
5893 if ((*it)->i_getStorageBus() == aConnectionType)
5894 {
5895 ULONG ulCurInst = (*it)->i_getInstance();
5896
5897 if (ulCurInst >= ulInstance)
5898 ulInstance = ulCurInst + 1;
5899
5900 /* Only one controller of each type can be marked as bootable. */
5901 if ((*it)->i_getBootable())
5902 fBootable = false;
5903 }
5904 }
5905
5906 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5907 if (FAILED(rc)) return rc;
5908
5909 i_setModified(IsModified_Storage);
5910 mStorageControllers.backup();
5911 mStorageControllers->push_back(ctrl);
5912
5913 ctrl.queryInterfaceTo(aController.asOutParam());
5914
5915 /* inform the direct session if any */
5916 alock.release();
5917 i_onStorageControllerChange(i_getId(), aName);
5918
5919 return S_OK;
5920}
5921
5922HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5923 ComPtr<IStorageController> &aStorageController)
5924{
5925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5926
5927 ComObjPtr<StorageController> ctrl;
5928
5929 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5930 if (SUCCEEDED(rc))
5931 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5932
5933 return rc;
5934}
5935
5936HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5937 ULONG aInstance,
5938 ComPtr<IStorageController> &aStorageController)
5939{
5940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5941
5942 for (StorageControllerList::const_iterator
5943 it = mStorageControllers->begin();
5944 it != mStorageControllers->end();
5945 ++it)
5946 {
5947 if ( (*it)->i_getStorageBus() == aConnectionType
5948 && (*it)->i_getInstance() == aInstance)
5949 {
5950 (*it).queryInterfaceTo(aStorageController.asOutParam());
5951 return S_OK;
5952 }
5953 }
5954
5955 return setError(VBOX_E_OBJECT_NOT_FOUND,
5956 tr("Could not find a storage controller with instance number '%lu'"),
5957 aInstance);
5958}
5959
5960HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5961{
5962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5963
5964 HRESULT rc = i_checkStateDependency(MutableStateDep);
5965 if (FAILED(rc)) return rc;
5966
5967 ComObjPtr<StorageController> ctrl;
5968
5969 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5970 if (SUCCEEDED(rc))
5971 {
5972 /* Ensure that only one controller of each type is marked as bootable. */
5973 if (aBootable == TRUE)
5974 {
5975 for (StorageControllerList::const_iterator
5976 it = mStorageControllers->begin();
5977 it != mStorageControllers->end();
5978 ++it)
5979 {
5980 ComObjPtr<StorageController> aCtrl = (*it);
5981
5982 if ( (aCtrl->i_getName() != aName)
5983 && aCtrl->i_getBootable() == TRUE
5984 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5985 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5986 {
5987 aCtrl->i_setBootable(FALSE);
5988 break;
5989 }
5990 }
5991 }
5992
5993 if (SUCCEEDED(rc))
5994 {
5995 ctrl->i_setBootable(aBootable);
5996 i_setModified(IsModified_Storage);
5997 }
5998 }
5999
6000 if (SUCCEEDED(rc))
6001 {
6002 /* inform the direct session if any */
6003 alock.release();
6004 i_onStorageControllerChange(i_getId(), aName);
6005 }
6006
6007 return rc;
6008}
6009
6010HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6011{
6012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6013
6014 HRESULT rc = i_checkStateDependency(MutableStateDep);
6015 if (FAILED(rc)) return rc;
6016
6017 ComObjPtr<StorageController> ctrl;
6018 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6019 if (FAILED(rc)) return rc;
6020
6021 MediumAttachmentList llDetachedAttachments;
6022 {
6023 /* find all attached devices to the appropriate storage controller and detach them all */
6024 // make a temporary list because detachDevice invalidates iterators into
6025 // mMediumAttachments
6026 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6027
6028 for (MediumAttachmentList::const_iterator
6029 it = llAttachments2.begin();
6030 it != llAttachments2.end();
6031 ++it)
6032 {
6033 MediumAttachment *pAttachTemp = *it;
6034
6035 AutoCaller localAutoCaller(pAttachTemp);
6036 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6037
6038 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6039
6040 if (pAttachTemp->i_getControllerName() == aName)
6041 {
6042 llDetachedAttachments.push_back(pAttachTemp);
6043 rc = i_detachDevice(pAttachTemp, alock, NULL);
6044 if (FAILED(rc)) return rc;
6045 }
6046 }
6047 }
6048
6049 /* send event about detached devices before removing parent controller */
6050 for (MediumAttachmentList::const_iterator
6051 it = llDetachedAttachments.begin();
6052 it != llDetachedAttachments.end();
6053 ++it)
6054 {
6055 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6056 }
6057
6058 /* We can remove it now. */
6059 i_setModified(IsModified_Storage);
6060 mStorageControllers.backup();
6061
6062 ctrl->i_unshare();
6063
6064 mStorageControllers->remove(ctrl);
6065
6066 /* inform the direct session if any */
6067 alock.release();
6068 i_onStorageControllerChange(i_getId(), aName);
6069
6070 return S_OK;
6071}
6072
6073HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6074 ComPtr<IUSBController> &aController)
6075{
6076 if ( (aType <= USBControllerType_Null)
6077 || (aType >= USBControllerType_Last))
6078 return setError(E_INVALIDARG,
6079 tr("Invalid USB controller type: %d"),
6080 aType);
6081
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 HRESULT rc = i_checkStateDependency(MutableStateDep);
6085 if (FAILED(rc)) return rc;
6086
6087 /* try to find one with the same type first. */
6088 ComObjPtr<USBController> ctrl;
6089
6090 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6091 if (SUCCEEDED(rc))
6092 return setError(VBOX_E_OBJECT_IN_USE,
6093 tr("USB controller named '%s' already exists"),
6094 aName.c_str());
6095
6096 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6097 ULONG maxInstances;
6098 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6099 if (FAILED(rc))
6100 return rc;
6101
6102 ULONG cInstances = i_getUSBControllerCountByType(aType);
6103 if (cInstances >= maxInstances)
6104 return setError(E_INVALIDARG,
6105 tr("Too many USB controllers of this type"));
6106
6107 ctrl.createObject();
6108
6109 rc = ctrl->init(this, aName, aType);
6110 if (FAILED(rc)) return rc;
6111
6112 i_setModified(IsModified_USB);
6113 mUSBControllers.backup();
6114 mUSBControllers->push_back(ctrl);
6115
6116 ctrl.queryInterfaceTo(aController.asOutParam());
6117
6118 /* inform the direct session if any */
6119 alock.release();
6120 i_onUSBControllerChange();
6121
6122 return S_OK;
6123}
6124
6125HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6126{
6127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6128
6129 ComObjPtr<USBController> ctrl;
6130
6131 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6132 if (SUCCEEDED(rc))
6133 ctrl.queryInterfaceTo(aController.asOutParam());
6134
6135 return rc;
6136}
6137
6138HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6139 ULONG *aControllers)
6140{
6141 if ( (aType <= USBControllerType_Null)
6142 || (aType >= USBControllerType_Last))
6143 return setError(E_INVALIDARG,
6144 tr("Invalid USB controller type: %d"),
6145 aType);
6146
6147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6148
6149 ComObjPtr<USBController> ctrl;
6150
6151 *aControllers = i_getUSBControllerCountByType(aType);
6152
6153 return S_OK;
6154}
6155
6156HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6157{
6158
6159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6160
6161 HRESULT rc = i_checkStateDependency(MutableStateDep);
6162 if (FAILED(rc)) return rc;
6163
6164 ComObjPtr<USBController> ctrl;
6165 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6166 if (FAILED(rc)) return rc;
6167
6168 i_setModified(IsModified_USB);
6169 mUSBControllers.backup();
6170
6171 ctrl->i_unshare();
6172
6173 mUSBControllers->remove(ctrl);
6174
6175 /* inform the direct session if any */
6176 alock.release();
6177 i_onUSBControllerChange();
6178
6179 return S_OK;
6180}
6181
6182HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6183 ULONG *aOriginX,
6184 ULONG *aOriginY,
6185 ULONG *aWidth,
6186 ULONG *aHeight,
6187 BOOL *aEnabled)
6188{
6189 uint32_t u32OriginX= 0;
6190 uint32_t u32OriginY= 0;
6191 uint32_t u32Width = 0;
6192 uint32_t u32Height = 0;
6193 uint16_t u16Flags = 0;
6194
6195 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6196 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6197 if (RT_FAILURE(vrc))
6198 {
6199#ifdef RT_OS_WINDOWS
6200 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6201 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6202 * So just assign fEnable to TRUE again.
6203 * The right fix would be to change GUI API wrappers to make sure that parameters
6204 * are changed only if API succeeds.
6205 */
6206 *aEnabled = TRUE;
6207#endif
6208 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6209 tr("Saved guest size is not available (%Rrc)"),
6210 vrc);
6211 }
6212
6213 *aOriginX = u32OriginX;
6214 *aOriginY = u32OriginY;
6215 *aWidth = u32Width;
6216 *aHeight = u32Height;
6217 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6218
6219 return S_OK;
6220}
6221
6222HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6223 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6224{
6225 if (aScreenId != 0)
6226 return E_NOTIMPL;
6227
6228 if ( aBitmapFormat != BitmapFormat_BGR0
6229 && aBitmapFormat != BitmapFormat_BGRA
6230 && aBitmapFormat != BitmapFormat_RGBA
6231 && aBitmapFormat != BitmapFormat_PNG)
6232 return setError(E_NOTIMPL,
6233 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6234
6235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6236
6237 uint8_t *pu8Data = NULL;
6238 uint32_t cbData = 0;
6239 uint32_t u32Width = 0;
6240 uint32_t u32Height = 0;
6241
6242 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6243
6244 if (RT_FAILURE(vrc))
6245 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6246 tr("Saved thumbnail data is not available (%Rrc)"),
6247 vrc);
6248
6249 HRESULT hr = S_OK;
6250
6251 *aWidth = u32Width;
6252 *aHeight = u32Height;
6253
6254 if (cbData > 0)
6255 {
6256 /* Convert pixels to the format expected by the API caller. */
6257 if (aBitmapFormat == BitmapFormat_BGR0)
6258 {
6259 /* [0] B, [1] G, [2] R, [3] 0. */
6260 aData.resize(cbData);
6261 memcpy(&aData.front(), pu8Data, cbData);
6262 }
6263 else if (aBitmapFormat == BitmapFormat_BGRA)
6264 {
6265 /* [0] B, [1] G, [2] R, [3] A. */
6266 aData.resize(cbData);
6267 for (uint32_t i = 0; i < cbData; i += 4)
6268 {
6269 aData[i] = pu8Data[i];
6270 aData[i + 1] = pu8Data[i + 1];
6271 aData[i + 2] = pu8Data[i + 2];
6272 aData[i + 3] = 0xff;
6273 }
6274 }
6275 else if (aBitmapFormat == BitmapFormat_RGBA)
6276 {
6277 /* [0] R, [1] G, [2] B, [3] A. */
6278 aData.resize(cbData);
6279 for (uint32_t i = 0; i < cbData; i += 4)
6280 {
6281 aData[i] = pu8Data[i + 2];
6282 aData[i + 1] = pu8Data[i + 1];
6283 aData[i + 2] = pu8Data[i];
6284 aData[i + 3] = 0xff;
6285 }
6286 }
6287 else if (aBitmapFormat == BitmapFormat_PNG)
6288 {
6289 uint8_t *pu8PNG = NULL;
6290 uint32_t cbPNG = 0;
6291 uint32_t cxPNG = 0;
6292 uint32_t cyPNG = 0;
6293
6294 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6295
6296 if (RT_SUCCESS(vrc))
6297 {
6298 aData.resize(cbPNG);
6299 if (cbPNG)
6300 memcpy(&aData.front(), pu8PNG, cbPNG);
6301 }
6302 else
6303 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6304 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6305 vrc);
6306
6307 RTMemFree(pu8PNG);
6308 }
6309 }
6310
6311 freeSavedDisplayScreenshot(pu8Data);
6312
6313 return hr;
6314}
6315
6316HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6317 ULONG *aWidth,
6318 ULONG *aHeight,
6319 std::vector<BitmapFormat_T> &aBitmapFormats)
6320{
6321 if (aScreenId != 0)
6322 return E_NOTIMPL;
6323
6324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6325
6326 uint8_t *pu8Data = NULL;
6327 uint32_t cbData = 0;
6328 uint32_t u32Width = 0;
6329 uint32_t u32Height = 0;
6330
6331 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6332
6333 if (RT_FAILURE(vrc))
6334 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6335 tr("Saved screenshot data is not available (%Rrc)"),
6336 vrc);
6337
6338 *aWidth = u32Width;
6339 *aHeight = u32Height;
6340 aBitmapFormats.resize(1);
6341 aBitmapFormats[0] = BitmapFormat_PNG;
6342
6343 freeSavedDisplayScreenshot(pu8Data);
6344
6345 return S_OK;
6346}
6347
6348HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6349 BitmapFormat_T aBitmapFormat,
6350 ULONG *aWidth,
6351 ULONG *aHeight,
6352 std::vector<BYTE> &aData)
6353{
6354 if (aScreenId != 0)
6355 return E_NOTIMPL;
6356
6357 if (aBitmapFormat != BitmapFormat_PNG)
6358 return E_NOTIMPL;
6359
6360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 uint8_t *pu8Data = NULL;
6363 uint32_t cbData = 0;
6364 uint32_t u32Width = 0;
6365 uint32_t u32Height = 0;
6366
6367 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6368
6369 if (RT_FAILURE(vrc))
6370 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6371 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6372 vrc);
6373
6374 *aWidth = u32Width;
6375 *aHeight = u32Height;
6376
6377 aData.resize(cbData);
6378 if (cbData)
6379 memcpy(&aData.front(), pu8Data, cbData);
6380
6381 freeSavedDisplayScreenshot(pu8Data);
6382
6383 return S_OK;
6384}
6385
6386HRESULT Machine::hotPlugCPU(ULONG aCpu)
6387{
6388 HRESULT rc = S_OK;
6389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6390
6391 if (!mHWData->mCPUHotPlugEnabled)
6392 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6393
6394 if (aCpu >= mHWData->mCPUCount)
6395 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6396
6397 if (mHWData->mCPUAttached[aCpu])
6398 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6399
6400 alock.release();
6401 rc = i_onCPUChange(aCpu, false);
6402 alock.acquire();
6403 if (FAILED(rc)) return rc;
6404
6405 i_setModified(IsModified_MachineData);
6406 mHWData.backup();
6407 mHWData->mCPUAttached[aCpu] = true;
6408
6409 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6410 if (Global::IsOnline(mData->mMachineState))
6411 i_saveSettings(NULL, alock);
6412
6413 return S_OK;
6414}
6415
6416HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6417{
6418 HRESULT rc = S_OK;
6419
6420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6421
6422 if (!mHWData->mCPUHotPlugEnabled)
6423 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6424
6425 if (aCpu >= SchemaDefs::MaxCPUCount)
6426 return setError(E_INVALIDARG,
6427 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6428 SchemaDefs::MaxCPUCount);
6429
6430 if (!mHWData->mCPUAttached[aCpu])
6431 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6432
6433 /* CPU 0 can't be detached */
6434 if (aCpu == 0)
6435 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6436
6437 alock.release();
6438 rc = i_onCPUChange(aCpu, true);
6439 alock.acquire();
6440 if (FAILED(rc)) return rc;
6441
6442 i_setModified(IsModified_MachineData);
6443 mHWData.backup();
6444 mHWData->mCPUAttached[aCpu] = false;
6445
6446 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6447 if (Global::IsOnline(mData->mMachineState))
6448 i_saveSettings(NULL, alock);
6449
6450 return S_OK;
6451}
6452
6453HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6454{
6455 *aAttached = false;
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 /* If hotplug is enabled the CPU is always enabled. */
6460 if (!mHWData->mCPUHotPlugEnabled)
6461 {
6462 if (aCpu < mHWData->mCPUCount)
6463 *aAttached = true;
6464 }
6465 else
6466 {
6467 if (aCpu < SchemaDefs::MaxCPUCount)
6468 *aAttached = mHWData->mCPUAttached[aCpu];
6469 }
6470
6471 return S_OK;
6472}
6473
6474HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6475{
6476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6477
6478 Utf8Str log = i_getLogFilename(aIdx);
6479 if (!RTFileExists(log.c_str()))
6480 log.setNull();
6481 aFilename = log;
6482
6483 return S_OK;
6484}
6485
6486HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6487{
6488 if (aSize < 0)
6489 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6490
6491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6492
6493 HRESULT rc = S_OK;
6494 Utf8Str log = i_getLogFilename(aIdx);
6495
6496 /* do not unnecessarily hold the lock while doing something which does
6497 * not need the lock and potentially takes a long time. */
6498 alock.release();
6499
6500 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6501 * keeps the SOAP reply size under 1M for the webservice (we're using
6502 * base64 encoded strings for binary data for years now, avoiding the
6503 * expansion of each byte array element to approx. 25 bytes of XML. */
6504 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6505 aData.resize(cbData);
6506
6507 RTFILE LogFile;
6508 int vrc = RTFileOpen(&LogFile, log.c_str(),
6509 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6510 if (RT_SUCCESS(vrc))
6511 {
6512 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6513 if (RT_SUCCESS(vrc))
6514 aData.resize(cbData);
6515 else
6516 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6517 tr("Could not read log file '%s' (%Rrc)"),
6518 log.c_str(), vrc);
6519 RTFileClose(LogFile);
6520 }
6521 else
6522 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6523 tr("Could not open log file '%s' (%Rrc)"),
6524 log.c_str(), vrc);
6525
6526 if (FAILED(rc))
6527 aData.resize(0);
6528
6529 return rc;
6530}
6531
6532
6533/**
6534 * Currently this method doesn't attach device to the running VM,
6535 * just makes sure it's plugged on next VM start.
6536 */
6537HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6538{
6539 // lock scope
6540 {
6541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6542
6543 HRESULT rc = i_checkStateDependency(MutableStateDep);
6544 if (FAILED(rc)) return rc;
6545
6546 ChipsetType_T aChipset = ChipsetType_PIIX3;
6547 COMGETTER(ChipsetType)(&aChipset);
6548
6549 if (aChipset != ChipsetType_ICH9)
6550 {
6551 return setError(E_INVALIDARG,
6552 tr("Host PCI attachment only supported with ICH9 chipset"));
6553 }
6554
6555 // check if device with this host PCI address already attached
6556 for (HWData::PCIDeviceAssignmentList::const_iterator
6557 it = mHWData->mPCIDeviceAssignments.begin();
6558 it != mHWData->mPCIDeviceAssignments.end();
6559 ++it)
6560 {
6561 LONG iHostAddress = -1;
6562 ComPtr<PCIDeviceAttachment> pAttach;
6563 pAttach = *it;
6564 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6565 if (iHostAddress == aHostAddress)
6566 return setError(E_INVALIDARG,
6567 tr("Device with host PCI address already attached to this VM"));
6568 }
6569
6570 ComObjPtr<PCIDeviceAttachment> pda;
6571 char name[32];
6572
6573 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6574 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6575 pda.createObject();
6576 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6577 i_setModified(IsModified_MachineData);
6578 mHWData.backup();
6579 mHWData->mPCIDeviceAssignments.push_back(pda);
6580 }
6581
6582 return S_OK;
6583}
6584
6585/**
6586 * Currently this method doesn't detach device from the running VM,
6587 * just makes sure it's not plugged on next VM start.
6588 */
6589HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6590{
6591 ComObjPtr<PCIDeviceAttachment> pAttach;
6592 bool fRemoved = false;
6593 HRESULT rc;
6594
6595 // lock scope
6596 {
6597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6598
6599 rc = i_checkStateDependency(MutableStateDep);
6600 if (FAILED(rc)) return rc;
6601
6602 for (HWData::PCIDeviceAssignmentList::const_iterator
6603 it = mHWData->mPCIDeviceAssignments.begin();
6604 it != mHWData->mPCIDeviceAssignments.end();
6605 ++it)
6606 {
6607 LONG iHostAddress = -1;
6608 pAttach = *it;
6609 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6610 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6611 {
6612 i_setModified(IsModified_MachineData);
6613 mHWData.backup();
6614 mHWData->mPCIDeviceAssignments.remove(pAttach);
6615 fRemoved = true;
6616 break;
6617 }
6618 }
6619 }
6620
6621
6622 /* Fire event outside of the lock */
6623 if (fRemoved)
6624 {
6625 Assert(!pAttach.isNull());
6626 ComPtr<IEventSource> es;
6627 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6628 Assert(SUCCEEDED(rc));
6629 Bstr mid;
6630 rc = this->COMGETTER(Id)(mid.asOutParam());
6631 Assert(SUCCEEDED(rc));
6632 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6633 }
6634
6635 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6636 tr("No host PCI device %08x attached"),
6637 aHostAddress
6638 );
6639}
6640
6641HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6642{
6643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6646 size_t i = 0;
6647 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6648 it = mHWData->mPCIDeviceAssignments.begin();
6649 it != mHWData->mPCIDeviceAssignments.end();
6650 ++it, ++i)
6651 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6652
6653 return S_OK;
6654}
6655
6656HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6657{
6658 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6659
6660 return S_OK;
6661}
6662
6663HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6664{
6665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6666
6667 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6668
6669 return S_OK;
6670}
6671
6672HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6673{
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6676 if (SUCCEEDED(hrc))
6677 {
6678 hrc = mHWData.backupEx();
6679 if (SUCCEEDED(hrc))
6680 {
6681 i_setModified(IsModified_MachineData);
6682 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6683 }
6684 }
6685 return hrc;
6686}
6687
6688HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6689{
6690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6691 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6692 return S_OK;
6693}
6694
6695HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6696{
6697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6698 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6699 if (SUCCEEDED(hrc))
6700 {
6701 hrc = mHWData.backupEx();
6702 if (SUCCEEDED(hrc))
6703 {
6704 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6705 if (SUCCEEDED(hrc))
6706 i_setModified(IsModified_MachineData);
6707 }
6708 }
6709 return hrc;
6710}
6711
6712HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6713{
6714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6715
6716 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6717
6718 return S_OK;
6719}
6720
6721HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6722{
6723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6724 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6725 if (SUCCEEDED(hrc))
6726 {
6727 hrc = mHWData.backupEx();
6728 if (SUCCEEDED(hrc))
6729 {
6730 i_setModified(IsModified_MachineData);
6731 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6732 }
6733 }
6734 return hrc;
6735}
6736
6737HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6738{
6739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6740
6741 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6742
6743 return S_OK;
6744}
6745
6746HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6747{
6748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6749
6750 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6751 if ( SUCCEEDED(hrc)
6752 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6753 {
6754 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6755 int vrc;
6756
6757 if (aAutostartEnabled)
6758 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6759 else
6760 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6761
6762 if (RT_SUCCESS(vrc))
6763 {
6764 hrc = mHWData.backupEx();
6765 if (SUCCEEDED(hrc))
6766 {
6767 i_setModified(IsModified_MachineData);
6768 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6769 }
6770 }
6771 else if (vrc == VERR_NOT_SUPPORTED)
6772 hrc = setError(VBOX_E_NOT_SUPPORTED,
6773 tr("The VM autostart feature is not supported on this platform"));
6774 else if (vrc == VERR_PATH_NOT_FOUND)
6775 hrc = setError(E_FAIL,
6776 tr("The path to the autostart database is not set"));
6777 else
6778 hrc = setError(E_UNEXPECTED,
6779 aAutostartEnabled ?
6780 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6781 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6782 mUserData->s.strName.c_str(), vrc);
6783 }
6784 return hrc;
6785}
6786
6787HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6788{
6789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6790
6791 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6792
6793 return S_OK;
6794}
6795
6796HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6797{
6798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6800 if (SUCCEEDED(hrc))
6801 {
6802 hrc = mHWData.backupEx();
6803 if (SUCCEEDED(hrc))
6804 {
6805 i_setModified(IsModified_MachineData);
6806 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6807 }
6808 }
6809 return hrc;
6810}
6811
6812HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6813{
6814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6815
6816 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6817
6818 return S_OK;
6819}
6820
6821HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6822{
6823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6824 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6825 if ( SUCCEEDED(hrc)
6826 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6827 {
6828 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6829 int vrc;
6830
6831 if (aAutostopType != AutostopType_Disabled)
6832 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6833 else
6834 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6835
6836 if (RT_SUCCESS(vrc))
6837 {
6838 hrc = mHWData.backupEx();
6839 if (SUCCEEDED(hrc))
6840 {
6841 i_setModified(IsModified_MachineData);
6842 mHWData->mAutostart.enmAutostopType = aAutostopType;
6843 }
6844 }
6845 else if (vrc == VERR_NOT_SUPPORTED)
6846 hrc = setError(VBOX_E_NOT_SUPPORTED,
6847 tr("The VM autostop feature is not supported on this platform"));
6848 else if (vrc == VERR_PATH_NOT_FOUND)
6849 hrc = setError(E_FAIL,
6850 tr("The path to the autostart database is not set"));
6851 else
6852 hrc = setError(E_UNEXPECTED,
6853 aAutostopType != AutostopType_Disabled ?
6854 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6855 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6856 mUserData->s.strName.c_str(), vrc);
6857 }
6858 return hrc;
6859}
6860
6861HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6862{
6863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6864
6865 aDefaultFrontend = mHWData->mDefaultFrontend;
6866
6867 return S_OK;
6868}
6869
6870HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6871{
6872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6873 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6874 if (SUCCEEDED(hrc))
6875 {
6876 hrc = mHWData.backupEx();
6877 if (SUCCEEDED(hrc))
6878 {
6879 i_setModified(IsModified_MachineData);
6880 mHWData->mDefaultFrontend = aDefaultFrontend;
6881 }
6882 }
6883 return hrc;
6884}
6885
6886HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6887{
6888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 size_t cbIcon = mUserData->s.ovIcon.size();
6890 aIcon.resize(cbIcon);
6891 if (cbIcon)
6892 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6893 return S_OK;
6894}
6895
6896HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6897{
6898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6899 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6900 if (SUCCEEDED(hrc))
6901 {
6902 i_setModified(IsModified_MachineData);
6903 mUserData.backup();
6904 size_t cbIcon = aIcon.size();
6905 mUserData->s.ovIcon.resize(cbIcon);
6906 if (cbIcon)
6907 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6908 }
6909 return hrc;
6910}
6911
6912HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6913{
6914#ifdef VBOX_WITH_USB
6915 *aUSBProxyAvailable = true;
6916#else
6917 *aUSBProxyAvailable = false;
6918#endif
6919 return S_OK;
6920}
6921
6922HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6923{
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925
6926 *aVMProcessPriority = mUserData->s.enmVMPriority;
6927
6928 return S_OK;
6929}
6930
6931HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6932{
6933 RT_NOREF(aVMProcessPriority);
6934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6935 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6936 if (SUCCEEDED(hrc))
6937 {
6938 hrc = mUserData.backupEx();
6939 if (SUCCEEDED(hrc))
6940 {
6941 i_setModified(IsModified_MachineData);
6942 mUserData->s.enmVMPriority = aVMProcessPriority;
6943 }
6944 }
6945 alock.release();
6946 if (SUCCEEDED(hrc))
6947 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6948 return hrc;
6949}
6950
6951HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6952 ComPtr<IProgress> &aProgress)
6953{
6954 ComObjPtr<Progress> pP;
6955 Progress *ppP = pP;
6956 IProgress *iP = static_cast<IProgress *>(ppP);
6957 IProgress **pProgress = &iP;
6958
6959 IMachine *pTarget = aTarget;
6960
6961 /* Convert the options. */
6962 RTCList<CloneOptions_T> optList;
6963 if (aOptions.size())
6964 for (size_t i = 0; i < aOptions.size(); ++i)
6965 optList.append(aOptions[i]);
6966
6967 if (optList.contains(CloneOptions_Link))
6968 {
6969 if (!i_isSnapshotMachine())
6970 return setError(E_INVALIDARG,
6971 tr("Linked clone can only be created from a snapshot"));
6972 if (aMode != CloneMode_MachineState)
6973 return setError(E_INVALIDARG,
6974 tr("Linked clone can only be created for a single machine state"));
6975 }
6976 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6977
6978 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6979
6980 HRESULT rc = pWorker->start(pProgress);
6981
6982 pP = static_cast<Progress *>(*pProgress);
6983 pP.queryInterfaceTo(aProgress.asOutParam());
6984
6985 return rc;
6986
6987}
6988
6989HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6990 const com::Utf8Str &aType,
6991 ComPtr<IProgress> &aProgress)
6992{
6993 LogFlowThisFuncEnter();
6994
6995 ComObjPtr<Progress> ptrProgress;
6996 HRESULT hrc = ptrProgress.createObject();
6997 if (SUCCEEDED(hrc))
6998 {
6999 com::Utf8Str strDefaultPath;
7000 if (aTargetPath.isEmpty())
7001 i_calculateFullPath(".", strDefaultPath);
7002
7003 /* Initialize our worker task */
7004 MachineMoveVM *pTask = NULL;
7005 try
7006 {
7007 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7008 }
7009 catch (std::bad_alloc &)
7010 {
7011 return E_OUTOFMEMORY;
7012 }
7013
7014 hrc = pTask->init();//no exceptions are thrown
7015
7016 if (SUCCEEDED(hrc))
7017 {
7018 hrc = pTask->createThread();
7019 pTask = NULL; /* Consumed by createThread(). */
7020 if (SUCCEEDED(hrc))
7021 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7022 else
7023 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7024 }
7025 else
7026 delete pTask;
7027 }
7028
7029 LogFlowThisFuncLeave();
7030 return hrc;
7031
7032}
7033
7034HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7035{
7036 NOREF(aProgress);
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 // This check should always fail.
7040 HRESULT rc = i_checkStateDependency(MutableStateDep);
7041 if (FAILED(rc)) return rc;
7042
7043 AssertFailedReturn(E_NOTIMPL);
7044}
7045
7046HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7047{
7048 NOREF(aSavedStateFile);
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050
7051 // This check should always fail.
7052 HRESULT rc = i_checkStateDependency(MutableStateDep);
7053 if (FAILED(rc)) return rc;
7054
7055 AssertFailedReturn(E_NOTIMPL);
7056}
7057
7058HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7059{
7060 NOREF(aFRemoveFile);
7061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7062
7063 // This check should always fail.
7064 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7065 if (FAILED(rc)) return rc;
7066
7067 AssertFailedReturn(E_NOTIMPL);
7068}
7069
7070// public methods for internal purposes
7071/////////////////////////////////////////////////////////////////////////////
7072
7073/**
7074 * Adds the given IsModified_* flag to the dirty flags of the machine.
7075 * This must be called either during i_loadSettings or under the machine write lock.
7076 * @param fl Flag
7077 * @param fAllowStateModification If state modifications are allowed.
7078 */
7079void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7080{
7081 mData->flModifications |= fl;
7082 if (fAllowStateModification && i_isStateModificationAllowed())
7083 mData->mCurrentStateModified = true;
7084}
7085
7086/**
7087 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7088 * care of the write locking.
7089 *
7090 * @param fModification The flag to add.
7091 * @param fAllowStateModification If state modifications are allowed.
7092 */
7093void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7094{
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 i_setModified(fModification, fAllowStateModification);
7097}
7098
7099/**
7100 * Saves the registry entry of this machine to the given configuration node.
7101 *
7102 * @param data Machine registry data.
7103 *
7104 * @note locks this object for reading.
7105 */
7106HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7107{
7108 AutoLimitedCaller autoCaller(this);
7109 AssertComRCReturnRC(autoCaller.rc());
7110
7111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7112
7113 data.uuid = mData->mUuid;
7114 data.strSettingsFile = mData->m_strConfigFile;
7115
7116 return S_OK;
7117}
7118
7119/**
7120 * Calculates the absolute path of the given path taking the directory of the
7121 * machine settings file as the current directory.
7122 *
7123 * @param strPath Path to calculate the absolute path for.
7124 * @param aResult Where to put the result (used only on success, can be the
7125 * same Utf8Str instance as passed in @a aPath).
7126 * @return IPRT result.
7127 *
7128 * @note Locks this object for reading.
7129 */
7130int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7131{
7132 AutoCaller autoCaller(this);
7133 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7134
7135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7136
7137 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7138
7139 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7140
7141 strSettingsDir.stripFilename();
7142 char szFolder[RTPATH_MAX];
7143 size_t cbFolder = sizeof(szFolder);
7144 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7145 if (RT_SUCCESS(vrc))
7146 aResult = szFolder;
7147
7148 return vrc;
7149}
7150
7151/**
7152 * Copies strSource to strTarget, making it relative to the machine folder
7153 * if it is a subdirectory thereof, or simply copying it otherwise.
7154 *
7155 * @param strSource Path to evaluate and copy.
7156 * @param strTarget Buffer to receive target path.
7157 *
7158 * @note Locks this object for reading.
7159 */
7160void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7161 Utf8Str &strTarget)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturn(autoCaller.rc(), (void)0);
7165
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7169 // use strTarget as a temporary buffer to hold the machine settings dir
7170 strTarget = mData->m_strConfigFileFull;
7171 strTarget.stripFilename();
7172 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7173 {
7174 // is relative: then append what's left
7175 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7176 // for empty paths (only possible for subdirs) use "." to avoid
7177 // triggering default settings for not present config attributes.
7178 if (strTarget.isEmpty())
7179 strTarget = ".";
7180 }
7181 else
7182 // is not relative: then overwrite
7183 strTarget = strSource;
7184}
7185
7186/**
7187 * Returns the full path to the machine's log folder in the
7188 * \a aLogFolder argument.
7189 */
7190void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7191{
7192 AutoCaller autoCaller(this);
7193 AssertComRCReturnVoid(autoCaller.rc());
7194
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 char szTmp[RTPATH_MAX];
7198 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7199 if (RT_SUCCESS(vrc))
7200 {
7201 if (szTmp[0] && !mUserData.isNull())
7202 {
7203 char szTmp2[RTPATH_MAX];
7204 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7205 if (RT_SUCCESS(vrc))
7206 aLogFolder.printf("%s%c%s",
7207 szTmp2,
7208 RTPATH_DELIMITER,
7209 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7210 }
7211 else
7212 vrc = VERR_PATH_IS_RELATIVE;
7213 }
7214
7215 if (RT_FAILURE(vrc))
7216 {
7217 // fallback if VBOX_USER_LOGHOME is not set or invalid
7218 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7219 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7220 aLogFolder.append(RTPATH_DELIMITER);
7221 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7222 }
7223}
7224
7225/**
7226 * Returns the full path to the machine's log file for an given index.
7227 */
7228Utf8Str Machine::i_getLogFilename(ULONG idx)
7229{
7230 Utf8Str logFolder;
7231 getLogFolder(logFolder);
7232 Assert(logFolder.length());
7233
7234 Utf8Str log;
7235 if (idx == 0)
7236 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7237#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7238 else if (idx == 1)
7239 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7240 else
7241 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7242#else
7243 else
7244 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7245#endif
7246 return log;
7247}
7248
7249/**
7250 * Returns the full path to the machine's hardened log file.
7251 */
7252Utf8Str Machine::i_getHardeningLogFilename(void)
7253{
7254 Utf8Str strFilename;
7255 getLogFolder(strFilename);
7256 Assert(strFilename.length());
7257 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7258 return strFilename;
7259}
7260
7261/**
7262 * Returns the default NVRAM filename based on the location of the VM config.
7263 * Note that this is a relative path.
7264 */
7265Utf8Str Machine::i_getDefaultNVRAMFilename()
7266{
7267 AutoCaller autoCaller(this);
7268 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7269
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271
7272 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7273 || i_isSnapshotMachine())
7274 return Utf8Str::Empty;
7275
7276 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7277 strNVRAMFilePath.stripPath();
7278 strNVRAMFilePath.stripSuffix();
7279 strNVRAMFilePath += ".nvram";
7280
7281 return strNVRAMFilePath;
7282}
7283
7284/**
7285 * Returns the NVRAM filename for a new snapshot. This intentionally works
7286 * similarly to the saved state file naming. Note that this is usually
7287 * a relative path, unless the snapshot folder is absolute.
7288 */
7289Utf8Str Machine::i_getSnapshotNVRAMFilename()
7290{
7291 AutoCaller autoCaller(this);
7292 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7293
7294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7295
7296 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7297 return Utf8Str::Empty;
7298
7299 RTTIMESPEC ts;
7300 RTTimeNow(&ts);
7301 RTTIME time;
7302 RTTimeExplode(&time, &ts);
7303
7304 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7305 strNVRAMFilePath += RTPATH_DELIMITER;
7306 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7307 time.i32Year, time.u8Month, time.u8MonthDay,
7308 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7309
7310 return strNVRAMFilePath;
7311}
7312
7313/**
7314 * Returns the version of the settings file.
7315 */
7316SettingsVersion_T Machine::i_getSettingsVersion(void)
7317{
7318 AutoCaller autoCaller(this);
7319 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7320
7321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7322
7323 return mData->pMachineConfigFile->getSettingsVersion();
7324}
7325
7326/**
7327 * Composes a unique saved state filename based on the current system time. The filename is
7328 * granular to the second so this will work so long as no more than one snapshot is taken on
7329 * a machine per second.
7330 *
7331 * Before version 4.1, we used this formula for saved state files:
7332 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7333 * which no longer works because saved state files can now be shared between the saved state of the
7334 * "saved" machine and an online snapshot, and the following would cause problems:
7335 * 1) save machine
7336 * 2) create online snapshot from that machine state --> reusing saved state file
7337 * 3) save machine again --> filename would be reused, breaking the online snapshot
7338 *
7339 * So instead we now use a timestamp.
7340 *
7341 * @param strStateFilePath
7342 */
7343
7344void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7345{
7346 AutoCaller autoCaller(this);
7347 AssertComRCReturnVoid(autoCaller.rc());
7348
7349 {
7350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7351 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7352 }
7353
7354 RTTIMESPEC ts;
7355 RTTimeNow(&ts);
7356 RTTIME time;
7357 RTTimeExplode(&time, &ts);
7358
7359 strStateFilePath += RTPATH_DELIMITER;
7360 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7361 time.i32Year, time.u8Month, time.u8MonthDay,
7362 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7363}
7364
7365/**
7366 * Returns whether at least one USB controller is present for the VM.
7367 */
7368bool Machine::i_isUSBControllerPresent()
7369{
7370 AutoCaller autoCaller(this);
7371 AssertComRCReturn(autoCaller.rc(), false);
7372
7373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7374
7375 return (mUSBControllers->size() > 0);
7376}
7377
7378
7379/**
7380 * @note Locks this object for writing, calls the client process
7381 * (inside the lock).
7382 */
7383HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7384 const Utf8Str &strFrontend,
7385 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7386 ProgressProxy *aProgress)
7387{
7388 LogFlowThisFuncEnter();
7389
7390 AssertReturn(aControl, E_FAIL);
7391 AssertReturn(aProgress, E_FAIL);
7392 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7393
7394 AutoCaller autoCaller(this);
7395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7396
7397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7398
7399 if (!mData->mRegistered)
7400 return setError(E_UNEXPECTED,
7401 tr("The machine '%s' is not registered"),
7402 mUserData->s.strName.c_str());
7403
7404 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7405
7406 /* The process started when launching a VM with separate UI/VM processes is always
7407 * the UI process, i.e. needs special handling as it won't claim the session. */
7408 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7409
7410 if (fSeparate)
7411 {
7412 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7413 return setError(VBOX_E_INVALID_OBJECT_STATE,
7414 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7415 mUserData->s.strName.c_str());
7416 }
7417 else
7418 {
7419 if ( mData->mSession.mState == SessionState_Locked
7420 || mData->mSession.mState == SessionState_Spawning
7421 || mData->mSession.mState == SessionState_Unlocking)
7422 return setError(VBOX_E_INVALID_OBJECT_STATE,
7423 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7424 mUserData->s.strName.c_str());
7425
7426 /* may not be busy */
7427 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7428 }
7429
7430 /* Hardening logging */
7431#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7432 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7433 {
7434 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7435 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7436 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7437 {
7438 Utf8Str strStartupLogDir = strHardeningLogFile;
7439 strStartupLogDir.stripFilename();
7440 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7441 file without stripping the file. */
7442 }
7443 strSupHardeningLogArg.append(strHardeningLogFile);
7444
7445 /* Remove legacy log filename to avoid confusion. */
7446 Utf8Str strOldStartupLogFile;
7447 getLogFolder(strOldStartupLogFile);
7448 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7449 RTFileDelete(strOldStartupLogFile.c_str());
7450 }
7451#else
7452 Utf8Str strSupHardeningLogArg;
7453#endif
7454
7455 Utf8Str strAppOverride;
7456#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7457 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7458#endif
7459
7460 bool fUseVBoxSDS = false;
7461 Utf8Str strCanonicalName;
7462 if (false)
7463 { }
7464#ifdef VBOX_WITH_QTGUI
7465 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7466 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7467 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7468 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7469 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7470 {
7471 strCanonicalName = "GUI/Qt";
7472 fUseVBoxSDS = true;
7473 }
7474#endif
7475#ifdef VBOX_WITH_VBOXSDL
7476 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7477 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7478 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7479 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7480 {
7481 strCanonicalName = "GUI/SDL";
7482 fUseVBoxSDS = true;
7483 }
7484#endif
7485#ifdef VBOX_WITH_HEADLESS
7486 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7487 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7488 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7489 {
7490 strCanonicalName = "headless";
7491 }
7492#endif
7493 else
7494 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7495
7496 Utf8Str idStr = mData->mUuid.toString();
7497 Utf8Str const &strMachineName = mUserData->s.strName;
7498 RTPROCESS pid = NIL_RTPROCESS;
7499
7500#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7501 RT_NOREF(fUseVBoxSDS);
7502#else
7503 DWORD idCallerSession = ~(DWORD)0;
7504 if (fUseVBoxSDS)
7505 {
7506 /*
7507 * The VBoxSDS should be used for process launching the VM with
7508 * GUI only if the caller and the VBoxSDS are in different Windows
7509 * sessions and the caller in the interactive one.
7510 */
7511 fUseVBoxSDS = false;
7512
7513 /* Get windows session of the current process. The process token used
7514 due to several reasons:
7515 1. The token is absent for the current thread except someone set it
7516 for us.
7517 2. Needs to get the id of the session where the process is started.
7518 We only need to do this once, though. */
7519 static DWORD s_idCurrentSession = ~(DWORD)0;
7520 DWORD idCurrentSession = s_idCurrentSession;
7521 if (idCurrentSession == ~(DWORD)0)
7522 {
7523 HANDLE hCurrentProcessToken = NULL;
7524 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7525 {
7526 DWORD cbIgn = 0;
7527 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7528 s_idCurrentSession = idCurrentSession;
7529 else
7530 {
7531 idCurrentSession = ~(DWORD)0;
7532 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7533 }
7534 CloseHandle(hCurrentProcessToken);
7535 }
7536 else
7537 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7538 }
7539
7540 /* get the caller's session */
7541 HRESULT hrc = CoImpersonateClient();
7542 if (SUCCEEDED(hrc))
7543 {
7544 HANDLE hCallerThreadToken;
7545 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7546 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7547 &hCallerThreadToken))
7548 {
7549 SetLastError(NO_ERROR);
7550 DWORD cbIgn = 0;
7551 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7552 {
7553 /* Only need to use SDS if the session ID differs: */
7554 if (idCurrentSession != idCallerSession)
7555 {
7556 fUseVBoxSDS = false;
7557
7558 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7559 DWORD cbTokenGroups = 0;
7560 PTOKEN_GROUPS pTokenGroups = NULL;
7561 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7562 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7563 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7564 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7565 {
7566 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7567 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7568 PSID pInteractiveSid = NULL;
7569 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7570 {
7571 /* Iterate over the groups looking for the interactive SID: */
7572 fUseVBoxSDS = false;
7573 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7574 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7575 {
7576 fUseVBoxSDS = true;
7577 break;
7578 }
7579 FreeSid(pInteractiveSid);
7580 }
7581 }
7582 else
7583 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7584 RTMemTmpFree(pTokenGroups);
7585 }
7586 }
7587 else
7588 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7589 CloseHandle(hCallerThreadToken);
7590 }
7591 else
7592 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7593 CoRevertToSelf();
7594 }
7595 else
7596 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7597 }
7598 if (fUseVBoxSDS)
7599 {
7600 /* connect to VBoxSDS */
7601 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7602 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7603 if (FAILED(rc))
7604 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7605 strMachineName.c_str());
7606
7607 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7608 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7609 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7610 service to access the files. */
7611 rc = CoSetProxyBlanket(pVBoxSDS,
7612 RPC_C_AUTHN_DEFAULT,
7613 RPC_C_AUTHZ_DEFAULT,
7614 COLE_DEFAULT_PRINCIPAL,
7615 RPC_C_AUTHN_LEVEL_DEFAULT,
7616 RPC_C_IMP_LEVEL_IMPERSONATE,
7617 NULL,
7618 EOAC_DEFAULT);
7619 if (FAILED(rc))
7620 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7621
7622 size_t const cEnvVars = aEnvironmentChanges.size();
7623 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7624 for (size_t i = 0; i < cEnvVars; i++)
7625 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7626
7627 ULONG uPid = 0;
7628 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7629 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7630 idCallerSession, &uPid);
7631 if (FAILED(rc))
7632 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7633 pid = (RTPROCESS)uPid;
7634 }
7635 else
7636#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7637 {
7638 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7639 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7640 if (RT_FAILURE(vrc))
7641 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7642 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7643 }
7644
7645 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7646 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7647
7648 if (!fSeparate)
7649 {
7650 /*
7651 * Note that we don't release the lock here before calling the client,
7652 * because it doesn't need to call us back if called with a NULL argument.
7653 * Releasing the lock here is dangerous because we didn't prepare the
7654 * launch data yet, but the client we've just started may happen to be
7655 * too fast and call LockMachine() that will fail (because of PID, etc.),
7656 * so that the Machine will never get out of the Spawning session state.
7657 */
7658
7659 /* inform the session that it will be a remote one */
7660 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7661#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7662 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7663#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7664 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7665#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7666 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7667
7668 if (FAILED(rc))
7669 {
7670 /* restore the session state */
7671 mData->mSession.mState = SessionState_Unlocked;
7672 alock.release();
7673 mParent->i_addProcessToReap(pid);
7674 /* The failure may occur w/o any error info (from RPC), so provide one */
7675 return setError(VBOX_E_VM_ERROR,
7676 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7677 }
7678
7679 /* attach launch data to the machine */
7680 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7681 mData->mSession.mRemoteControls.push_back(aControl);
7682 mData->mSession.mProgress = aProgress;
7683 mData->mSession.mPID = pid;
7684 mData->mSession.mState = SessionState_Spawning;
7685 Assert(strCanonicalName.isNotEmpty());
7686 mData->mSession.mName = strCanonicalName;
7687 }
7688 else
7689 {
7690 /* For separate UI process we declare the launch as completed instantly, as the
7691 * actual headless VM start may or may not come. No point in remembering anything
7692 * yet, as what matters for us is when the headless VM gets started. */
7693 aProgress->i_notifyComplete(S_OK);
7694 }
7695
7696 alock.release();
7697 mParent->i_addProcessToReap(pid);
7698
7699 LogFlowThisFuncLeave();
7700 return S_OK;
7701}
7702
7703/**
7704 * Returns @c true if the given session machine instance has an open direct
7705 * session (and optionally also for direct sessions which are closing) and
7706 * returns the session control machine instance if so.
7707 *
7708 * Note that when the method returns @c false, the arguments remain unchanged.
7709 *
7710 * @param aMachine Session machine object.
7711 * @param aControl Direct session control object (optional).
7712 * @param aRequireVM If true then only allow VM sessions.
7713 * @param aAllowClosing If true then additionally a session which is currently
7714 * being closed will also be allowed.
7715 *
7716 * @note locks this object for reading.
7717 */
7718bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7719 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7720 bool aRequireVM /*= false*/,
7721 bool aAllowClosing /*= false*/)
7722{
7723 AutoLimitedCaller autoCaller(this);
7724 AssertComRCReturn(autoCaller.rc(), false);
7725
7726 /* just return false for inaccessible machines */
7727 if (getObjectState().getState() != ObjectState::Ready)
7728 return false;
7729
7730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7731
7732 if ( ( mData->mSession.mState == SessionState_Locked
7733 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7734 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7735 )
7736 {
7737 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7738
7739 aMachine = mData->mSession.mMachine;
7740
7741 if (aControl != NULL)
7742 *aControl = mData->mSession.mDirectControl;
7743
7744 return true;
7745 }
7746
7747 return false;
7748}
7749
7750/**
7751 * Returns @c true if the given machine has an spawning direct session.
7752 *
7753 * @note locks this object for reading.
7754 */
7755bool Machine::i_isSessionSpawning()
7756{
7757 AutoLimitedCaller autoCaller(this);
7758 AssertComRCReturn(autoCaller.rc(), false);
7759
7760 /* just return false for inaccessible machines */
7761 if (getObjectState().getState() != ObjectState::Ready)
7762 return false;
7763
7764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7765
7766 if (mData->mSession.mState == SessionState_Spawning)
7767 return true;
7768
7769 return false;
7770}
7771
7772/**
7773 * Called from the client watcher thread to check for unexpected client process
7774 * death during Session_Spawning state (e.g. before it successfully opened a
7775 * direct session).
7776 *
7777 * On Win32 and on OS/2, this method is called only when we've got the
7778 * direct client's process termination notification, so it always returns @c
7779 * true.
7780 *
7781 * On other platforms, this method returns @c true if the client process is
7782 * terminated and @c false if it's still alive.
7783 *
7784 * @note Locks this object for writing.
7785 */
7786bool Machine::i_checkForSpawnFailure()
7787{
7788 AutoCaller autoCaller(this);
7789 if (!autoCaller.isOk())
7790 {
7791 /* nothing to do */
7792 LogFlowThisFunc(("Already uninitialized!\n"));
7793 return true;
7794 }
7795
7796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 if (mData->mSession.mState != SessionState_Spawning)
7799 {
7800 /* nothing to do */
7801 LogFlowThisFunc(("Not spawning any more!\n"));
7802 return true;
7803 }
7804
7805 HRESULT rc = S_OK;
7806
7807 /* PID not yet initialized, skip check. */
7808 if (mData->mSession.mPID == NIL_RTPROCESS)
7809 return false;
7810
7811 RTPROCSTATUS status;
7812 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7813
7814 if (vrc != VERR_PROCESS_RUNNING)
7815 {
7816 Utf8Str strExtraInfo;
7817
7818#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7819 /* If the startup logfile exists and is of non-zero length, tell the
7820 user to look there for more details to encourage them to attach it
7821 when reporting startup issues. */
7822 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7823 uint64_t cbStartupLogFile = 0;
7824 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7825 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7826 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7827#endif
7828
7829 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7830 rc = setError(E_FAIL,
7831 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7832 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7833 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7834 rc = setError(E_FAIL,
7835 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7836 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7837 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7838 rc = setError(E_FAIL,
7839 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7840 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7841 else
7842 rc = setErrorBoth(E_FAIL, vrc,
7843 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7844 i_getName().c_str(), vrc, strExtraInfo.c_str());
7845 }
7846
7847 if (FAILED(rc))
7848 {
7849 /* Close the remote session, remove the remote control from the list
7850 * and reset session state to Closed (@note keep the code in sync with
7851 * the relevant part in LockMachine()). */
7852
7853 Assert(mData->mSession.mRemoteControls.size() == 1);
7854 if (mData->mSession.mRemoteControls.size() == 1)
7855 {
7856 ErrorInfoKeeper eik;
7857 mData->mSession.mRemoteControls.front()->Uninitialize();
7858 }
7859
7860 mData->mSession.mRemoteControls.clear();
7861 mData->mSession.mState = SessionState_Unlocked;
7862
7863 /* finalize the progress after setting the state */
7864 if (!mData->mSession.mProgress.isNull())
7865 {
7866 mData->mSession.mProgress->notifyComplete(rc);
7867 mData->mSession.mProgress.setNull();
7868 }
7869
7870 mData->mSession.mPID = NIL_RTPROCESS;
7871
7872 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7873 return true;
7874 }
7875
7876 return false;
7877}
7878
7879/**
7880 * Checks whether the machine can be registered. If so, commits and saves
7881 * all settings.
7882 *
7883 * @note Must be called from mParent's write lock. Locks this object and
7884 * children for writing.
7885 */
7886HRESULT Machine::i_prepareRegister()
7887{
7888 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7889
7890 AutoLimitedCaller autoCaller(this);
7891 AssertComRCReturnRC(autoCaller.rc());
7892
7893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7894
7895 /* wait for state dependents to drop to zero */
7896 i_ensureNoStateDependencies(alock);
7897
7898 if (!mData->mAccessible)
7899 return setError(VBOX_E_INVALID_OBJECT_STATE,
7900 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7901 mUserData->s.strName.c_str(),
7902 mData->mUuid.toString().c_str());
7903
7904 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7905
7906 if (mData->mRegistered)
7907 return setError(VBOX_E_INVALID_OBJECT_STATE,
7908 tr("The machine '%s' with UUID {%s} is already registered"),
7909 mUserData->s.strName.c_str(),
7910 mData->mUuid.toString().c_str());
7911
7912 HRESULT rc = S_OK;
7913
7914 // Ensure the settings are saved. If we are going to be registered and
7915 // no config file exists yet, create it by calling i_saveSettings() too.
7916 if ( (mData->flModifications)
7917 || (!mData->pMachineConfigFile->fileExists())
7918 )
7919 {
7920 rc = i_saveSettings(NULL, alock);
7921 // no need to check whether VirtualBox.xml needs saving too since
7922 // we can't have a machine XML file rename pending
7923 if (FAILED(rc)) return rc;
7924 }
7925
7926 /* more config checking goes here */
7927
7928 if (SUCCEEDED(rc))
7929 {
7930 /* we may have had implicit modifications we want to fix on success */
7931 i_commit();
7932
7933 mData->mRegistered = true;
7934 }
7935 else
7936 {
7937 /* we may have had implicit modifications we want to cancel on failure*/
7938 i_rollback(false /* aNotify */);
7939 }
7940
7941 return rc;
7942}
7943
7944/**
7945 * Increases the number of objects dependent on the machine state or on the
7946 * registered state. Guarantees that these two states will not change at least
7947 * until #i_releaseStateDependency() is called.
7948 *
7949 * Depending on the @a aDepType value, additional state checks may be made.
7950 * These checks will set extended error info on failure. See
7951 * #i_checkStateDependency() for more info.
7952 *
7953 * If this method returns a failure, the dependency is not added and the caller
7954 * is not allowed to rely on any particular machine state or registration state
7955 * value and may return the failed result code to the upper level.
7956 *
7957 * @param aDepType Dependency type to add.
7958 * @param aState Current machine state (NULL if not interested).
7959 * @param aRegistered Current registered state (NULL if not interested).
7960 *
7961 * @note Locks this object for writing.
7962 */
7963HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7964 MachineState_T *aState /* = NULL */,
7965 BOOL *aRegistered /* = NULL */)
7966{
7967 AutoCaller autoCaller(this);
7968 AssertComRCReturnRC(autoCaller.rc());
7969
7970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7971
7972 HRESULT rc = i_checkStateDependency(aDepType);
7973 if (FAILED(rc)) return rc;
7974
7975 {
7976 if (mData->mMachineStateChangePending != 0)
7977 {
7978 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7979 * drop to zero so don't add more. It may make sense to wait a bit
7980 * and retry before reporting an error (since the pending state
7981 * transition should be really quick) but let's just assert for
7982 * now to see if it ever happens on practice. */
7983
7984 AssertFailed();
7985
7986 return setError(E_ACCESSDENIED,
7987 tr("Machine state change is in progress. Please retry the operation later."));
7988 }
7989
7990 ++mData->mMachineStateDeps;
7991 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7992 }
7993
7994 if (aState)
7995 *aState = mData->mMachineState;
7996 if (aRegistered)
7997 *aRegistered = mData->mRegistered;
7998
7999 return S_OK;
8000}
8001
8002/**
8003 * Decreases the number of objects dependent on the machine state.
8004 * Must always complete the #i_addStateDependency() call after the state
8005 * dependency is no more necessary.
8006 */
8007void Machine::i_releaseStateDependency()
8008{
8009 AutoCaller autoCaller(this);
8010 AssertComRCReturnVoid(autoCaller.rc());
8011
8012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8013
8014 /* releaseStateDependency() w/o addStateDependency()? */
8015 AssertReturnVoid(mData->mMachineStateDeps != 0);
8016 -- mData->mMachineStateDeps;
8017
8018 if (mData->mMachineStateDeps == 0)
8019 {
8020 /* inform i_ensureNoStateDependencies() that there are no more deps */
8021 if (mData->mMachineStateChangePending != 0)
8022 {
8023 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8024 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8025 }
8026 }
8027}
8028
8029Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8030{
8031 /* start with nothing found */
8032 Utf8Str strResult("");
8033
8034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8035
8036 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8037 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8038 // found:
8039 strResult = it->second; // source is a Utf8Str
8040
8041 return strResult;
8042}
8043
8044// protected methods
8045/////////////////////////////////////////////////////////////////////////////
8046
8047/**
8048 * Performs machine state checks based on the @a aDepType value. If a check
8049 * fails, this method will set extended error info, otherwise it will return
8050 * S_OK. It is supposed, that on failure, the caller will immediately return
8051 * the return value of this method to the upper level.
8052 *
8053 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8054 *
8055 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8056 * current state of this machine object allows to change settings of the
8057 * machine (i.e. the machine is not registered, or registered but not running
8058 * and not saved). It is useful to call this method from Machine setters
8059 * before performing any change.
8060 *
8061 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8062 * as for MutableStateDep except that if the machine is saved, S_OK is also
8063 * returned. This is useful in setters which allow changing machine
8064 * properties when it is in the saved state.
8065 *
8066 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8067 * if the current state of this machine object allows to change runtime
8068 * changeable settings of the machine (i.e. the machine is not registered, or
8069 * registered but either running or not running and not saved). It is useful
8070 * to call this method from Machine setters before performing any changes to
8071 * runtime changeable settings.
8072 *
8073 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8074 * the same as for MutableOrRunningStateDep except that if the machine is
8075 * saved, S_OK is also returned. This is useful in setters which allow
8076 * changing runtime and saved state changeable machine properties.
8077 *
8078 * @param aDepType Dependency type to check.
8079 *
8080 * @note Non Machine based classes should use #i_addStateDependency() and
8081 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8082 * template.
8083 *
8084 * @note This method must be called from under this object's read or write
8085 * lock.
8086 */
8087HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8088{
8089 switch (aDepType)
8090 {
8091 case AnyStateDep:
8092 {
8093 break;
8094 }
8095 case MutableStateDep:
8096 {
8097 if ( mData->mRegistered
8098 && ( !i_isSessionMachine()
8099 || ( mData->mMachineState != MachineState_Aborted
8100 && mData->mMachineState != MachineState_Teleported
8101 && mData->mMachineState != MachineState_PoweredOff
8102 )
8103 )
8104 )
8105 return setError(VBOX_E_INVALID_VM_STATE,
8106 tr("The machine is not mutable (state is %s)"),
8107 Global::stringifyMachineState(mData->mMachineState));
8108 break;
8109 }
8110 case MutableOrSavedStateDep:
8111 {
8112 if ( mData->mRegistered
8113 && ( !i_isSessionMachine()
8114 || ( mData->mMachineState != MachineState_Aborted
8115 && mData->mMachineState != MachineState_Teleported
8116 && mData->mMachineState != MachineState_Saved
8117 && mData->mMachineState != MachineState_AbortedSaved
8118 && mData->mMachineState != MachineState_PoweredOff
8119 )
8120 )
8121 )
8122 return setError(VBOX_E_INVALID_VM_STATE,
8123 tr("The machine is not mutable or saved (state is %s)"),
8124 Global::stringifyMachineState(mData->mMachineState));
8125 break;
8126 }
8127 case MutableOrRunningStateDep:
8128 {
8129 if ( mData->mRegistered
8130 && ( !i_isSessionMachine()
8131 || ( mData->mMachineState != MachineState_Aborted
8132 && mData->mMachineState != MachineState_Teleported
8133 && mData->mMachineState != MachineState_PoweredOff
8134 && !Global::IsOnline(mData->mMachineState)
8135 )
8136 )
8137 )
8138 return setError(VBOX_E_INVALID_VM_STATE,
8139 tr("The machine is not mutable or running (state is %s)"),
8140 Global::stringifyMachineState(mData->mMachineState));
8141 break;
8142 }
8143 case MutableOrSavedOrRunningStateDep:
8144 {
8145 if ( mData->mRegistered
8146 && ( !i_isSessionMachine()
8147 || ( mData->mMachineState != MachineState_Aborted
8148 && mData->mMachineState != MachineState_Teleported
8149 && mData->mMachineState != MachineState_Saved
8150 && mData->mMachineState != MachineState_AbortedSaved
8151 && mData->mMachineState != MachineState_PoweredOff
8152 && !Global::IsOnline(mData->mMachineState)
8153 )
8154 )
8155 )
8156 return setError(VBOX_E_INVALID_VM_STATE,
8157 tr("The machine is not mutable, saved or running (state is %s)"),
8158 Global::stringifyMachineState(mData->mMachineState));
8159 break;
8160 }
8161 }
8162
8163 return S_OK;
8164}
8165
8166/**
8167 * Helper to initialize all associated child objects and allocate data
8168 * structures.
8169 *
8170 * This method must be called as a part of the object's initialization procedure
8171 * (usually done in the #init() method).
8172 *
8173 * @note Must be called only from #init() or from #i_registeredInit().
8174 */
8175HRESULT Machine::initDataAndChildObjects()
8176{
8177 AutoCaller autoCaller(this);
8178 AssertComRCReturnRC(autoCaller.rc());
8179 AssertReturn( getObjectState().getState() == ObjectState::InInit
8180 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8181
8182 AssertReturn(!mData->mAccessible, E_FAIL);
8183
8184 /* allocate data structures */
8185 mSSData.allocate();
8186 mUserData.allocate();
8187 mHWData.allocate();
8188 mMediumAttachments.allocate();
8189 mStorageControllers.allocate();
8190 mUSBControllers.allocate();
8191
8192 /* initialize mOSTypeId */
8193 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8194
8195/** @todo r=bird: init() methods never fails, right? Why don't we make them
8196 * return void then! */
8197
8198 /* create associated BIOS settings object */
8199 unconst(mBIOSSettings).createObject();
8200 mBIOSSettings->init(this);
8201
8202 /* create associated trusted platform module object */
8203 unconst(mTrustedPlatformModule).createObject();
8204 mTrustedPlatformModule->init(this);
8205
8206 /* create associated NVRAM store object */
8207 unconst(mNvramStore).createObject();
8208 mNvramStore->init(this);
8209
8210 /* create associated record settings object */
8211 unconst(mRecordingSettings).createObject();
8212 mRecordingSettings->init(this);
8213
8214 /* create the graphics adapter object (always present) */
8215 unconst(mGraphicsAdapter).createObject();
8216 mGraphicsAdapter->init(this);
8217
8218 /* create an associated VRDE object (default is disabled) */
8219 unconst(mVRDEServer).createObject();
8220 mVRDEServer->init(this);
8221
8222 /* create associated serial port objects */
8223 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8224 {
8225 unconst(mSerialPorts[slot]).createObject();
8226 mSerialPorts[slot]->init(this, slot);
8227 }
8228
8229 /* create associated parallel port objects */
8230 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8231 {
8232 unconst(mParallelPorts[slot]).createObject();
8233 mParallelPorts[slot]->init(this, slot);
8234 }
8235
8236 /* create the audio adapter object (always present, default is disabled) */
8237 unconst(mAudioAdapter).createObject();
8238 mAudioAdapter->init(this);
8239
8240 /* create the USB device filters object (always present) */
8241 unconst(mUSBDeviceFilters).createObject();
8242 mUSBDeviceFilters->init(this);
8243
8244 /* create associated network adapter objects */
8245 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8246 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8247 {
8248 unconst(mNetworkAdapters[slot]).createObject();
8249 mNetworkAdapters[slot]->init(this, slot);
8250 }
8251
8252 /* create the bandwidth control */
8253 unconst(mBandwidthControl).createObject();
8254 mBandwidthControl->init(this);
8255
8256 return S_OK;
8257}
8258
8259/**
8260 * Helper to uninitialize all associated child objects and to free all data
8261 * structures.
8262 *
8263 * This method must be called as a part of the object's uninitialization
8264 * procedure (usually done in the #uninit() method).
8265 *
8266 * @note Must be called only from #uninit() or from #i_registeredInit().
8267 */
8268void Machine::uninitDataAndChildObjects()
8269{
8270 AutoCaller autoCaller(this);
8271 AssertComRCReturnVoid(autoCaller.rc());
8272 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8273 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8274 || getObjectState().getState() == ObjectState::InUninit
8275 || getObjectState().getState() == ObjectState::Limited);
8276
8277 /* tell all our other child objects we've been uninitialized */
8278 if (mBandwidthControl)
8279 {
8280 mBandwidthControl->uninit();
8281 unconst(mBandwidthControl).setNull();
8282 }
8283
8284 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8285 {
8286 if (mNetworkAdapters[slot])
8287 {
8288 mNetworkAdapters[slot]->uninit();
8289 unconst(mNetworkAdapters[slot]).setNull();
8290 }
8291 }
8292
8293 if (mUSBDeviceFilters)
8294 {
8295 mUSBDeviceFilters->uninit();
8296 unconst(mUSBDeviceFilters).setNull();
8297 }
8298
8299 if (mAudioAdapter)
8300 {
8301 mAudioAdapter->uninit();
8302 unconst(mAudioAdapter).setNull();
8303 }
8304
8305 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8306 {
8307 if (mParallelPorts[slot])
8308 {
8309 mParallelPorts[slot]->uninit();
8310 unconst(mParallelPorts[slot]).setNull();
8311 }
8312 }
8313
8314 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8315 {
8316 if (mSerialPorts[slot])
8317 {
8318 mSerialPorts[slot]->uninit();
8319 unconst(mSerialPorts[slot]).setNull();
8320 }
8321 }
8322
8323 if (mVRDEServer)
8324 {
8325 mVRDEServer->uninit();
8326 unconst(mVRDEServer).setNull();
8327 }
8328
8329 if (mGraphicsAdapter)
8330 {
8331 mGraphicsAdapter->uninit();
8332 unconst(mGraphicsAdapter).setNull();
8333 }
8334
8335 if (mBIOSSettings)
8336 {
8337 mBIOSSettings->uninit();
8338 unconst(mBIOSSettings).setNull();
8339 }
8340
8341 if (mTrustedPlatformModule)
8342 {
8343 mTrustedPlatformModule->uninit();
8344 unconst(mTrustedPlatformModule).setNull();
8345 }
8346
8347 if (mNvramStore)
8348 {
8349 mNvramStore->uninit();
8350 unconst(mNvramStore).setNull();
8351 }
8352
8353 if (mRecordingSettings)
8354 {
8355 mRecordingSettings->uninit();
8356 unconst(mRecordingSettings).setNull();
8357 }
8358
8359 /* Deassociate media (only when a real Machine or a SnapshotMachine
8360 * instance is uninitialized; SessionMachine instances refer to real
8361 * Machine media). This is necessary for a clean re-initialization of
8362 * the VM after successfully re-checking the accessibility state. Note
8363 * that in case of normal Machine or SnapshotMachine uninitialization (as
8364 * a result of unregistering or deleting the snapshot), outdated media
8365 * attachments will already be uninitialized and deleted, so this
8366 * code will not affect them. */
8367 if ( !mMediumAttachments.isNull()
8368 && !i_isSessionMachine()
8369 )
8370 {
8371 for (MediumAttachmentList::const_iterator
8372 it = mMediumAttachments->begin();
8373 it != mMediumAttachments->end();
8374 ++it)
8375 {
8376 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8377 if (pMedium.isNull())
8378 continue;
8379 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8380 AssertComRC(rc);
8381 }
8382 }
8383
8384 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8385 {
8386 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8387 if (mData->mFirstSnapshot)
8388 {
8389 // snapshots tree is protected by machine write lock; strictly
8390 // this isn't necessary here since we're deleting the entire
8391 // machine, but otherwise we assert in Snapshot::uninit()
8392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8393 mData->mFirstSnapshot->uninit();
8394 mData->mFirstSnapshot.setNull();
8395 }
8396
8397 mData->mCurrentSnapshot.setNull();
8398 }
8399
8400 /* free data structures (the essential mData structure is not freed here
8401 * since it may be still in use) */
8402 mMediumAttachments.free();
8403 mStorageControllers.free();
8404 mUSBControllers.free();
8405 mHWData.free();
8406 mUserData.free();
8407 mSSData.free();
8408}
8409
8410/**
8411 * Returns a pointer to the Machine object for this machine that acts like a
8412 * parent for complex machine data objects such as shared folders, etc.
8413 *
8414 * For primary Machine objects and for SnapshotMachine objects, returns this
8415 * object's pointer itself. For SessionMachine objects, returns the peer
8416 * (primary) machine pointer.
8417 */
8418Machine *Machine::i_getMachine()
8419{
8420 if (i_isSessionMachine())
8421 return (Machine*)mPeer;
8422 return this;
8423}
8424
8425/**
8426 * Makes sure that there are no machine state dependents. If necessary, waits
8427 * for the number of dependents to drop to zero.
8428 *
8429 * Make sure this method is called from under this object's write lock to
8430 * guarantee that no new dependents may be added when this method returns
8431 * control to the caller.
8432 *
8433 * @note Receives a lock to this object for writing. The lock will be released
8434 * while waiting (if necessary).
8435 *
8436 * @warning To be used only in methods that change the machine state!
8437 */
8438void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8439{
8440 AssertReturnVoid(isWriteLockOnCurrentThread());
8441
8442 /* Wait for all state dependents if necessary */
8443 if (mData->mMachineStateDeps != 0)
8444 {
8445 /* lazy semaphore creation */
8446 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8447 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8448
8449 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8450 mData->mMachineStateDeps));
8451
8452 ++mData->mMachineStateChangePending;
8453
8454 /* reset the semaphore before waiting, the last dependent will signal
8455 * it */
8456 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8457
8458 alock.release();
8459
8460 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8461
8462 alock.acquire();
8463
8464 -- mData->mMachineStateChangePending;
8465 }
8466}
8467
8468/**
8469 * Changes the machine state and informs callbacks.
8470 *
8471 * This method is not intended to fail so it either returns S_OK or asserts (and
8472 * returns a failure).
8473 *
8474 * @note Locks this object for writing.
8475 */
8476HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8477{
8478 LogFlowThisFuncEnter();
8479 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8480 Assert(aMachineState != MachineState_Null);
8481
8482 AutoCaller autoCaller(this);
8483 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8484
8485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8486
8487 /* wait for state dependents to drop to zero */
8488 i_ensureNoStateDependencies(alock);
8489
8490 MachineState_T const enmOldState = mData->mMachineState;
8491 if (enmOldState != aMachineState)
8492 {
8493 mData->mMachineState = aMachineState;
8494 RTTimeNow(&mData->mLastStateChange);
8495
8496#ifdef VBOX_WITH_DTRACE_R3_MAIN
8497 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8498#endif
8499 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8500 }
8501
8502 LogFlowThisFuncLeave();
8503 return S_OK;
8504}
8505
8506/**
8507 * Searches for a shared folder with the given logical name
8508 * in the collection of shared folders.
8509 *
8510 * @param aName logical name of the shared folder
8511 * @param aSharedFolder where to return the found object
8512 * @param aSetError whether to set the error info if the folder is
8513 * not found
8514 * @return
8515 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8516 *
8517 * @note
8518 * must be called from under the object's lock!
8519 */
8520HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8521 ComObjPtr<SharedFolder> &aSharedFolder,
8522 bool aSetError /* = false */)
8523{
8524 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8525 for (HWData::SharedFolderList::const_iterator
8526 it = mHWData->mSharedFolders.begin();
8527 it != mHWData->mSharedFolders.end();
8528 ++it)
8529 {
8530 SharedFolder *pSF = *it;
8531 AutoCaller autoCaller(pSF);
8532 if (pSF->i_getName() == aName)
8533 {
8534 aSharedFolder = pSF;
8535 rc = S_OK;
8536 break;
8537 }
8538 }
8539
8540 if (aSetError && FAILED(rc))
8541 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8542
8543 return rc;
8544}
8545
8546/**
8547 * Initializes all machine instance data from the given settings structures
8548 * from XML. The exception is the machine UUID which needs special handling
8549 * depending on the caller's use case, so the caller needs to set that herself.
8550 *
8551 * This gets called in several contexts during machine initialization:
8552 *
8553 * -- When machine XML exists on disk already and needs to be loaded into memory,
8554 * for example, from #i_registeredInit() to load all registered machines on
8555 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8556 * attached to the machine should be part of some media registry already.
8557 *
8558 * -- During OVF import, when a machine config has been constructed from an
8559 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8560 * ensure that the media listed as attachments in the config (which have
8561 * been imported from the OVF) receive the correct registry ID.
8562 *
8563 * -- During VM cloning.
8564 *
8565 * @param config Machine settings from XML.
8566 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8567 * for each attached medium in the config.
8568 * @return
8569 */
8570HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8571 const Guid *puuidRegistry)
8572{
8573 // copy name, description, OS type, teleporter, UTC etc.
8574 mUserData->s = config.machineUserData;
8575
8576 // look up the object by Id to check it is valid
8577 ComObjPtr<GuestOSType> pGuestOSType;
8578 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8579 if (!pGuestOSType.isNull())
8580 mUserData->s.strOsType = pGuestOSType->i_id();
8581
8582 // stateFile (optional)
8583 if (config.strStateFile.isEmpty())
8584 mSSData->strStateFilePath.setNull();
8585 else
8586 {
8587 Utf8Str stateFilePathFull(config.strStateFile);
8588 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8589 if (RT_FAILURE(vrc))
8590 return setErrorBoth(E_FAIL, vrc,
8591 tr("Invalid saved state file path '%s' (%Rrc)"),
8592 config.strStateFile.c_str(),
8593 vrc);
8594 mSSData->strStateFilePath = stateFilePathFull;
8595 }
8596
8597 // snapshot folder needs special processing so set it again
8598 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8599 if (FAILED(rc)) return rc;
8600
8601 /* Copy the extra data items (config may or may not be the same as
8602 * mData->pMachineConfigFile) if necessary. When loading the XML files
8603 * from disk they are the same, but not for OVF import. */
8604 if (mData->pMachineConfigFile != &config)
8605 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8606
8607 /* currentStateModified (optional, default is true) */
8608 mData->mCurrentStateModified = config.fCurrentStateModified;
8609
8610 mData->mLastStateChange = config.timeLastStateChange;
8611
8612 /*
8613 * note: all mUserData members must be assigned prior this point because
8614 * we need to commit changes in order to let mUserData be shared by all
8615 * snapshot machine instances.
8616 */
8617 mUserData.commitCopy();
8618
8619 // machine registry, if present (must be loaded before snapshots)
8620 if (config.canHaveOwnMediaRegistry())
8621 {
8622 // determine machine folder
8623 Utf8Str strMachineFolder = i_getSettingsFileFull();
8624 strMachineFolder.stripFilename();
8625 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8626 config.mediaRegistry,
8627 strMachineFolder);
8628 if (FAILED(rc)) return rc;
8629 }
8630
8631 /* Snapshot node (optional) */
8632 size_t cRootSnapshots;
8633 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8634 {
8635 // there must be only one root snapshot
8636 Assert(cRootSnapshots == 1);
8637
8638 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8639
8640 rc = i_loadSnapshot(snap,
8641 config.uuidCurrentSnapshot,
8642 NULL); // no parent == first snapshot
8643 if (FAILED(rc)) return rc;
8644 }
8645
8646 // hardware data
8647 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8648 if (FAILED(rc)) return rc;
8649
8650 /*
8651 * NOTE: the assignment below must be the last thing to do,
8652 * otherwise it will be not possible to change the settings
8653 * somewhere in the code above because all setters will be
8654 * blocked by i_checkStateDependency(MutableStateDep).
8655 */
8656
8657 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8658 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8659 {
8660 /* no need to use i_setMachineState() during init() */
8661 mData->mMachineState = MachineState_AbortedSaved;
8662 }
8663 else if (config.fAborted)
8664 {
8665 mSSData->strStateFilePath.setNull();
8666
8667 /* no need to use i_setMachineState() during init() */
8668 mData->mMachineState = MachineState_Aborted;
8669 }
8670 else if (!mSSData->strStateFilePath.isEmpty())
8671 {
8672 /* no need to use i_setMachineState() during init() */
8673 mData->mMachineState = MachineState_Saved;
8674 }
8675
8676 // after loading settings, we are no longer different from the XML on disk
8677 mData->flModifications = 0;
8678
8679 return S_OK;
8680}
8681
8682/**
8683 * Recursively loads all snapshots starting from the given.
8684 *
8685 * @param data snapshot settings.
8686 * @param aCurSnapshotId Current snapshot ID from the settings file.
8687 * @param aParentSnapshot Parent snapshot.
8688 */
8689HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8690 const Guid &aCurSnapshotId,
8691 Snapshot *aParentSnapshot)
8692{
8693 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8694 AssertReturn(!i_isSessionMachine(), E_FAIL);
8695
8696 HRESULT rc = S_OK;
8697
8698 Utf8Str strStateFile;
8699 if (!data.strStateFile.isEmpty())
8700 {
8701 /* optional */
8702 strStateFile = data.strStateFile;
8703 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8704 if (RT_FAILURE(vrc))
8705 return setErrorBoth(E_FAIL, vrc,
8706 tr("Invalid saved state file path '%s' (%Rrc)"),
8707 strStateFile.c_str(),
8708 vrc);
8709 }
8710
8711 /* create a snapshot machine object */
8712 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8713 pSnapshotMachine.createObject();
8714 rc = pSnapshotMachine->initFromSettings(this,
8715 data.hardware,
8716 &data.debugging,
8717 &data.autostart,
8718 data.uuid.ref(),
8719 strStateFile);
8720 if (FAILED(rc)) return rc;
8721
8722 /* create a snapshot object */
8723 ComObjPtr<Snapshot> pSnapshot;
8724 pSnapshot.createObject();
8725 /* initialize the snapshot */
8726 rc = pSnapshot->init(mParent, // VirtualBox object
8727 data.uuid,
8728 data.strName,
8729 data.strDescription,
8730 data.timestamp,
8731 pSnapshotMachine,
8732 aParentSnapshot);
8733 if (FAILED(rc)) return rc;
8734
8735 /* memorize the first snapshot if necessary */
8736 if (!mData->mFirstSnapshot)
8737 mData->mFirstSnapshot = pSnapshot;
8738
8739 /* memorize the current snapshot when appropriate */
8740 if ( !mData->mCurrentSnapshot
8741 && pSnapshot->i_getId() == aCurSnapshotId
8742 )
8743 mData->mCurrentSnapshot = pSnapshot;
8744
8745 // now create the children
8746 for (settings::SnapshotsList::const_iterator
8747 it = data.llChildSnapshots.begin();
8748 it != data.llChildSnapshots.end();
8749 ++it)
8750 {
8751 const settings::Snapshot &childData = *it;
8752 // recurse
8753 rc = i_loadSnapshot(childData,
8754 aCurSnapshotId,
8755 pSnapshot); // parent = the one we created above
8756 if (FAILED(rc)) return rc;
8757 }
8758
8759 return rc;
8760}
8761
8762/**
8763 * Loads settings into mHWData.
8764 *
8765 * @param puuidRegistry Registry ID.
8766 * @param puuidSnapshot Snapshot ID
8767 * @param data Reference to the hardware settings.
8768 * @param pDbg Pointer to the debugging settings.
8769 * @param pAutostart Pointer to the autostart settings.
8770 */
8771HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8772 const Guid *puuidSnapshot,
8773 const settings::Hardware &data,
8774 const settings::Debugging *pDbg,
8775 const settings::Autostart *pAutostart)
8776{
8777 AssertReturn(!i_isSessionMachine(), E_FAIL);
8778
8779 HRESULT rc = S_OK;
8780
8781 try
8782 {
8783 ComObjPtr<GuestOSType> pGuestOSType;
8784 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8785
8786 /* The hardware version attribute (optional). */
8787 mHWData->mHWVersion = data.strVersion;
8788 mHWData->mHardwareUUID = data.uuid;
8789
8790 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8791 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8792 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8793 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8794 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8795 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8796 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8797 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8798 mHWData->mPAEEnabled = data.fPAE;
8799 mHWData->mLongMode = data.enmLongMode;
8800 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8801 mHWData->mAPIC = data.fAPIC;
8802 mHWData->mX2APIC = data.fX2APIC;
8803 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8804 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8805 mHWData->mSpecCtrl = data.fSpecCtrl;
8806 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8807 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8808 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8809 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8810 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8811 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8812 mHWData->mCPUCount = data.cCPUs;
8813 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8814 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8815 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8816 mHWData->mCpuProfile = data.strCpuProfile;
8817
8818 // cpu
8819 if (mHWData->mCPUHotPlugEnabled)
8820 {
8821 for (settings::CpuList::const_iterator
8822 it = data.llCpus.begin();
8823 it != data.llCpus.end();
8824 ++it)
8825 {
8826 const settings::Cpu &cpu = *it;
8827
8828 mHWData->mCPUAttached[cpu.ulId] = true;
8829 }
8830 }
8831
8832 // cpuid leafs
8833 for (settings::CpuIdLeafsList::const_iterator
8834 it = data.llCpuIdLeafs.begin();
8835 it != data.llCpuIdLeafs.end();
8836 ++it)
8837 {
8838 const settings::CpuIdLeaf &rLeaf= *it;
8839 if ( rLeaf.idx < UINT32_C(0x20)
8840 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8841 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8842 mHWData->mCpuIdLeafList.push_back(rLeaf);
8843 /* else: just ignore */
8844 }
8845
8846 mHWData->mMemorySize = data.ulMemorySizeMB;
8847 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8848
8849 // boot order
8850 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8851 {
8852 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8853 if (it == data.mapBootOrder.end())
8854 mHWData->mBootOrder[i] = DeviceType_Null;
8855 else
8856 mHWData->mBootOrder[i] = it->second;
8857 }
8858
8859 mHWData->mFirmwareType = data.firmwareType;
8860 mHWData->mPointingHIDType = data.pointingHIDType;
8861 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8862 mHWData->mChipsetType = data.chipsetType;
8863 mHWData->mIommuType = data.iommuType;
8864 mHWData->mParavirtProvider = data.paravirtProvider;
8865 mHWData->mParavirtDebug = data.strParavirtDebug;
8866 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8867 mHWData->mHPETEnabled = data.fHPETEnabled;
8868
8869 /* GraphicsAdapter */
8870 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8871 if (FAILED(rc)) return rc;
8872
8873 /* VRDEServer */
8874 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8875 if (FAILED(rc)) return rc;
8876
8877 /* BIOS */
8878 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8879 if (FAILED(rc)) return rc;
8880
8881 /* Trusted Platform Module */
8882 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8883 if (FAILED(rc)) return rc;
8884
8885 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8886 if (FAILED(rc)) return rc;
8887
8888 /* Recording settings */
8889 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8890 if (FAILED(rc)) return rc;
8891
8892 // Bandwidth control (must come before network adapters)
8893 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8894 if (FAILED(rc)) return rc;
8895
8896 /* USB controllers */
8897 for (settings::USBControllerList::const_iterator
8898 it = data.usbSettings.llUSBControllers.begin();
8899 it != data.usbSettings.llUSBControllers.end();
8900 ++it)
8901 {
8902 const settings::USBController &settingsCtrl = *it;
8903 ComObjPtr<USBController> newCtrl;
8904
8905 newCtrl.createObject();
8906 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8907 mUSBControllers->push_back(newCtrl);
8908 }
8909
8910 /* USB device filters */
8911 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8912 if (FAILED(rc)) return rc;
8913
8914 // network adapters (establish array size first and apply defaults, to
8915 // ensure reading the same settings as we saved, since the list skips
8916 // adapters having defaults)
8917 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8918 size_t oldCount = mNetworkAdapters.size();
8919 if (newCount > oldCount)
8920 {
8921 mNetworkAdapters.resize(newCount);
8922 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8923 {
8924 unconst(mNetworkAdapters[slot]).createObject();
8925 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8926 }
8927 }
8928 else if (newCount < oldCount)
8929 mNetworkAdapters.resize(newCount);
8930 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8931 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8932 for (settings::NetworkAdaptersList::const_iterator
8933 it = data.llNetworkAdapters.begin();
8934 it != data.llNetworkAdapters.end();
8935 ++it)
8936 {
8937 const settings::NetworkAdapter &nic = *it;
8938
8939 /* slot uniqueness is guaranteed by XML Schema */
8940 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8941 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8942 if (FAILED(rc)) return rc;
8943 }
8944
8945 // serial ports (establish defaults first, to ensure reading the same
8946 // settings as we saved, since the list skips ports having defaults)
8947 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8948 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8949 for (settings::SerialPortsList::const_iterator
8950 it = data.llSerialPorts.begin();
8951 it != data.llSerialPorts.end();
8952 ++it)
8953 {
8954 const settings::SerialPort &s = *it;
8955
8956 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8957 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8958 if (FAILED(rc)) return rc;
8959 }
8960
8961 // parallel ports (establish defaults first, to ensure reading the same
8962 // settings as we saved, since the list skips ports having defaults)
8963 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8964 mParallelPorts[i]->i_applyDefaults();
8965 for (settings::ParallelPortsList::const_iterator
8966 it = data.llParallelPorts.begin();
8967 it != data.llParallelPorts.end();
8968 ++it)
8969 {
8970 const settings::ParallelPort &p = *it;
8971
8972 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8973 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8974 if (FAILED(rc)) return rc;
8975 }
8976
8977 /* AudioAdapter */
8978 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8979 if (FAILED(rc)) return rc;
8980
8981 /* storage controllers */
8982 rc = i_loadStorageControllers(data.storage,
8983 puuidRegistry,
8984 puuidSnapshot);
8985 if (FAILED(rc)) return rc;
8986
8987 /* Shared folders */
8988 for (settings::SharedFoldersList::const_iterator
8989 it = data.llSharedFolders.begin();
8990 it != data.llSharedFolders.end();
8991 ++it)
8992 {
8993 const settings::SharedFolder &sf = *it;
8994
8995 ComObjPtr<SharedFolder> sharedFolder;
8996 /* Check for double entries. Not allowed! */
8997 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8998 if (SUCCEEDED(rc))
8999 return setError(VBOX_E_OBJECT_IN_USE,
9000 tr("Shared folder named '%s' already exists"),
9001 sf.strName.c_str());
9002
9003 /* Create the new shared folder. Don't break on error. This will be
9004 * reported when the machine starts. */
9005 sharedFolder.createObject();
9006 rc = sharedFolder->init(i_getMachine(),
9007 sf.strName,
9008 sf.strHostPath,
9009 RT_BOOL(sf.fWritable),
9010 RT_BOOL(sf.fAutoMount),
9011 sf.strAutoMountPoint,
9012 false /* fFailOnError */);
9013 if (FAILED(rc)) return rc;
9014 mHWData->mSharedFolders.push_back(sharedFolder);
9015 }
9016
9017 // Clipboard
9018 mHWData->mClipboardMode = data.clipboardMode;
9019 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9020
9021 // drag'n'drop
9022 mHWData->mDnDMode = data.dndMode;
9023
9024 // guest settings
9025 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9026
9027 // IO settings
9028 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9029 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9030
9031 // Host PCI devices
9032 for (settings::HostPCIDeviceAttachmentList::const_iterator
9033 it = data.pciAttachments.begin();
9034 it != data.pciAttachments.end();
9035 ++it)
9036 {
9037 const settings::HostPCIDeviceAttachment &hpda = *it;
9038 ComObjPtr<PCIDeviceAttachment> pda;
9039
9040 pda.createObject();
9041 pda->i_loadSettings(this, hpda);
9042 mHWData->mPCIDeviceAssignments.push_back(pda);
9043 }
9044
9045 /*
9046 * (The following isn't really real hardware, but it lives in HWData
9047 * for reasons of convenience.)
9048 */
9049
9050#ifdef VBOX_WITH_GUEST_PROPS
9051 /* Guest properties (optional) */
9052
9053 /* Only load transient guest properties for configs which have saved
9054 * state, because there shouldn't be any for powered off VMs. The same
9055 * logic applies for snapshots, as offline snapshots shouldn't have
9056 * any such properties. They confuse the code in various places.
9057 * Note: can't rely on the machine state, as it isn't set yet. */
9058 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9059 /* apologies for the hacky unconst() usage, but this needs hacking
9060 * actually inconsistent settings into consistency, otherwise there
9061 * will be some corner cases where the inconsistency survives
9062 * surprisingly long without getting fixed, especially for snapshots
9063 * as there are no config changes. */
9064 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9065 for (settings::GuestPropertiesList::iterator
9066 it = llGuestProperties.begin();
9067 it != llGuestProperties.end();
9068 /*nothing*/)
9069 {
9070 const settings::GuestProperty &prop = *it;
9071 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9072 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9073 if ( fSkipTransientGuestProperties
9074 && ( fFlags & GUEST_PROP_F_TRANSIENT
9075 || fFlags & GUEST_PROP_F_TRANSRESET))
9076 {
9077 it = llGuestProperties.erase(it);
9078 continue;
9079 }
9080 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9081 mHWData->mGuestProperties[prop.strName] = property;
9082 ++it;
9083 }
9084#endif /* VBOX_WITH_GUEST_PROPS defined */
9085
9086 rc = i_loadDebugging(pDbg);
9087 if (FAILED(rc))
9088 return rc;
9089
9090 mHWData->mAutostart = *pAutostart;
9091
9092 /* default frontend */
9093 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9094 }
9095 catch (std::bad_alloc &)
9096 {
9097 return E_OUTOFMEMORY;
9098 }
9099
9100 AssertComRC(rc);
9101 return rc;
9102}
9103
9104/**
9105 * Called from i_loadHardware() to load the debugging settings of the
9106 * machine.
9107 *
9108 * @param pDbg Pointer to the settings.
9109 */
9110HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9111{
9112 mHWData->mDebugging = *pDbg;
9113 /* no more processing currently required, this will probably change. */
9114 return S_OK;
9115}
9116
9117/**
9118 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9119 *
9120 * @param data storage settings.
9121 * @param puuidRegistry media registry ID to set media to or NULL;
9122 * see Machine::i_loadMachineDataFromSettings()
9123 * @param puuidSnapshot snapshot ID
9124 * @return
9125 */
9126HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9127 const Guid *puuidRegistry,
9128 const Guid *puuidSnapshot)
9129{
9130 AssertReturn(!i_isSessionMachine(), E_FAIL);
9131
9132 HRESULT rc = S_OK;
9133
9134 for (settings::StorageControllersList::const_iterator
9135 it = data.llStorageControllers.begin();
9136 it != data.llStorageControllers.end();
9137 ++it)
9138 {
9139 const settings::StorageController &ctlData = *it;
9140
9141 ComObjPtr<StorageController> pCtl;
9142 /* Try to find one with the name first. */
9143 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9144 if (SUCCEEDED(rc))
9145 return setError(VBOX_E_OBJECT_IN_USE,
9146 tr("Storage controller named '%s' already exists"),
9147 ctlData.strName.c_str());
9148
9149 pCtl.createObject();
9150 rc = pCtl->init(this,
9151 ctlData.strName,
9152 ctlData.storageBus,
9153 ctlData.ulInstance,
9154 ctlData.fBootable);
9155 if (FAILED(rc)) return rc;
9156
9157 mStorageControllers->push_back(pCtl);
9158
9159 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9160 if (FAILED(rc)) return rc;
9161
9162 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9163 if (FAILED(rc)) return rc;
9164
9165 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9166 if (FAILED(rc)) return rc;
9167
9168 /* Load the attached devices now. */
9169 rc = i_loadStorageDevices(pCtl,
9170 ctlData,
9171 puuidRegistry,
9172 puuidSnapshot);
9173 if (FAILED(rc)) return rc;
9174 }
9175
9176 return S_OK;
9177}
9178
9179/**
9180 * Called from i_loadStorageControllers for a controller's devices.
9181 *
9182 * @param aStorageController
9183 * @param data
9184 * @param puuidRegistry media registry ID to set media to or NULL; see
9185 * Machine::i_loadMachineDataFromSettings()
9186 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9187 * @return
9188 */
9189HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9190 const settings::StorageController &data,
9191 const Guid *puuidRegistry,
9192 const Guid *puuidSnapshot)
9193{
9194 HRESULT rc = S_OK;
9195
9196 /* paranoia: detect duplicate attachments */
9197 for (settings::AttachedDevicesList::const_iterator
9198 it = data.llAttachedDevices.begin();
9199 it != data.llAttachedDevices.end();
9200 ++it)
9201 {
9202 const settings::AttachedDevice &ad = *it;
9203
9204 for (settings::AttachedDevicesList::const_iterator it2 = it;
9205 it2 != data.llAttachedDevices.end();
9206 ++it2)
9207 {
9208 if (it == it2)
9209 continue;
9210
9211 const settings::AttachedDevice &ad2 = *it2;
9212
9213 if ( ad.lPort == ad2.lPort
9214 && ad.lDevice == ad2.lDevice)
9215 {
9216 return setError(E_FAIL,
9217 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9218 aStorageController->i_getName().c_str(),
9219 ad.lPort,
9220 ad.lDevice,
9221 mUserData->s.strName.c_str());
9222 }
9223 }
9224 }
9225
9226 for (settings::AttachedDevicesList::const_iterator
9227 it = data.llAttachedDevices.begin();
9228 it != data.llAttachedDevices.end();
9229 ++it)
9230 {
9231 const settings::AttachedDevice &dev = *it;
9232 ComObjPtr<Medium> medium;
9233
9234 switch (dev.deviceType)
9235 {
9236 case DeviceType_Floppy:
9237 case DeviceType_DVD:
9238 if (dev.strHostDriveSrc.isNotEmpty())
9239 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9240 false /* fRefresh */, medium);
9241 else
9242 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9243 dev.uuid,
9244 false /* fRefresh */,
9245 false /* aSetError */,
9246 medium);
9247 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9248 // This is not an error. The host drive or UUID might have vanished, so just go
9249 // ahead without this removeable medium attachment
9250 rc = S_OK;
9251 break;
9252
9253 case DeviceType_HardDisk:
9254 {
9255 /* find a hard disk by UUID */
9256 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9257 if (FAILED(rc))
9258 {
9259 if (i_isSnapshotMachine())
9260 {
9261 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9262 // so the user knows that the bad disk is in a snapshot somewhere
9263 com::ErrorInfo info;
9264 return setError(E_FAIL,
9265 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9266 puuidSnapshot->raw(),
9267 info.getText().raw());
9268 }
9269 else
9270 return rc;
9271 }
9272
9273 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9274
9275 if (medium->i_getType() == MediumType_Immutable)
9276 {
9277 if (i_isSnapshotMachine())
9278 return setError(E_FAIL,
9279 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9280 "of the virtual machine '%s' ('%s')"),
9281 medium->i_getLocationFull().c_str(),
9282 dev.uuid.raw(),
9283 puuidSnapshot->raw(),
9284 mUserData->s.strName.c_str(),
9285 mData->m_strConfigFileFull.c_str());
9286
9287 return setError(E_FAIL,
9288 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9289 medium->i_getLocationFull().c_str(),
9290 dev.uuid.raw(),
9291 mUserData->s.strName.c_str(),
9292 mData->m_strConfigFileFull.c_str());
9293 }
9294
9295 if (medium->i_getType() == MediumType_MultiAttach)
9296 {
9297 if (i_isSnapshotMachine())
9298 return setError(E_FAIL,
9299 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9300 "of the virtual machine '%s' ('%s')"),
9301 medium->i_getLocationFull().c_str(),
9302 dev.uuid.raw(),
9303 puuidSnapshot->raw(),
9304 mUserData->s.strName.c_str(),
9305 mData->m_strConfigFileFull.c_str());
9306
9307 return setError(E_FAIL,
9308 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9309 medium->i_getLocationFull().c_str(),
9310 dev.uuid.raw(),
9311 mUserData->s.strName.c_str(),
9312 mData->m_strConfigFileFull.c_str());
9313 }
9314
9315 if ( !i_isSnapshotMachine()
9316 && medium->i_getChildren().size() != 0
9317 )
9318 return setError(E_FAIL,
9319 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9320 "because it has %d differencing child hard disks"),
9321 medium->i_getLocationFull().c_str(),
9322 dev.uuid.raw(),
9323 mUserData->s.strName.c_str(),
9324 mData->m_strConfigFileFull.c_str(),
9325 medium->i_getChildren().size());
9326
9327 if (i_findAttachment(*mMediumAttachments.data(),
9328 medium))
9329 return setError(E_FAIL,
9330 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9331 medium->i_getLocationFull().c_str(),
9332 dev.uuid.raw(),
9333 mUserData->s.strName.c_str(),
9334 mData->m_strConfigFileFull.c_str());
9335
9336 break;
9337 }
9338
9339 default:
9340 return setError(E_FAIL,
9341 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9342 medium->i_getLocationFull().c_str(),
9343 mUserData->s.strName.c_str(),
9344 mData->m_strConfigFileFull.c_str());
9345 }
9346
9347 if (FAILED(rc))
9348 break;
9349
9350 /* Bandwidth groups are loaded at this point. */
9351 ComObjPtr<BandwidthGroup> pBwGroup;
9352
9353 if (!dev.strBwGroup.isEmpty())
9354 {
9355 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9356 if (FAILED(rc))
9357 return setError(E_FAIL,
9358 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9359 medium->i_getLocationFull().c_str(),
9360 dev.strBwGroup.c_str(),
9361 mUserData->s.strName.c_str(),
9362 mData->m_strConfigFileFull.c_str());
9363 pBwGroup->i_reference();
9364 }
9365
9366 const Utf8Str controllerName = aStorageController->i_getName();
9367 ComObjPtr<MediumAttachment> pAttachment;
9368 pAttachment.createObject();
9369 rc = pAttachment->init(this,
9370 medium,
9371 controllerName,
9372 dev.lPort,
9373 dev.lDevice,
9374 dev.deviceType,
9375 false,
9376 dev.fPassThrough,
9377 dev.fTempEject,
9378 dev.fNonRotational,
9379 dev.fDiscard,
9380 dev.fHotPluggable,
9381 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9382 if (FAILED(rc)) break;
9383
9384 /* associate the medium with this machine and snapshot */
9385 if (!medium.isNull())
9386 {
9387 AutoCaller medCaller(medium);
9388 if (FAILED(medCaller.rc())) return medCaller.rc();
9389 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9390
9391 if (i_isSnapshotMachine())
9392 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9393 else
9394 rc = medium->i_addBackReference(mData->mUuid);
9395 /* If the medium->addBackReference fails it sets an appropriate
9396 * error message, so no need to do any guesswork here. */
9397
9398 if (puuidRegistry)
9399 // caller wants registry ID to be set on all attached media (OVF import case)
9400 medium->i_addRegistry(*puuidRegistry);
9401 }
9402
9403 if (FAILED(rc))
9404 break;
9405
9406 /* back up mMediumAttachments to let registeredInit() properly rollback
9407 * on failure (= limited accessibility) */
9408 i_setModified(IsModified_Storage);
9409 mMediumAttachments.backup();
9410 mMediumAttachments->push_back(pAttachment);
9411 }
9412
9413 return rc;
9414}
9415
9416/**
9417 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9418 *
9419 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9420 * @param aSnapshot where to return the found snapshot
9421 * @param aSetError true to set extended error info on failure
9422 */
9423HRESULT Machine::i_findSnapshotById(const Guid &aId,
9424 ComObjPtr<Snapshot> &aSnapshot,
9425 bool aSetError /* = false */)
9426{
9427 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9428
9429 if (!mData->mFirstSnapshot)
9430 {
9431 if (aSetError)
9432 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9433 return E_FAIL;
9434 }
9435
9436 if (aId.isZero())
9437 aSnapshot = mData->mFirstSnapshot;
9438 else
9439 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9440
9441 if (!aSnapshot)
9442 {
9443 if (aSetError)
9444 return setError(E_FAIL,
9445 tr("Could not find a snapshot with UUID {%s}"),
9446 aId.toString().c_str());
9447 return E_FAIL;
9448 }
9449
9450 return S_OK;
9451}
9452
9453/**
9454 * Returns the snapshot with the given name or fails of no such snapshot.
9455 *
9456 * @param strName snapshot name to find
9457 * @param aSnapshot where to return the found snapshot
9458 * @param aSetError true to set extended error info on failure
9459 */
9460HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9461 ComObjPtr<Snapshot> &aSnapshot,
9462 bool aSetError /* = false */)
9463{
9464 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9465
9466 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9467
9468 if (!mData->mFirstSnapshot)
9469 {
9470 if (aSetError)
9471 return setError(VBOX_E_OBJECT_NOT_FOUND,
9472 tr("This machine does not have any snapshots"));
9473 return VBOX_E_OBJECT_NOT_FOUND;
9474 }
9475
9476 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9477
9478 if (!aSnapshot)
9479 {
9480 if (aSetError)
9481 return setError(VBOX_E_OBJECT_NOT_FOUND,
9482 tr("Could not find a snapshot named '%s'"), strName.c_str());
9483 return VBOX_E_OBJECT_NOT_FOUND;
9484 }
9485
9486 return S_OK;
9487}
9488
9489/**
9490 * Returns a storage controller object with the given name.
9491 *
9492 * @param aName storage controller name to find
9493 * @param aStorageController where to return the found storage controller
9494 * @param aSetError true to set extended error info on failure
9495 */
9496HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9497 ComObjPtr<StorageController> &aStorageController,
9498 bool aSetError /* = false */)
9499{
9500 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9501
9502 for (StorageControllerList::const_iterator
9503 it = mStorageControllers->begin();
9504 it != mStorageControllers->end();
9505 ++it)
9506 {
9507 if ((*it)->i_getName() == aName)
9508 {
9509 aStorageController = (*it);
9510 return S_OK;
9511 }
9512 }
9513
9514 if (aSetError)
9515 return setError(VBOX_E_OBJECT_NOT_FOUND,
9516 tr("Could not find a storage controller named '%s'"),
9517 aName.c_str());
9518 return VBOX_E_OBJECT_NOT_FOUND;
9519}
9520
9521/**
9522 * Returns a USB controller object with the given name.
9523 *
9524 * @param aName USB controller name to find
9525 * @param aUSBController where to return the found USB controller
9526 * @param aSetError true to set extended error info on failure
9527 */
9528HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9529 ComObjPtr<USBController> &aUSBController,
9530 bool aSetError /* = false */)
9531{
9532 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9533
9534 for (USBControllerList::const_iterator
9535 it = mUSBControllers->begin();
9536 it != mUSBControllers->end();
9537 ++it)
9538 {
9539 if ((*it)->i_getName() == aName)
9540 {
9541 aUSBController = (*it);
9542 return S_OK;
9543 }
9544 }
9545
9546 if (aSetError)
9547 return setError(VBOX_E_OBJECT_NOT_FOUND,
9548 tr("Could not find a storage controller named '%s'"),
9549 aName.c_str());
9550 return VBOX_E_OBJECT_NOT_FOUND;
9551}
9552
9553/**
9554 * Returns the number of USB controller instance of the given type.
9555 *
9556 * @param enmType USB controller type.
9557 */
9558ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9559{
9560 ULONG cCtrls = 0;
9561
9562 for (USBControllerList::const_iterator
9563 it = mUSBControllers->begin();
9564 it != mUSBControllers->end();
9565 ++it)
9566 {
9567 if ((*it)->i_getControllerType() == enmType)
9568 cCtrls++;
9569 }
9570
9571 return cCtrls;
9572}
9573
9574HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9575 MediumAttachmentList &atts)
9576{
9577 AutoCaller autoCaller(this);
9578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9579
9580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9581
9582 for (MediumAttachmentList::const_iterator
9583 it = mMediumAttachments->begin();
9584 it != mMediumAttachments->end();
9585 ++it)
9586 {
9587 const ComObjPtr<MediumAttachment> &pAtt = *it;
9588 // should never happen, but deal with NULL pointers in the list.
9589 AssertContinue(!pAtt.isNull());
9590
9591 // getControllerName() needs caller+read lock
9592 AutoCaller autoAttCaller(pAtt);
9593 if (FAILED(autoAttCaller.rc()))
9594 {
9595 atts.clear();
9596 return autoAttCaller.rc();
9597 }
9598 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9599
9600 if (pAtt->i_getControllerName() == aName)
9601 atts.push_back(pAtt);
9602 }
9603
9604 return S_OK;
9605}
9606
9607
9608/**
9609 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9610 * file if the machine name was changed and about creating a new settings file
9611 * if this is a new machine.
9612 *
9613 * @note Must be never called directly but only from #saveSettings().
9614 */
9615HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9616 bool *pfSettingsFileIsNew)
9617{
9618 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9619
9620 HRESULT rc = S_OK;
9621
9622 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9623 /// @todo need to handle primary group change, too
9624
9625 /* attempt to rename the settings file if machine name is changed */
9626 if ( mUserData->s.fNameSync
9627 && mUserData.isBackedUp()
9628 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9629 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9630 )
9631 {
9632 bool dirRenamed = false;
9633 bool fileRenamed = false;
9634
9635 Utf8Str configFile, newConfigFile;
9636 Utf8Str configFilePrev, newConfigFilePrev;
9637 Utf8Str NVRAMFile, newNVRAMFile;
9638 Utf8Str configDir, newConfigDir;
9639
9640 do
9641 {
9642 int vrc = VINF_SUCCESS;
9643
9644 Utf8Str name = mUserData.backedUpData()->s.strName;
9645 Utf8Str newName = mUserData->s.strName;
9646 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9647 if (group == "/")
9648 group.setNull();
9649 Utf8Str newGroup = mUserData->s.llGroups.front();
9650 if (newGroup == "/")
9651 newGroup.setNull();
9652
9653 configFile = mData->m_strConfigFileFull;
9654
9655 /* first, rename the directory if it matches the group and machine name */
9656 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9657 /** @todo hack, make somehow use of ComposeMachineFilename */
9658 if (mUserData->s.fDirectoryIncludesUUID)
9659 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9660 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9661 /** @todo hack, make somehow use of ComposeMachineFilename */
9662 if (mUserData->s.fDirectoryIncludesUUID)
9663 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9664 configDir = configFile;
9665 configDir.stripFilename();
9666 newConfigDir = configDir;
9667 if ( configDir.length() >= groupPlusName.length()
9668 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9669 groupPlusName.c_str()))
9670 {
9671 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9672 Utf8Str newConfigBaseDir(newConfigDir);
9673 newConfigDir.append(newGroupPlusName);
9674 /* consistency: use \ if appropriate on the platform */
9675 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9676 /* new dir and old dir cannot be equal here because of 'if'
9677 * above and because name != newName */
9678 Assert(configDir != newConfigDir);
9679 if (!fSettingsFileIsNew)
9680 {
9681 /* perform real rename only if the machine is not new */
9682 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9683 if ( vrc == VERR_FILE_NOT_FOUND
9684 || vrc == VERR_PATH_NOT_FOUND)
9685 {
9686 /* create the parent directory, then retry renaming */
9687 Utf8Str parent(newConfigDir);
9688 parent.stripFilename();
9689 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9690 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9691 }
9692 if (RT_FAILURE(vrc))
9693 {
9694 rc = setErrorBoth(E_FAIL, vrc,
9695 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9696 configDir.c_str(),
9697 newConfigDir.c_str(),
9698 vrc);
9699 break;
9700 }
9701 /* delete subdirectories which are no longer needed */
9702 Utf8Str dir(configDir);
9703 dir.stripFilename();
9704 while (dir != newConfigBaseDir && dir != ".")
9705 {
9706 vrc = RTDirRemove(dir.c_str());
9707 if (RT_FAILURE(vrc))
9708 break;
9709 dir.stripFilename();
9710 }
9711 dirRenamed = true;
9712 }
9713 }
9714
9715 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9716
9717 /* then try to rename the settings file itself */
9718 if (newConfigFile != configFile)
9719 {
9720 /* get the path to old settings file in renamed directory */
9721 Assert(mData->m_strConfigFileFull == configFile);
9722 configFile.printf("%s%c%s",
9723 newConfigDir.c_str(),
9724 RTPATH_DELIMITER,
9725 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9726 if (!fSettingsFileIsNew)
9727 {
9728 /* perform real rename only if the machine is not new */
9729 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9730 if (RT_FAILURE(vrc))
9731 {
9732 rc = setErrorBoth(E_FAIL, vrc,
9733 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9734 configFile.c_str(),
9735 newConfigFile.c_str(),
9736 vrc);
9737 break;
9738 }
9739 fileRenamed = true;
9740 configFilePrev = configFile;
9741 configFilePrev += "-prev";
9742 newConfigFilePrev = newConfigFile;
9743 newConfigFilePrev += "-prev";
9744 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9745 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9746 if (NVRAMFile.isNotEmpty())
9747 {
9748 // in the NVRAM file path, replace the old directory with the new directory
9749 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9750 {
9751 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9752 NVRAMFile = newConfigDir + strNVRAMFile;
9753 }
9754 newNVRAMFile = newConfigFile;
9755 newNVRAMFile.stripSuffix();
9756 newNVRAMFile += ".nvram";
9757 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9758 }
9759 }
9760 }
9761
9762 // update m_strConfigFileFull amd mConfigFile
9763 mData->m_strConfigFileFull = newConfigFile;
9764 // compute the relative path too
9765 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9766
9767 // store the old and new so that VirtualBox::i_saveSettings() can update
9768 // the media registry
9769 if ( mData->mRegistered
9770 && (configDir != newConfigDir || configFile != newConfigFile))
9771 {
9772 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9773
9774 if (pfNeedsGlobalSaveSettings)
9775 *pfNeedsGlobalSaveSettings = true;
9776 }
9777
9778 // in the saved state file path, replace the old directory with the new directory
9779 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9780 {
9781 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9782 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9783 }
9784 if (newNVRAMFile.isNotEmpty())
9785 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9786
9787 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9788 if (mData->mFirstSnapshot)
9789 {
9790 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9791 newConfigDir.c_str());
9792 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9793 newConfigDir.c_str());
9794 }
9795 }
9796 while (0);
9797
9798 if (FAILED(rc))
9799 {
9800 /* silently try to rename everything back */
9801 if (fileRenamed)
9802 {
9803 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9804 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9805 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9806 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9807 }
9808 if (dirRenamed)
9809 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9810 }
9811
9812 if (FAILED(rc)) return rc;
9813 }
9814
9815 if (fSettingsFileIsNew)
9816 {
9817 /* create a virgin config file */
9818 int vrc = VINF_SUCCESS;
9819
9820 /* ensure the settings directory exists */
9821 Utf8Str path(mData->m_strConfigFileFull);
9822 path.stripFilename();
9823 if (!RTDirExists(path.c_str()))
9824 {
9825 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9826 if (RT_FAILURE(vrc))
9827 {
9828 return setErrorBoth(E_FAIL, vrc,
9829 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9830 path.c_str(),
9831 vrc);
9832 }
9833 }
9834
9835 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9836 path = mData->m_strConfigFileFull;
9837 RTFILE f = NIL_RTFILE;
9838 vrc = RTFileOpen(&f, path.c_str(),
9839 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9840 if (RT_FAILURE(vrc))
9841 return setErrorBoth(E_FAIL, vrc,
9842 tr("Could not create the settings file '%s' (%Rrc)"),
9843 path.c_str(),
9844 vrc);
9845 RTFileClose(f);
9846 }
9847 if (pfSettingsFileIsNew)
9848 *pfSettingsFileIsNew = fSettingsFileIsNew;
9849
9850 return rc;
9851}
9852
9853/**
9854 * Saves and commits machine data, user data and hardware data.
9855 *
9856 * Note that on failure, the data remains uncommitted.
9857 *
9858 * @a aFlags may combine the following flags:
9859 *
9860 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9861 * Used when saving settings after an operation that makes them 100%
9862 * correspond to the settings from the current snapshot.
9863 * - SaveS_Force: settings will be saved without doing a deep compare of the
9864 * settings structures. This is used when this is called because snapshots
9865 * have changed to avoid the overhead of the deep compare.
9866 *
9867 * @note Must be called from under this object's write lock. Locks children for
9868 * writing.
9869 *
9870 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9871 * initialized to false and that will be set to true by this function if
9872 * the caller must invoke VirtualBox::i_saveSettings() because the global
9873 * settings have changed. This will happen if a machine rename has been
9874 * saved and the global machine and media registries will therefore need
9875 * updating.
9876 * @param alock Reference to the lock for this machine object.
9877 * @param aFlags Flags.
9878 */
9879HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9880 AutoWriteLock &alock,
9881 int aFlags /*= 0*/)
9882{
9883 LogFlowThisFuncEnter();
9884
9885 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9886
9887 /* make sure child objects are unable to modify the settings while we are
9888 * saving them */
9889 i_ensureNoStateDependencies(alock);
9890
9891 AssertReturn(!i_isSnapshotMachine(),
9892 E_FAIL);
9893
9894 if (!mData->mAccessible)
9895 return setError(VBOX_E_INVALID_VM_STATE,
9896 tr("The machine is not accessible, so cannot save settings"));
9897
9898 HRESULT rc = S_OK;
9899 bool fNeedsWrite = false;
9900 bool fSettingsFileIsNew = false;
9901
9902 /* First, prepare to save settings. It will care about renaming the
9903 * settings directory and file if the machine name was changed and about
9904 * creating a new settings file if this is a new machine. */
9905 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
9906 &fSettingsFileIsNew);
9907 if (FAILED(rc)) return rc;
9908
9909 // keep a pointer to the current settings structures
9910 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9911 settings::MachineConfigFile *pNewConfig = NULL;
9912
9913 try
9914 {
9915 // make a fresh one to have everyone write stuff into
9916 pNewConfig = new settings::MachineConfigFile(NULL);
9917 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9918
9919 // now go and copy all the settings data from COM to the settings structures
9920 // (this calls i_saveSettings() on all the COM objects in the machine)
9921 i_copyMachineDataToSettings(*pNewConfig);
9922
9923 if (aFlags & SaveS_ResetCurStateModified)
9924 {
9925 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9926 mData->mCurrentStateModified = FALSE;
9927 fNeedsWrite = true; // always, no need to compare
9928 }
9929 else if (aFlags & SaveS_Force)
9930 {
9931 fNeedsWrite = true; // always, no need to compare
9932 }
9933 else
9934 {
9935 if (!mData->mCurrentStateModified)
9936 {
9937 // do a deep compare of the settings that we just saved with the settings
9938 // previously stored in the config file; this invokes MachineConfigFile::operator==
9939 // which does a deep compare of all the settings, which is expensive but less expensive
9940 // than writing out XML in vain
9941 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9942
9943 // could still be modified if any settings changed
9944 mData->mCurrentStateModified = fAnySettingsChanged;
9945
9946 fNeedsWrite = fAnySettingsChanged;
9947 }
9948 else
9949 fNeedsWrite = true;
9950 }
9951
9952 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9953
9954 if (fNeedsWrite)
9955 // now spit it all out!
9956 pNewConfig->write(mData->m_strConfigFileFull);
9957
9958 mData->pMachineConfigFile = pNewConfig;
9959 delete pOldConfig;
9960 i_commit();
9961
9962 // after saving settings, we are no longer different from the XML on disk
9963 mData->flModifications = 0;
9964 }
9965 catch (HRESULT err)
9966 {
9967 // we assume that error info is set by the thrower
9968 rc = err;
9969
9970 // delete any newly created settings file
9971 if (fSettingsFileIsNew)
9972 RTFileDelete(mData->m_strConfigFileFull.c_str());
9973
9974 // restore old config
9975 delete pNewConfig;
9976 mData->pMachineConfigFile = pOldConfig;
9977 }
9978 catch (...)
9979 {
9980 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9981 }
9982
9983 if (fNeedsWrite)
9984 {
9985 /* Fire the data change event, even on failure (since we've already
9986 * committed all data). This is done only for SessionMachines because
9987 * mutable Machine instances are always not registered (i.e. private
9988 * to the client process that creates them) and thus don't need to
9989 * inform callbacks. */
9990 if (i_isSessionMachine())
9991 mParent->i_onMachineDataChanged(mData->mUuid);
9992 }
9993
9994 LogFlowThisFunc(("rc=%08X\n", rc));
9995 LogFlowThisFuncLeave();
9996 return rc;
9997}
9998
9999/**
10000 * Implementation for saving the machine settings into the given
10001 * settings::MachineConfigFile instance. This copies machine extradata
10002 * from the previous machine config file in the instance data, if any.
10003 *
10004 * This gets called from two locations:
10005 *
10006 * -- Machine::i_saveSettings(), during the regular XML writing;
10007 *
10008 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10009 * exported to OVF and we write the VirtualBox proprietary XML
10010 * into a <vbox:Machine> tag.
10011 *
10012 * This routine fills all the fields in there, including snapshots, *except*
10013 * for the following:
10014 *
10015 * -- fCurrentStateModified. There is some special logic associated with that.
10016 *
10017 * The caller can then call MachineConfigFile::write() or do something else
10018 * with it.
10019 *
10020 * Caller must hold the machine lock!
10021 *
10022 * This throws XML errors and HRESULT, so the caller must have a catch block!
10023 */
10024void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10025{
10026 // deep copy extradata, being extra careful with self assignment (the STL
10027 // map assignment on Mac OS X clang based Xcode isn't checking)
10028 if (&config != mData->pMachineConfigFile)
10029 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10030
10031 config.uuid = mData->mUuid;
10032
10033 // copy name, description, OS type, teleport, UTC etc.
10034 config.machineUserData = mUserData->s;
10035
10036 if ( mData->mMachineState == MachineState_Saved
10037 || mData->mMachineState == MachineState_AbortedSaved
10038 || mData->mMachineState == MachineState_Restoring
10039 // when doing certain snapshot operations we may or may not have
10040 // a saved state in the current state, so keep everything as is
10041 || ( ( mData->mMachineState == MachineState_Snapshotting
10042 || mData->mMachineState == MachineState_DeletingSnapshot
10043 || mData->mMachineState == MachineState_RestoringSnapshot)
10044 && (!mSSData->strStateFilePath.isEmpty())
10045 )
10046 )
10047 {
10048 Assert(!mSSData->strStateFilePath.isEmpty());
10049 /* try to make the file name relative to the settings file dir */
10050 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10051 }
10052 else
10053 {
10054 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10055 config.strStateFile.setNull();
10056 }
10057
10058 if (mData->mCurrentSnapshot)
10059 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10060 else
10061 config.uuidCurrentSnapshot.clear();
10062
10063 config.timeLastStateChange = mData->mLastStateChange;
10064 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10065 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10066
10067 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10068 if (FAILED(rc)) throw rc;
10069
10070 // save machine's media registry if this is VirtualBox 4.0 or later
10071 if (config.canHaveOwnMediaRegistry())
10072 {
10073 // determine machine folder
10074 Utf8Str strMachineFolder = i_getSettingsFileFull();
10075 strMachineFolder.stripFilename();
10076 mParent->i_saveMediaRegistry(config.mediaRegistry,
10077 i_getId(), // only media with registry ID == machine UUID
10078 strMachineFolder);
10079 // this throws HRESULT
10080 }
10081
10082 // save snapshots
10083 rc = i_saveAllSnapshots(config);
10084 if (FAILED(rc)) throw rc;
10085}
10086
10087/**
10088 * Saves all snapshots of the machine into the given machine config file. Called
10089 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10090 * @param config
10091 * @return
10092 */
10093HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10094{
10095 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10096
10097 HRESULT rc = S_OK;
10098
10099 try
10100 {
10101 config.llFirstSnapshot.clear();
10102
10103 if (mData->mFirstSnapshot)
10104 {
10105 // the settings use a list for "the first snapshot"
10106 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10107
10108 // get reference to the snapshot on the list and work on that
10109 // element straight in the list to avoid excessive copying later
10110 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10111 if (FAILED(rc)) throw rc;
10112 }
10113
10114// if (mType == IsSessionMachine)
10115// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10116
10117 }
10118 catch (HRESULT err)
10119 {
10120 /* we assume that error info is set by the thrower */
10121 rc = err;
10122 }
10123 catch (...)
10124 {
10125 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10126 }
10127
10128 return rc;
10129}
10130
10131/**
10132 * Saves the VM hardware configuration. It is assumed that the
10133 * given node is empty.
10134 *
10135 * @param data Reference to the settings object for the hardware config.
10136 * @param pDbg Pointer to the settings object for the debugging config
10137 * which happens to live in mHWData.
10138 * @param pAutostart Pointer to the settings object for the autostart config
10139 * which happens to live in mHWData.
10140 */
10141HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10142 settings::Autostart *pAutostart)
10143{
10144 HRESULT rc = S_OK;
10145
10146 try
10147 {
10148 /* The hardware version attribute (optional).
10149 Automatically upgrade from 1 to current default hardware version
10150 when there is no saved state. (ugly!) */
10151 if ( mHWData->mHWVersion == "1"
10152 && mSSData->strStateFilePath.isEmpty()
10153 )
10154 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10155
10156 data.strVersion = mHWData->mHWVersion;
10157 data.uuid = mHWData->mHardwareUUID;
10158
10159 // CPU
10160 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10161 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10162 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10163 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10164 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10165 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10166 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10167 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10168 data.fPAE = !!mHWData->mPAEEnabled;
10169 data.enmLongMode = mHWData->mLongMode;
10170 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10171 data.fAPIC = !!mHWData->mAPIC;
10172 data.fX2APIC = !!mHWData->mX2APIC;
10173 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10174 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10175 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10176 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10177 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10178 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10179 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10180 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10181 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10182 data.cCPUs = mHWData->mCPUCount;
10183 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10184 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10185 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10186 data.strCpuProfile = mHWData->mCpuProfile;
10187
10188 data.llCpus.clear();
10189 if (data.fCpuHotPlug)
10190 {
10191 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10192 {
10193 if (mHWData->mCPUAttached[idx])
10194 {
10195 settings::Cpu cpu;
10196 cpu.ulId = idx;
10197 data.llCpus.push_back(cpu);
10198 }
10199 }
10200 }
10201
10202 /* Standard and Extended CPUID leafs. */
10203 data.llCpuIdLeafs.clear();
10204 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10205
10206 // memory
10207 data.ulMemorySizeMB = mHWData->mMemorySize;
10208 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10209
10210 // firmware
10211 data.firmwareType = mHWData->mFirmwareType;
10212
10213 // HID
10214 data.pointingHIDType = mHWData->mPointingHIDType;
10215 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10216
10217 // chipset
10218 data.chipsetType = mHWData->mChipsetType;
10219
10220 // iommu
10221 data.iommuType = mHWData->mIommuType;
10222
10223 // paravirt
10224 data.paravirtProvider = mHWData->mParavirtProvider;
10225 data.strParavirtDebug = mHWData->mParavirtDebug;
10226
10227 // emulated USB card reader
10228 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10229
10230 // HPET
10231 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10232
10233 // boot order
10234 data.mapBootOrder.clear();
10235 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10236 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10237
10238 /* VRDEServer settings (optional) */
10239 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10240 if (FAILED(rc)) throw rc;
10241
10242 /* BIOS settings (required) */
10243 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10244 if (FAILED(rc)) throw rc;
10245
10246 /* Trusted Platform Module settings (required) */
10247 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10248 if (FAILED(rc)) throw rc;
10249
10250 /* NVRAM settings (required) */
10251 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10252 if (FAILED(rc)) throw rc;
10253
10254 /* Recording settings (required) */
10255 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10256 if (FAILED(rc)) throw rc;
10257
10258 /* GraphicsAdapter settings (required) */
10259 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10260 if (FAILED(rc)) throw rc;
10261
10262 /* USB Controller (required) */
10263 data.usbSettings.llUSBControllers.clear();
10264 for (USBControllerList::const_iterator
10265 it = mUSBControllers->begin();
10266 it != mUSBControllers->end();
10267 ++it)
10268 {
10269 ComObjPtr<USBController> ctrl = *it;
10270 settings::USBController settingsCtrl;
10271
10272 settingsCtrl.strName = ctrl->i_getName();
10273 settingsCtrl.enmType = ctrl->i_getControllerType();
10274
10275 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10276 }
10277
10278 /* USB device filters (required) */
10279 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10280 if (FAILED(rc)) throw rc;
10281
10282 /* Network adapters (required) */
10283 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10284 data.llNetworkAdapters.clear();
10285 /* Write out only the nominal number of network adapters for this
10286 * chipset type. Since Machine::commit() hasn't been called there
10287 * may be extra NIC settings in the vector. */
10288 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10289 {
10290 settings::NetworkAdapter nic;
10291 nic.ulSlot = (uint32_t)slot;
10292 /* paranoia check... must not be NULL, but must not crash either. */
10293 if (mNetworkAdapters[slot])
10294 {
10295 if (mNetworkAdapters[slot]->i_hasDefaults())
10296 continue;
10297
10298 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10299 if (FAILED(rc)) throw rc;
10300
10301 data.llNetworkAdapters.push_back(nic);
10302 }
10303 }
10304
10305 /* Serial ports */
10306 data.llSerialPorts.clear();
10307 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10308 {
10309 if (mSerialPorts[slot]->i_hasDefaults())
10310 continue;
10311
10312 settings::SerialPort s;
10313 s.ulSlot = slot;
10314 rc = mSerialPorts[slot]->i_saveSettings(s);
10315 if (FAILED(rc)) return rc;
10316
10317 data.llSerialPorts.push_back(s);
10318 }
10319
10320 /* Parallel ports */
10321 data.llParallelPorts.clear();
10322 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10323 {
10324 if (mParallelPorts[slot]->i_hasDefaults())
10325 continue;
10326
10327 settings::ParallelPort p;
10328 p.ulSlot = slot;
10329 rc = mParallelPorts[slot]->i_saveSettings(p);
10330 if (FAILED(rc)) return rc;
10331
10332 data.llParallelPorts.push_back(p);
10333 }
10334
10335 /* Audio adapter */
10336 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10337 if (FAILED(rc)) return rc;
10338
10339 rc = i_saveStorageControllers(data.storage);
10340 if (FAILED(rc)) return rc;
10341
10342 /* Shared folders */
10343 data.llSharedFolders.clear();
10344 for (HWData::SharedFolderList::const_iterator
10345 it = mHWData->mSharedFolders.begin();
10346 it != mHWData->mSharedFolders.end();
10347 ++it)
10348 {
10349 SharedFolder *pSF = *it;
10350 AutoCaller sfCaller(pSF);
10351 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10352 settings::SharedFolder sf;
10353 sf.strName = pSF->i_getName();
10354 sf.strHostPath = pSF->i_getHostPath();
10355 sf.fWritable = !!pSF->i_isWritable();
10356 sf.fAutoMount = !!pSF->i_isAutoMounted();
10357 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10358
10359 data.llSharedFolders.push_back(sf);
10360 }
10361
10362 // clipboard
10363 data.clipboardMode = mHWData->mClipboardMode;
10364 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10365
10366 // drag'n'drop
10367 data.dndMode = mHWData->mDnDMode;
10368
10369 /* Guest */
10370 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10371
10372 // IO settings
10373 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10374 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10375
10376 /* BandwidthControl (required) */
10377 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10378 if (FAILED(rc)) throw rc;
10379
10380 /* Host PCI devices */
10381 data.pciAttachments.clear();
10382 for (HWData::PCIDeviceAssignmentList::const_iterator
10383 it = mHWData->mPCIDeviceAssignments.begin();
10384 it != mHWData->mPCIDeviceAssignments.end();
10385 ++it)
10386 {
10387 ComObjPtr<PCIDeviceAttachment> pda = *it;
10388 settings::HostPCIDeviceAttachment hpda;
10389
10390 rc = pda->i_saveSettings(hpda);
10391 if (FAILED(rc)) throw rc;
10392
10393 data.pciAttachments.push_back(hpda);
10394 }
10395
10396 // guest properties
10397 data.llGuestProperties.clear();
10398#ifdef VBOX_WITH_GUEST_PROPS
10399 for (HWData::GuestPropertyMap::const_iterator
10400 it = mHWData->mGuestProperties.begin();
10401 it != mHWData->mGuestProperties.end();
10402 ++it)
10403 {
10404 HWData::GuestProperty property = it->second;
10405
10406 /* Remove transient guest properties at shutdown unless we
10407 * are saving state. Note that restoring snapshot intentionally
10408 * keeps them, they will be removed if appropriate once the final
10409 * machine state is set (as crashes etc. need to work). */
10410 if ( ( mData->mMachineState == MachineState_PoweredOff
10411 || mData->mMachineState == MachineState_Aborted
10412 || mData->mMachineState == MachineState_Teleported)
10413 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10414 continue;
10415 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10416 prop.strName = it->first;
10417 prop.strValue = property.strValue;
10418 prop.timestamp = (uint64_t)property.mTimestamp;
10419 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10420 GuestPropWriteFlags(property.mFlags, szFlags);
10421 prop.strFlags = szFlags;
10422
10423 data.llGuestProperties.push_back(prop);
10424 }
10425
10426 /* I presume this doesn't require a backup(). */
10427 mData->mGuestPropertiesModified = FALSE;
10428#endif /* VBOX_WITH_GUEST_PROPS defined */
10429
10430 *pDbg = mHWData->mDebugging;
10431 *pAutostart = mHWData->mAutostart;
10432
10433 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10434 }
10435 catch (std::bad_alloc &)
10436 {
10437 return E_OUTOFMEMORY;
10438 }
10439
10440 AssertComRC(rc);
10441 return rc;
10442}
10443
10444/**
10445 * Saves the storage controller configuration.
10446 *
10447 * @param data storage settings.
10448 */
10449HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10450{
10451 data.llStorageControllers.clear();
10452
10453 for (StorageControllerList::const_iterator
10454 it = mStorageControllers->begin();
10455 it != mStorageControllers->end();
10456 ++it)
10457 {
10458 HRESULT rc;
10459 ComObjPtr<StorageController> pCtl = *it;
10460
10461 settings::StorageController ctl;
10462 ctl.strName = pCtl->i_getName();
10463 ctl.controllerType = pCtl->i_getControllerType();
10464 ctl.storageBus = pCtl->i_getStorageBus();
10465 ctl.ulInstance = pCtl->i_getInstance();
10466 ctl.fBootable = pCtl->i_getBootable();
10467
10468 /* Save the port count. */
10469 ULONG portCount;
10470 rc = pCtl->COMGETTER(PortCount)(&portCount);
10471 ComAssertComRCRet(rc, rc);
10472 ctl.ulPortCount = portCount;
10473
10474 /* Save fUseHostIOCache */
10475 BOOL fUseHostIOCache;
10476 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10477 ComAssertComRCRet(rc, rc);
10478 ctl.fUseHostIOCache = !!fUseHostIOCache;
10479
10480 /* save the devices now. */
10481 rc = i_saveStorageDevices(pCtl, ctl);
10482 ComAssertComRCRet(rc, rc);
10483
10484 data.llStorageControllers.push_back(ctl);
10485 }
10486
10487 return S_OK;
10488}
10489
10490/**
10491 * Saves the hard disk configuration.
10492 */
10493HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10494 settings::StorageController &data)
10495{
10496 MediumAttachmentList atts;
10497
10498 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10499 if (FAILED(rc)) return rc;
10500
10501 data.llAttachedDevices.clear();
10502 for (MediumAttachmentList::const_iterator
10503 it = atts.begin();
10504 it != atts.end();
10505 ++it)
10506 {
10507 settings::AttachedDevice dev;
10508 IMediumAttachment *iA = *it;
10509 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10510 Medium *pMedium = pAttach->i_getMedium();
10511
10512 dev.deviceType = pAttach->i_getType();
10513 dev.lPort = pAttach->i_getPort();
10514 dev.lDevice = pAttach->i_getDevice();
10515 dev.fPassThrough = pAttach->i_getPassthrough();
10516 dev.fHotPluggable = pAttach->i_getHotPluggable();
10517 if (pMedium)
10518 {
10519 if (pMedium->i_isHostDrive())
10520 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10521 else
10522 dev.uuid = pMedium->i_getId();
10523 dev.fTempEject = pAttach->i_getTempEject();
10524 dev.fNonRotational = pAttach->i_getNonRotational();
10525 dev.fDiscard = pAttach->i_getDiscard();
10526 }
10527
10528 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10529
10530 data.llAttachedDevices.push_back(dev);
10531 }
10532
10533 return S_OK;
10534}
10535
10536/**
10537 * Saves machine state settings as defined by aFlags
10538 * (SaveSTS_* values).
10539 *
10540 * @param aFlags Combination of SaveSTS_* flags.
10541 *
10542 * @note Locks objects for writing.
10543 */
10544HRESULT Machine::i_saveStateSettings(int aFlags)
10545{
10546 if (aFlags == 0)
10547 return S_OK;
10548
10549 AutoCaller autoCaller(this);
10550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10551
10552 /* This object's write lock is also necessary to serialize file access
10553 * (prevent concurrent reads and writes) */
10554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10555
10556 HRESULT rc = S_OK;
10557
10558 Assert(mData->pMachineConfigFile);
10559
10560 try
10561 {
10562 if (aFlags & SaveSTS_CurStateModified)
10563 mData->pMachineConfigFile->fCurrentStateModified = true;
10564
10565 if (aFlags & SaveSTS_StateFilePath)
10566 {
10567 if (!mSSData->strStateFilePath.isEmpty())
10568 /* try to make the file name relative to the settings file dir */
10569 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10570 else
10571 mData->pMachineConfigFile->strStateFile.setNull();
10572 }
10573
10574 if (aFlags & SaveSTS_StateTimeStamp)
10575 {
10576 Assert( mData->mMachineState != MachineState_Aborted
10577 || mSSData->strStateFilePath.isEmpty());
10578
10579 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10580
10581 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10582 || mData->mMachineState == MachineState_AbortedSaved);
10583/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10584 }
10585
10586 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10587 }
10588 catch (...)
10589 {
10590 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10591 }
10592
10593 return rc;
10594}
10595
10596/**
10597 * Ensures that the given medium is added to a media registry. If this machine
10598 * was created with 4.0 or later, then the machine registry is used. Otherwise
10599 * the global VirtualBox media registry is used.
10600 *
10601 * Caller must NOT hold machine lock, media tree or any medium locks!
10602 *
10603 * @param pMedium
10604 */
10605void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10606{
10607 /* Paranoia checks: do not hold machine or media tree locks. */
10608 AssertReturnVoid(!isWriteLockOnCurrentThread());
10609 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10610
10611 ComObjPtr<Medium> pBase;
10612 {
10613 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10614 pBase = pMedium->i_getBase();
10615 }
10616
10617 /* Paranoia checks: do not hold medium locks. */
10618 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10619 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10620
10621 // decide which medium registry to use now that the medium is attached:
10622 Guid uuid;
10623 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10624 if (fCanHaveOwnMediaRegistry)
10625 // machine XML is VirtualBox 4.0 or higher:
10626 uuid = i_getId(); // machine UUID
10627 else
10628 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10629
10630 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10631 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10632 if (pMedium->i_addRegistry(uuid))
10633 mParent->i_markRegistryModified(uuid);
10634
10635 /* For more complex hard disk structures it can happen that the base
10636 * medium isn't yet associated with any medium registry. Do that now. */
10637 if (pMedium != pBase)
10638 {
10639 /* Tree lock needed by Medium::addRegistry when recursing. */
10640 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10641 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10642 {
10643 treeLock.release();
10644 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10645 treeLock.acquire();
10646 }
10647 if (pBase->i_addRegistryRecursive(uuid))
10648 {
10649 treeLock.release();
10650 mParent->i_markRegistryModified(uuid);
10651 }
10652 }
10653}
10654
10655/**
10656 * Creates differencing hard disks for all normal hard disks attached to this
10657 * machine and a new set of attachments to refer to created disks.
10658 *
10659 * Used when taking a snapshot or when deleting the current state. Gets called
10660 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10661 *
10662 * This method assumes that mMediumAttachments contains the original hard disk
10663 * attachments it needs to create diffs for. On success, these attachments will
10664 * be replaced with the created diffs.
10665 *
10666 * Attachments with non-normal hard disks are left as is.
10667 *
10668 * If @a aOnline is @c false then the original hard disks that require implicit
10669 * diffs will be locked for reading. Otherwise it is assumed that they are
10670 * already locked for writing (when the VM was started). Note that in the latter
10671 * case it is responsibility of the caller to lock the newly created diffs for
10672 * writing if this method succeeds.
10673 *
10674 * @param aProgress Progress object to run (must contain at least as
10675 * many operations left as the number of hard disks
10676 * attached).
10677 * @param aWeight Weight of this operation.
10678 * @param aOnline Whether the VM was online prior to this operation.
10679 *
10680 * @note The progress object is not marked as completed, neither on success nor
10681 * on failure. This is a responsibility of the caller.
10682 *
10683 * @note Locks this object and the media tree for writing.
10684 */
10685HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10686 ULONG aWeight,
10687 bool aOnline)
10688{
10689 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10690
10691 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10692 AssertReturn(!!pProgressControl, E_INVALIDARG);
10693
10694 AutoCaller autoCaller(this);
10695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10696
10697 AutoMultiWriteLock2 alock(this->lockHandle(),
10698 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10699
10700 /* must be in a protective state because we release the lock below */
10701 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10702 || mData->mMachineState == MachineState_OnlineSnapshotting
10703 || mData->mMachineState == MachineState_LiveSnapshotting
10704 || mData->mMachineState == MachineState_RestoringSnapshot
10705 || mData->mMachineState == MachineState_DeletingSnapshot
10706 , E_FAIL);
10707
10708 HRESULT rc = S_OK;
10709
10710 // use appropriate locked media map (online or offline)
10711 MediumLockListMap lockedMediaOffline;
10712 MediumLockListMap *lockedMediaMap;
10713 if (aOnline)
10714 lockedMediaMap = &mData->mSession.mLockedMedia;
10715 else
10716 lockedMediaMap = &lockedMediaOffline;
10717
10718 try
10719 {
10720 if (!aOnline)
10721 {
10722 /* lock all attached hard disks early to detect "in use"
10723 * situations before creating actual diffs */
10724 for (MediumAttachmentList::const_iterator
10725 it = mMediumAttachments->begin();
10726 it != mMediumAttachments->end();
10727 ++it)
10728 {
10729 MediumAttachment *pAtt = *it;
10730 if (pAtt->i_getType() == DeviceType_HardDisk)
10731 {
10732 Medium *pMedium = pAtt->i_getMedium();
10733 Assert(pMedium);
10734
10735 MediumLockList *pMediumLockList(new MediumLockList());
10736 alock.release();
10737 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10738 NULL /* pToLockWrite */,
10739 false /* fMediumLockWriteAll */,
10740 NULL,
10741 *pMediumLockList);
10742 alock.acquire();
10743 if (FAILED(rc))
10744 {
10745 delete pMediumLockList;
10746 throw rc;
10747 }
10748 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10749 if (FAILED(rc))
10750 {
10751 throw setError(rc,
10752 tr("Collecting locking information for all attached media failed"));
10753 }
10754 }
10755 }
10756
10757 /* Now lock all media. If this fails, nothing is locked. */
10758 alock.release();
10759 rc = lockedMediaMap->Lock();
10760 alock.acquire();
10761 if (FAILED(rc))
10762 {
10763 throw setError(rc,
10764 tr("Locking of attached media failed"));
10765 }
10766 }
10767
10768 /* remember the current list (note that we don't use backup() since
10769 * mMediumAttachments may be already backed up) */
10770 MediumAttachmentList atts = *mMediumAttachments.data();
10771
10772 /* start from scratch */
10773 mMediumAttachments->clear();
10774
10775 /* go through remembered attachments and create diffs for normal hard
10776 * disks and attach them */
10777 for (MediumAttachmentList::const_iterator
10778 it = atts.begin();
10779 it != atts.end();
10780 ++it)
10781 {
10782 MediumAttachment *pAtt = *it;
10783
10784 DeviceType_T devType = pAtt->i_getType();
10785 Medium *pMedium = pAtt->i_getMedium();
10786
10787 if ( devType != DeviceType_HardDisk
10788 || pMedium == NULL
10789 || pMedium->i_getType() != MediumType_Normal)
10790 {
10791 /* copy the attachment as is */
10792
10793 /** @todo the progress object created in SessionMachine::TakeSnaphot
10794 * only expects operations for hard disks. Later other
10795 * device types need to show up in the progress as well. */
10796 if (devType == DeviceType_HardDisk)
10797 {
10798 if (pMedium == NULL)
10799 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10800 aWeight); // weight
10801 else
10802 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10803 pMedium->i_getBase()->i_getName().c_str()).raw(),
10804 aWeight); // weight
10805 }
10806
10807 mMediumAttachments->push_back(pAtt);
10808 continue;
10809 }
10810
10811 /* need a diff */
10812 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10813 pMedium->i_getBase()->i_getName().c_str()).raw(),
10814 aWeight); // weight
10815
10816 Utf8Str strFullSnapshotFolder;
10817 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10818
10819 ComObjPtr<Medium> diff;
10820 diff.createObject();
10821 // store the diff in the same registry as the parent
10822 // (this cannot fail here because we can't create implicit diffs for
10823 // unregistered images)
10824 Guid uuidRegistryParent;
10825 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10826 Assert(fInRegistry); NOREF(fInRegistry);
10827 rc = diff->init(mParent,
10828 pMedium->i_getPreferredDiffFormat(),
10829 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10830 uuidRegistryParent,
10831 DeviceType_HardDisk);
10832 if (FAILED(rc)) throw rc;
10833
10834 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10835 * the push_back? Looks like we're going to release medium with the
10836 * wrong kind of lock (general issue with if we fail anywhere at all)
10837 * and an orphaned VDI in the snapshots folder. */
10838
10839 /* update the appropriate lock list */
10840 MediumLockList *pMediumLockList;
10841 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10842 AssertComRCThrowRC(rc);
10843 if (aOnline)
10844 {
10845 alock.release();
10846 /* The currently attached medium will be read-only, change
10847 * the lock type to read. */
10848 rc = pMediumLockList->Update(pMedium, false);
10849 alock.acquire();
10850 AssertComRCThrowRC(rc);
10851 }
10852
10853 /* release the locks before the potentially lengthy operation */
10854 alock.release();
10855 rc = pMedium->i_createDiffStorage(diff,
10856 pMedium->i_getPreferredDiffVariant(),
10857 pMediumLockList,
10858 NULL /* aProgress */,
10859 true /* aWait */,
10860 false /* aNotify */);
10861 alock.acquire();
10862 if (FAILED(rc)) throw rc;
10863
10864 /* actual lock list update is done in Machine::i_commitMedia */
10865
10866 rc = diff->i_addBackReference(mData->mUuid);
10867 AssertComRCThrowRC(rc);
10868
10869 /* add a new attachment */
10870 ComObjPtr<MediumAttachment> attachment;
10871 attachment.createObject();
10872 rc = attachment->init(this,
10873 diff,
10874 pAtt->i_getControllerName(),
10875 pAtt->i_getPort(),
10876 pAtt->i_getDevice(),
10877 DeviceType_HardDisk,
10878 true /* aImplicit */,
10879 false /* aPassthrough */,
10880 false /* aTempEject */,
10881 pAtt->i_getNonRotational(),
10882 pAtt->i_getDiscard(),
10883 pAtt->i_getHotPluggable(),
10884 pAtt->i_getBandwidthGroup());
10885 if (FAILED(rc)) throw rc;
10886
10887 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10888 AssertComRCThrowRC(rc);
10889 mMediumAttachments->push_back(attachment);
10890 }
10891 }
10892 catch (HRESULT aRC) { rc = aRC; }
10893
10894 /* unlock all hard disks we locked when there is no VM */
10895 if (!aOnline)
10896 {
10897 ErrorInfoKeeper eik;
10898
10899 HRESULT rc1 = lockedMediaMap->Clear();
10900 AssertComRC(rc1);
10901 }
10902
10903 return rc;
10904}
10905
10906/**
10907 * Deletes implicit differencing hard disks created either by
10908 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10909 * mMediumAttachments.
10910 *
10911 * Note that to delete hard disks created by #attachDevice() this method is
10912 * called from #i_rollbackMedia() when the changes are rolled back.
10913 *
10914 * @note Locks this object and the media tree for writing.
10915 */
10916HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10917{
10918 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10919
10920 AutoCaller autoCaller(this);
10921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10922
10923 AutoMultiWriteLock2 alock(this->lockHandle(),
10924 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10925
10926 /* We absolutely must have backed up state. */
10927 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10928
10929 /* Check if there are any implicitly created diff images. */
10930 bool fImplicitDiffs = false;
10931 for (MediumAttachmentList::const_iterator
10932 it = mMediumAttachments->begin();
10933 it != mMediumAttachments->end();
10934 ++it)
10935 {
10936 const ComObjPtr<MediumAttachment> &pAtt = *it;
10937 if (pAtt->i_isImplicit())
10938 {
10939 fImplicitDiffs = true;
10940 break;
10941 }
10942 }
10943 /* If there is nothing to do, leave early. This saves lots of image locking
10944 * effort. It also avoids a MachineStateChanged event without real reason.
10945 * This is important e.g. when loading a VM config, because there should be
10946 * no events. Otherwise API clients can become thoroughly confused for
10947 * inaccessible VMs (the code for loading VM configs uses this method for
10948 * cleanup if the config makes no sense), as they take such events as an
10949 * indication that the VM is alive, and they would force the VM config to
10950 * be reread, leading to an endless loop. */
10951 if (!fImplicitDiffs)
10952 return S_OK;
10953
10954 HRESULT rc = S_OK;
10955 MachineState_T oldState = mData->mMachineState;
10956
10957 /* will release the lock before the potentially lengthy operation,
10958 * so protect with the special state (unless already protected) */
10959 if ( oldState != MachineState_Snapshotting
10960 && oldState != MachineState_OnlineSnapshotting
10961 && oldState != MachineState_LiveSnapshotting
10962 && oldState != MachineState_RestoringSnapshot
10963 && oldState != MachineState_DeletingSnapshot
10964 && oldState != MachineState_DeletingSnapshotOnline
10965 && oldState != MachineState_DeletingSnapshotPaused
10966 )
10967 i_setMachineState(MachineState_SettingUp);
10968
10969 // use appropriate locked media map (online or offline)
10970 MediumLockListMap lockedMediaOffline;
10971 MediumLockListMap *lockedMediaMap;
10972 if (aOnline)
10973 lockedMediaMap = &mData->mSession.mLockedMedia;
10974 else
10975 lockedMediaMap = &lockedMediaOffline;
10976
10977 try
10978 {
10979 if (!aOnline)
10980 {
10981 /* lock all attached hard disks early to detect "in use"
10982 * situations before deleting actual diffs */
10983 for (MediumAttachmentList::const_iterator
10984 it = mMediumAttachments->begin();
10985 it != mMediumAttachments->end();
10986 ++it)
10987 {
10988 MediumAttachment *pAtt = *it;
10989 if (pAtt->i_getType() == DeviceType_HardDisk)
10990 {
10991 Medium *pMedium = pAtt->i_getMedium();
10992 Assert(pMedium);
10993
10994 MediumLockList *pMediumLockList(new MediumLockList());
10995 alock.release();
10996 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10997 NULL /* pToLockWrite */,
10998 false /* fMediumLockWriteAll */,
10999 NULL,
11000 *pMediumLockList);
11001 alock.acquire();
11002
11003 if (FAILED(rc))
11004 {
11005 delete pMediumLockList;
11006 throw rc;
11007 }
11008
11009 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11010 if (FAILED(rc))
11011 throw rc;
11012 }
11013 }
11014
11015 if (FAILED(rc))
11016 throw rc;
11017 } // end of offline
11018
11019 /* Lock lists are now up to date and include implicitly created media */
11020
11021 /* Go through remembered attachments and delete all implicitly created
11022 * diffs and fix up the attachment information */
11023 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11024 MediumAttachmentList implicitAtts;
11025 for (MediumAttachmentList::const_iterator
11026 it = mMediumAttachments->begin();
11027 it != mMediumAttachments->end();
11028 ++it)
11029 {
11030 ComObjPtr<MediumAttachment> pAtt = *it;
11031 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11032 if (pMedium.isNull())
11033 continue;
11034
11035 // Implicit attachments go on the list for deletion and back references are removed.
11036 if (pAtt->i_isImplicit())
11037 {
11038 /* Deassociate and mark for deletion */
11039 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11040 rc = pMedium->i_removeBackReference(mData->mUuid);
11041 if (FAILED(rc))
11042 throw rc;
11043 implicitAtts.push_back(pAtt);
11044 continue;
11045 }
11046
11047 /* Was this medium attached before? */
11048 if (!i_findAttachment(oldAtts, pMedium))
11049 {
11050 /* no: de-associate */
11051 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11052 rc = pMedium->i_removeBackReference(mData->mUuid);
11053 if (FAILED(rc))
11054 throw rc;
11055 continue;
11056 }
11057 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11058 }
11059
11060 /* If there are implicit attachments to delete, throw away the lock
11061 * map contents (which will unlock all media) since the medium
11062 * attachments will be rolled back. Below we need to completely
11063 * recreate the lock map anyway since it is infinitely complex to
11064 * do this incrementally (would need reconstructing each attachment
11065 * change, which would be extremely hairy). */
11066 if (implicitAtts.size() != 0)
11067 {
11068 ErrorInfoKeeper eik;
11069
11070 HRESULT rc1 = lockedMediaMap->Clear();
11071 AssertComRC(rc1);
11072 }
11073
11074 /* rollback hard disk changes */
11075 mMediumAttachments.rollback();
11076
11077 MultiResult mrc(S_OK);
11078
11079 // Delete unused implicit diffs.
11080 if (implicitAtts.size() != 0)
11081 {
11082 alock.release();
11083
11084 for (MediumAttachmentList::const_iterator
11085 it = implicitAtts.begin();
11086 it != implicitAtts.end();
11087 ++it)
11088 {
11089 // Remove medium associated with this attachment.
11090 ComObjPtr<MediumAttachment> pAtt = *it;
11091 Assert(pAtt);
11092 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11093 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11094 Assert(pMedium);
11095
11096 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11097 // continue on delete failure, just collect error messages
11098 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11099 pMedium->i_getLocationFull().c_str() ));
11100 mrc = rc;
11101 }
11102 // Clear the list of deleted implicit attachments now, while not
11103 // holding the lock, as it will ultimately trigger Medium::uninit()
11104 // calls which assume that the media tree lock isn't held.
11105 implicitAtts.clear();
11106
11107 alock.acquire();
11108
11109 /* if there is a VM recreate media lock map as mentioned above,
11110 * otherwise it is a waste of time and we leave things unlocked */
11111 if (aOnline)
11112 {
11113 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11114 /* must never be NULL, but better safe than sorry */
11115 if (!pMachine.isNull())
11116 {
11117 alock.release();
11118 rc = mData->mSession.mMachine->i_lockMedia();
11119 alock.acquire();
11120 if (FAILED(rc))
11121 throw rc;
11122 }
11123 }
11124 }
11125 }
11126 catch (HRESULT aRC) {rc = aRC;}
11127
11128 if (mData->mMachineState == MachineState_SettingUp)
11129 i_setMachineState(oldState);
11130
11131 /* unlock all hard disks we locked when there is no VM */
11132 if (!aOnline)
11133 {
11134 ErrorInfoKeeper eik;
11135
11136 HRESULT rc1 = lockedMediaMap->Clear();
11137 AssertComRC(rc1);
11138 }
11139
11140 return rc;
11141}
11142
11143
11144/**
11145 * Looks through the given list of media attachments for one with the given parameters
11146 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11147 * can be searched as well if needed.
11148 *
11149 * @param ll
11150 * @param aControllerName
11151 * @param aControllerPort
11152 * @param aDevice
11153 * @return
11154 */
11155MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11156 const Utf8Str &aControllerName,
11157 LONG aControllerPort,
11158 LONG aDevice)
11159{
11160 for (MediumAttachmentList::const_iterator
11161 it = ll.begin();
11162 it != ll.end();
11163 ++it)
11164 {
11165 MediumAttachment *pAttach = *it;
11166 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11167 return pAttach;
11168 }
11169
11170 return NULL;
11171}
11172
11173/**
11174 * Looks through the given list of media attachments for one with the given parameters
11175 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11176 * can be searched as well if needed.
11177 *
11178 * @param ll
11179 * @param pMedium
11180 * @return
11181 */
11182MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11183 ComObjPtr<Medium> pMedium)
11184{
11185 for (MediumAttachmentList::const_iterator
11186 it = ll.begin();
11187 it != ll.end();
11188 ++it)
11189 {
11190 MediumAttachment *pAttach = *it;
11191 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11192 if (pMediumThis == pMedium)
11193 return pAttach;
11194 }
11195
11196 return NULL;
11197}
11198
11199/**
11200 * Looks through the given list of media attachments for one with the given parameters
11201 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11202 * can be searched as well if needed.
11203 *
11204 * @param ll
11205 * @param id
11206 * @return
11207 */
11208MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11209 Guid &id)
11210{
11211 for (MediumAttachmentList::const_iterator
11212 it = ll.begin();
11213 it != ll.end();
11214 ++it)
11215 {
11216 MediumAttachment *pAttach = *it;
11217 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11218 if (pMediumThis->i_getId() == id)
11219 return pAttach;
11220 }
11221
11222 return NULL;
11223}
11224
11225/**
11226 * Main implementation for Machine::DetachDevice. This also gets called
11227 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11228 *
11229 * @param pAttach Medium attachment to detach.
11230 * @param writeLock Machine write lock which the caller must have locked once.
11231 * This may be released temporarily in here.
11232 * @param pSnapshot If NULL, then the detachment is for the current machine.
11233 * Otherwise this is for a SnapshotMachine, and this must be
11234 * its snapshot.
11235 * @return
11236 */
11237HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11238 AutoWriteLock &writeLock,
11239 Snapshot *pSnapshot)
11240{
11241 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11242 DeviceType_T mediumType = pAttach->i_getType();
11243
11244 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11245
11246 if (pAttach->i_isImplicit())
11247 {
11248 /* attempt to implicitly delete the implicitly created diff */
11249
11250 /// @todo move the implicit flag from MediumAttachment to Medium
11251 /// and forbid any hard disk operation when it is implicit. Or maybe
11252 /// a special media state for it to make it even more simple.
11253
11254 Assert(mMediumAttachments.isBackedUp());
11255
11256 /* will release the lock before the potentially lengthy operation, so
11257 * protect with the special state */
11258 MachineState_T oldState = mData->mMachineState;
11259 i_setMachineState(MachineState_SettingUp);
11260
11261 writeLock.release();
11262
11263 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11264 true /*aWait*/,
11265 false /*aNotify*/);
11266
11267 writeLock.acquire();
11268
11269 i_setMachineState(oldState);
11270
11271 if (FAILED(rc)) return rc;
11272 }
11273
11274 i_setModified(IsModified_Storage);
11275 mMediumAttachments.backup();
11276 mMediumAttachments->remove(pAttach);
11277
11278 if (!oldmedium.isNull())
11279 {
11280 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11281 if (pSnapshot)
11282 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11283 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11284 else if (mediumType != DeviceType_HardDisk)
11285 oldmedium->i_removeBackReference(mData->mUuid);
11286 }
11287
11288 return S_OK;
11289}
11290
11291/**
11292 * Goes thru all media of the given list and
11293 *
11294 * 1) calls i_detachDevice() on each of them for this machine and
11295 * 2) adds all Medium objects found in the process to the given list,
11296 * depending on cleanupMode.
11297 *
11298 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11299 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11300 * media to the list.
11301 *
11302 * This gets called from Machine::Unregister, both for the actual Machine and
11303 * the SnapshotMachine objects that might be found in the snapshots.
11304 *
11305 * Requires caller and locking. The machine lock must be passed in because it
11306 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11307 *
11308 * @param writeLock Machine lock from top-level caller; this gets passed to
11309 * i_detachDevice.
11310 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11311 * object if called for a SnapshotMachine.
11312 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11313 * added to llMedia; if Full, then all media get added;
11314 * otherwise no media get added.
11315 * @param llMedia Caller's list to receive Medium objects which got detached so
11316 * caller can close() them, depending on cleanupMode.
11317 * @return
11318 */
11319HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11320 Snapshot *pSnapshot,
11321 CleanupMode_T cleanupMode,
11322 MediaList &llMedia)
11323{
11324 Assert(isWriteLockOnCurrentThread());
11325
11326 HRESULT rc;
11327
11328 // make a temporary list because i_detachDevice invalidates iterators into
11329 // mMediumAttachments
11330 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11331
11332 for (MediumAttachmentList::iterator
11333 it = llAttachments2.begin();
11334 it != llAttachments2.end();
11335 ++it)
11336 {
11337 ComObjPtr<MediumAttachment> &pAttach = *it;
11338 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11339
11340 if (!pMedium.isNull())
11341 {
11342 AutoCaller mac(pMedium);
11343 if (FAILED(mac.rc())) return mac.rc();
11344 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11345 DeviceType_T devType = pMedium->i_getDeviceType();
11346 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11347 && devType == DeviceType_HardDisk)
11348 || (cleanupMode == CleanupMode_Full)
11349 )
11350 {
11351 llMedia.push_back(pMedium);
11352 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11353 /* Not allowed to keep this lock as below we need the parent
11354 * medium lock, and the lock order is parent to child. */
11355 lock.release();
11356 /*
11357 * Search for medias which are not attached to any machine, but
11358 * in the chain to an attached disk. Mediums are only consided
11359 * if they are:
11360 * - have only one child
11361 * - no references to any machines
11362 * - are of normal medium type
11363 */
11364 while (!pParent.isNull())
11365 {
11366 AutoCaller mac1(pParent);
11367 if (FAILED(mac1.rc())) return mac1.rc();
11368 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11369 if (pParent->i_getChildren().size() == 1)
11370 {
11371 if ( pParent->i_getMachineBackRefCount() == 0
11372 && pParent->i_getType() == MediumType_Normal
11373 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11374 llMedia.push_back(pParent);
11375 }
11376 else
11377 break;
11378 pParent = pParent->i_getParent();
11379 }
11380 }
11381 }
11382
11383 // real machine: then we need to use the proper method
11384 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11385
11386 if (FAILED(rc))
11387 return rc;
11388 }
11389
11390 return S_OK;
11391}
11392
11393/**
11394 * Perform deferred hard disk detachments.
11395 *
11396 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11397 * changed (not backed up).
11398 *
11399 * If @a aOnline is @c true then this method will also unlock the old hard
11400 * disks for which the new implicit diffs were created and will lock these new
11401 * diffs for writing.
11402 *
11403 * @param aOnline Whether the VM was online prior to this operation.
11404 *
11405 * @note Locks this object for writing!
11406 */
11407void Machine::i_commitMedia(bool aOnline /*= false*/)
11408{
11409 AutoCaller autoCaller(this);
11410 AssertComRCReturnVoid(autoCaller.rc());
11411
11412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11413
11414 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11415
11416 HRESULT rc = S_OK;
11417
11418 /* no attach/detach operations -- nothing to do */
11419 if (!mMediumAttachments.isBackedUp())
11420 return;
11421
11422 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11423 bool fMediaNeedsLocking = false;
11424
11425 /* enumerate new attachments */
11426 for (MediumAttachmentList::const_iterator
11427 it = mMediumAttachments->begin();
11428 it != mMediumAttachments->end();
11429 ++it)
11430 {
11431 MediumAttachment *pAttach = *it;
11432
11433 pAttach->i_commit();
11434
11435 Medium *pMedium = pAttach->i_getMedium();
11436 bool fImplicit = pAttach->i_isImplicit();
11437
11438 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11439 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11440 fImplicit));
11441
11442 /** @todo convert all this Machine-based voodoo to MediumAttachment
11443 * based commit logic. */
11444 if (fImplicit)
11445 {
11446 /* convert implicit attachment to normal */
11447 pAttach->i_setImplicit(false);
11448
11449 if ( aOnline
11450 && pMedium
11451 && pAttach->i_getType() == DeviceType_HardDisk
11452 )
11453 {
11454 /* update the appropriate lock list */
11455 MediumLockList *pMediumLockList;
11456 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11457 AssertComRC(rc);
11458 if (pMediumLockList)
11459 {
11460 /* unlock if there's a need to change the locking */
11461 if (!fMediaNeedsLocking)
11462 {
11463 rc = mData->mSession.mLockedMedia.Unlock();
11464 AssertComRC(rc);
11465 fMediaNeedsLocking = true;
11466 }
11467 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11468 AssertComRC(rc);
11469 rc = pMediumLockList->Append(pMedium, true);
11470 AssertComRC(rc);
11471 }
11472 }
11473
11474 continue;
11475 }
11476
11477 if (pMedium)
11478 {
11479 /* was this medium attached before? */
11480 for (MediumAttachmentList::iterator
11481 oldIt = oldAtts.begin();
11482 oldIt != oldAtts.end();
11483 ++oldIt)
11484 {
11485 MediumAttachment *pOldAttach = *oldIt;
11486 if (pOldAttach->i_getMedium() == pMedium)
11487 {
11488 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11489
11490 /* yes: remove from old to avoid de-association */
11491 oldAtts.erase(oldIt);
11492 break;
11493 }
11494 }
11495 }
11496 }
11497
11498 /* enumerate remaining old attachments and de-associate from the
11499 * current machine state */
11500 for (MediumAttachmentList::const_iterator
11501 it = oldAtts.begin();
11502 it != oldAtts.end();
11503 ++it)
11504 {
11505 MediumAttachment *pAttach = *it;
11506 Medium *pMedium = pAttach->i_getMedium();
11507
11508 /* Detach only hard disks, since DVD/floppy media is detached
11509 * instantly in MountMedium. */
11510 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11511 {
11512 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11513
11514 /* now de-associate from the current machine state */
11515 rc = pMedium->i_removeBackReference(mData->mUuid);
11516 AssertComRC(rc);
11517
11518 if (aOnline)
11519 {
11520 /* unlock since medium is not used anymore */
11521 MediumLockList *pMediumLockList;
11522 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11523 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11524 {
11525 /* this happens for online snapshots, there the attachment
11526 * is changing, but only to a diff image created under
11527 * the old one, so there is no separate lock list */
11528 Assert(!pMediumLockList);
11529 }
11530 else
11531 {
11532 AssertComRC(rc);
11533 if (pMediumLockList)
11534 {
11535 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11536 AssertComRC(rc);
11537 }
11538 }
11539 }
11540 }
11541 }
11542
11543 /* take media locks again so that the locking state is consistent */
11544 if (fMediaNeedsLocking)
11545 {
11546 Assert(aOnline);
11547 rc = mData->mSession.mLockedMedia.Lock();
11548 AssertComRC(rc);
11549 }
11550
11551 /* commit the hard disk changes */
11552 mMediumAttachments.commit();
11553
11554 if (i_isSessionMachine())
11555 {
11556 /*
11557 * Update the parent machine to point to the new owner.
11558 * This is necessary because the stored parent will point to the
11559 * session machine otherwise and cause crashes or errors later
11560 * when the session machine gets invalid.
11561 */
11562 /** @todo Change the MediumAttachment class to behave like any other
11563 * class in this regard by creating peer MediumAttachment
11564 * objects for session machines and share the data with the peer
11565 * machine.
11566 */
11567 for (MediumAttachmentList::const_iterator
11568 it = mMediumAttachments->begin();
11569 it != mMediumAttachments->end();
11570 ++it)
11571 (*it)->i_updateParentMachine(mPeer);
11572
11573 /* attach new data to the primary machine and reshare it */
11574 mPeer->mMediumAttachments.attach(mMediumAttachments);
11575 }
11576
11577 return;
11578}
11579
11580/**
11581 * Perform deferred deletion of implicitly created diffs.
11582 *
11583 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11584 * changed (not backed up).
11585 *
11586 * @note Locks this object for writing!
11587 */
11588void Machine::i_rollbackMedia()
11589{
11590 AutoCaller autoCaller(this);
11591 AssertComRCReturnVoid(autoCaller.rc());
11592
11593 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11594 LogFlowThisFunc(("Entering rollbackMedia\n"));
11595
11596 HRESULT rc = S_OK;
11597
11598 /* no attach/detach operations -- nothing to do */
11599 if (!mMediumAttachments.isBackedUp())
11600 return;
11601
11602 /* enumerate new attachments */
11603 for (MediumAttachmentList::const_iterator
11604 it = mMediumAttachments->begin();
11605 it != mMediumAttachments->end();
11606 ++it)
11607 {
11608 MediumAttachment *pAttach = *it;
11609 /* Fix up the backrefs for DVD/floppy media. */
11610 if (pAttach->i_getType() != DeviceType_HardDisk)
11611 {
11612 Medium *pMedium = pAttach->i_getMedium();
11613 if (pMedium)
11614 {
11615 rc = pMedium->i_removeBackReference(mData->mUuid);
11616 AssertComRC(rc);
11617 }
11618 }
11619
11620 (*it)->i_rollback();
11621
11622 pAttach = *it;
11623 /* Fix up the backrefs for DVD/floppy media. */
11624 if (pAttach->i_getType() != DeviceType_HardDisk)
11625 {
11626 Medium *pMedium = pAttach->i_getMedium();
11627 if (pMedium)
11628 {
11629 rc = pMedium->i_addBackReference(mData->mUuid);
11630 AssertComRC(rc);
11631 }
11632 }
11633 }
11634
11635 /** @todo convert all this Machine-based voodoo to MediumAttachment
11636 * based rollback logic. */
11637 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11638
11639 return;
11640}
11641
11642/**
11643 * Returns true if the settings file is located in the directory named exactly
11644 * as the machine; this means, among other things, that the machine directory
11645 * should be auto-renamed.
11646 *
11647 * @param aSettingsDir if not NULL, the full machine settings file directory
11648 * name will be assigned there.
11649 *
11650 * @note Doesn't lock anything.
11651 * @note Not thread safe (must be called from this object's lock).
11652 */
11653bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11654{
11655 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11656 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11657 if (aSettingsDir)
11658 *aSettingsDir = strMachineDirName;
11659 strMachineDirName.stripPath(); // vmname
11660 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11661 strConfigFileOnly.stripPath() // vmname.vbox
11662 .stripSuffix(); // vmname
11663 /** @todo hack, make somehow use of ComposeMachineFilename */
11664 if (mUserData->s.fDirectoryIncludesUUID)
11665 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11666
11667 AssertReturn(!strMachineDirName.isEmpty(), false);
11668 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11669
11670 return strMachineDirName == strConfigFileOnly;
11671}
11672
11673/**
11674 * Discards all changes to machine settings.
11675 *
11676 * @param aNotify Whether to notify the direct session about changes or not.
11677 *
11678 * @note Locks objects for writing!
11679 */
11680void Machine::i_rollback(bool aNotify)
11681{
11682 AutoCaller autoCaller(this);
11683 AssertComRCReturn(autoCaller.rc(), (void)0);
11684
11685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11686
11687 if (!mStorageControllers.isNull())
11688 {
11689 if (mStorageControllers.isBackedUp())
11690 {
11691 /* unitialize all new devices (absent in the backed up list). */
11692 StorageControllerList *backedList = mStorageControllers.backedUpData();
11693 for (StorageControllerList::const_iterator
11694 it = mStorageControllers->begin();
11695 it != mStorageControllers->end();
11696 ++it)
11697 {
11698 if ( std::find(backedList->begin(), backedList->end(), *it)
11699 == backedList->end()
11700 )
11701 {
11702 (*it)->uninit();
11703 }
11704 }
11705
11706 /* restore the list */
11707 mStorageControllers.rollback();
11708 }
11709
11710 /* rollback any changes to devices after restoring the list */
11711 if (mData->flModifications & IsModified_Storage)
11712 {
11713 for (StorageControllerList::const_iterator
11714 it = mStorageControllers->begin();
11715 it != mStorageControllers->end();
11716 ++it)
11717 {
11718 (*it)->i_rollback();
11719 }
11720 }
11721 }
11722
11723 if (!mUSBControllers.isNull())
11724 {
11725 if (mUSBControllers.isBackedUp())
11726 {
11727 /* unitialize all new devices (absent in the backed up list). */
11728 USBControllerList *backedList = mUSBControllers.backedUpData();
11729 for (USBControllerList::const_iterator
11730 it = mUSBControllers->begin();
11731 it != mUSBControllers->end();
11732 ++it)
11733 {
11734 if ( std::find(backedList->begin(), backedList->end(), *it)
11735 == backedList->end()
11736 )
11737 {
11738 (*it)->uninit();
11739 }
11740 }
11741
11742 /* restore the list */
11743 mUSBControllers.rollback();
11744 }
11745
11746 /* rollback any changes to devices after restoring the list */
11747 if (mData->flModifications & IsModified_USB)
11748 {
11749 for (USBControllerList::const_iterator
11750 it = mUSBControllers->begin();
11751 it != mUSBControllers->end();
11752 ++it)
11753 {
11754 (*it)->i_rollback();
11755 }
11756 }
11757 }
11758
11759 mUserData.rollback();
11760
11761 mHWData.rollback();
11762
11763 if (mData->flModifications & IsModified_Storage)
11764 i_rollbackMedia();
11765
11766 if (mBIOSSettings)
11767 mBIOSSettings->i_rollback();
11768
11769 if (mTrustedPlatformModule)
11770 mTrustedPlatformModule->i_rollback();
11771
11772 if (mNvramStore)
11773 mNvramStore->i_rollback();
11774
11775 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11776 mRecordingSettings->i_rollback();
11777
11778 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11779 mGraphicsAdapter->i_rollback();
11780
11781 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11782 mVRDEServer->i_rollback();
11783
11784 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11785 mAudioAdapter->i_rollback();
11786
11787 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11788 mUSBDeviceFilters->i_rollback();
11789
11790 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11791 mBandwidthControl->i_rollback();
11792
11793 if (!mHWData.isNull())
11794 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11795 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11796 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11797 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11798
11799 if (mData->flModifications & IsModified_NetworkAdapters)
11800 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11801 if ( mNetworkAdapters[slot]
11802 && mNetworkAdapters[slot]->i_isModified())
11803 {
11804 mNetworkAdapters[slot]->i_rollback();
11805 networkAdapters[slot] = mNetworkAdapters[slot];
11806 }
11807
11808 if (mData->flModifications & IsModified_SerialPorts)
11809 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11810 if ( mSerialPorts[slot]
11811 && mSerialPorts[slot]->i_isModified())
11812 {
11813 mSerialPorts[slot]->i_rollback();
11814 serialPorts[slot] = mSerialPorts[slot];
11815 }
11816
11817 if (mData->flModifications & IsModified_ParallelPorts)
11818 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11819 if ( mParallelPorts[slot]
11820 && mParallelPorts[slot]->i_isModified())
11821 {
11822 mParallelPorts[slot]->i_rollback();
11823 parallelPorts[slot] = mParallelPorts[slot];
11824 }
11825
11826 if (aNotify)
11827 {
11828 /* inform the direct session about changes */
11829
11830 ComObjPtr<Machine> that = this;
11831 uint32_t flModifications = mData->flModifications;
11832 alock.release();
11833
11834 if (flModifications & IsModified_SharedFolders)
11835 that->i_onSharedFolderChange();
11836
11837 if (flModifications & IsModified_VRDEServer)
11838 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11839 if (flModifications & IsModified_USB)
11840 that->i_onUSBControllerChange();
11841
11842 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11843 if (networkAdapters[slot])
11844 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11845 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11846 if (serialPorts[slot])
11847 that->i_onSerialPortChange(serialPorts[slot]);
11848 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11849 if (parallelPorts[slot])
11850 that->i_onParallelPortChange(parallelPorts[slot]);
11851
11852 if (flModifications & IsModified_Storage)
11853 {
11854 for (StorageControllerList::const_iterator
11855 it = mStorageControllers->begin();
11856 it != mStorageControllers->end();
11857 ++it)
11858 {
11859 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11860 }
11861 }
11862
11863
11864#if 0
11865 if (flModifications & IsModified_BandwidthControl)
11866 that->onBandwidthControlChange();
11867#endif
11868 }
11869}
11870
11871/**
11872 * Commits all the changes to machine settings.
11873 *
11874 * Note that this operation is supposed to never fail.
11875 *
11876 * @note Locks this object and children for writing.
11877 */
11878void Machine::i_commit()
11879{
11880 AutoCaller autoCaller(this);
11881 AssertComRCReturnVoid(autoCaller.rc());
11882
11883 AutoCaller peerCaller(mPeer);
11884 AssertComRCReturnVoid(peerCaller.rc());
11885
11886 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11887
11888 /*
11889 * use safe commit to ensure Snapshot machines (that share mUserData)
11890 * will still refer to a valid memory location
11891 */
11892 mUserData.commitCopy();
11893
11894 mHWData.commit();
11895
11896 if (mMediumAttachments.isBackedUp())
11897 i_commitMedia(Global::IsOnline(mData->mMachineState));
11898
11899 mBIOSSettings->i_commit();
11900 mTrustedPlatformModule->i_commit();
11901 mNvramStore->i_commit();
11902 mRecordingSettings->i_commit();
11903 mGraphicsAdapter->i_commit();
11904 mVRDEServer->i_commit();
11905 mAudioAdapter->i_commit();
11906 mUSBDeviceFilters->i_commit();
11907 mBandwidthControl->i_commit();
11908
11909 /* Since mNetworkAdapters is a list which might have been changed (resized)
11910 * without using the Backupable<> template we need to handle the copying
11911 * of the list entries manually, including the creation of peers for the
11912 * new objects. */
11913 bool commitNetworkAdapters = false;
11914 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11915 if (mPeer)
11916 {
11917 /* commit everything, even the ones which will go away */
11918 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11919 mNetworkAdapters[slot]->i_commit();
11920 /* copy over the new entries, creating a peer and uninit the original */
11921 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11922 for (size_t slot = 0; slot < newSize; slot++)
11923 {
11924 /* look if this adapter has a peer device */
11925 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11926 if (!peer)
11927 {
11928 /* no peer means the adapter is a newly created one;
11929 * create a peer owning data this data share it with */
11930 peer.createObject();
11931 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11932 }
11933 mPeer->mNetworkAdapters[slot] = peer;
11934 }
11935 /* uninit any no longer needed network adapters */
11936 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11937 mNetworkAdapters[slot]->uninit();
11938 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11939 {
11940 if (mPeer->mNetworkAdapters[slot])
11941 mPeer->mNetworkAdapters[slot]->uninit();
11942 }
11943 /* Keep the original network adapter count until this point, so that
11944 * discarding a chipset type change will not lose settings. */
11945 mNetworkAdapters.resize(newSize);
11946 mPeer->mNetworkAdapters.resize(newSize);
11947 }
11948 else
11949 {
11950 /* we have no peer (our parent is the newly created machine);
11951 * just commit changes to the network adapters */
11952 commitNetworkAdapters = true;
11953 }
11954 if (commitNetworkAdapters)
11955 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11956 mNetworkAdapters[slot]->i_commit();
11957
11958 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11959 mSerialPorts[slot]->i_commit();
11960 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11961 mParallelPorts[slot]->i_commit();
11962
11963 bool commitStorageControllers = false;
11964
11965 if (mStorageControllers.isBackedUp())
11966 {
11967 mStorageControllers.commit();
11968
11969 if (mPeer)
11970 {
11971 /* Commit all changes to new controllers (this will reshare data with
11972 * peers for those who have peers) */
11973 StorageControllerList *newList = new StorageControllerList();
11974 for (StorageControllerList::const_iterator
11975 it = mStorageControllers->begin();
11976 it != mStorageControllers->end();
11977 ++it)
11978 {
11979 (*it)->i_commit();
11980
11981 /* look if this controller has a peer device */
11982 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11983 if (!peer)
11984 {
11985 /* no peer means the device is a newly created one;
11986 * create a peer owning data this device share it with */
11987 peer.createObject();
11988 peer->init(mPeer, *it, true /* aReshare */);
11989 }
11990 else
11991 {
11992 /* remove peer from the old list */
11993 mPeer->mStorageControllers->remove(peer);
11994 }
11995 /* and add it to the new list */
11996 newList->push_back(peer);
11997 }
11998
11999 /* uninit old peer's controllers that are left */
12000 for (StorageControllerList::const_iterator
12001 it = mPeer->mStorageControllers->begin();
12002 it != mPeer->mStorageControllers->end();
12003 ++it)
12004 {
12005 (*it)->uninit();
12006 }
12007
12008 /* attach new list of controllers to our peer */
12009 mPeer->mStorageControllers.attach(newList);
12010 }
12011 else
12012 {
12013 /* we have no peer (our parent is the newly created machine);
12014 * just commit changes to devices */
12015 commitStorageControllers = true;
12016 }
12017 }
12018 else
12019 {
12020 /* the list of controllers itself is not changed,
12021 * just commit changes to controllers themselves */
12022 commitStorageControllers = true;
12023 }
12024
12025 if (commitStorageControllers)
12026 {
12027 for (StorageControllerList::const_iterator
12028 it = mStorageControllers->begin();
12029 it != mStorageControllers->end();
12030 ++it)
12031 {
12032 (*it)->i_commit();
12033 }
12034 }
12035
12036 bool commitUSBControllers = false;
12037
12038 if (mUSBControllers.isBackedUp())
12039 {
12040 mUSBControllers.commit();
12041
12042 if (mPeer)
12043 {
12044 /* Commit all changes to new controllers (this will reshare data with
12045 * peers for those who have peers) */
12046 USBControllerList *newList = new USBControllerList();
12047 for (USBControllerList::const_iterator
12048 it = mUSBControllers->begin();
12049 it != mUSBControllers->end();
12050 ++it)
12051 {
12052 (*it)->i_commit();
12053
12054 /* look if this controller has a peer device */
12055 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12056 if (!peer)
12057 {
12058 /* no peer means the device is a newly created one;
12059 * create a peer owning data this device share it with */
12060 peer.createObject();
12061 peer->init(mPeer, *it, true /* aReshare */);
12062 }
12063 else
12064 {
12065 /* remove peer from the old list */
12066 mPeer->mUSBControllers->remove(peer);
12067 }
12068 /* and add it to the new list */
12069 newList->push_back(peer);
12070 }
12071
12072 /* uninit old peer's controllers that are left */
12073 for (USBControllerList::const_iterator
12074 it = mPeer->mUSBControllers->begin();
12075 it != mPeer->mUSBControllers->end();
12076 ++it)
12077 {
12078 (*it)->uninit();
12079 }
12080
12081 /* attach new list of controllers to our peer */
12082 mPeer->mUSBControllers.attach(newList);
12083 }
12084 else
12085 {
12086 /* we have no peer (our parent is the newly created machine);
12087 * just commit changes to devices */
12088 commitUSBControllers = true;
12089 }
12090 }
12091 else
12092 {
12093 /* the list of controllers itself is not changed,
12094 * just commit changes to controllers themselves */
12095 commitUSBControllers = true;
12096 }
12097
12098 if (commitUSBControllers)
12099 {
12100 for (USBControllerList::const_iterator
12101 it = mUSBControllers->begin();
12102 it != mUSBControllers->end();
12103 ++it)
12104 {
12105 (*it)->i_commit();
12106 }
12107 }
12108
12109 if (i_isSessionMachine())
12110 {
12111 /* attach new data to the primary machine and reshare it */
12112 mPeer->mUserData.attach(mUserData);
12113 mPeer->mHWData.attach(mHWData);
12114 /* mmMediumAttachments is reshared by fixupMedia */
12115 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12116 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12117 }
12118}
12119
12120/**
12121 * Copies all the hardware data from the given machine.
12122 *
12123 * Currently, only called when the VM is being restored from a snapshot. In
12124 * particular, this implies that the VM is not running during this method's
12125 * call.
12126 *
12127 * @note This method must be called from under this object's lock.
12128 *
12129 * @note This method doesn't call #i_commit(), so all data remains backed up and
12130 * unsaved.
12131 */
12132void Machine::i_copyFrom(Machine *aThat)
12133{
12134 AssertReturnVoid(!i_isSnapshotMachine());
12135 AssertReturnVoid(aThat->i_isSnapshotMachine());
12136
12137 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12138
12139 mHWData.assignCopy(aThat->mHWData);
12140
12141 // create copies of all shared folders (mHWData after attaching a copy
12142 // contains just references to original objects)
12143 for (HWData::SharedFolderList::iterator
12144 it = mHWData->mSharedFolders.begin();
12145 it != mHWData->mSharedFolders.end();
12146 ++it)
12147 {
12148 ComObjPtr<SharedFolder> folder;
12149 folder.createObject();
12150 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12151 AssertComRC(rc);
12152 *it = folder;
12153 }
12154
12155 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12156 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12157 mNvramStore->i_copyFrom(aThat->mNvramStore);
12158 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12159 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12160 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12161 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12162 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12163 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12164
12165 /* create private copies of all controllers */
12166 mStorageControllers.backup();
12167 mStorageControllers->clear();
12168 for (StorageControllerList::const_iterator
12169 it = aThat->mStorageControllers->begin();
12170 it != aThat->mStorageControllers->end();
12171 ++it)
12172 {
12173 ComObjPtr<StorageController> ctrl;
12174 ctrl.createObject();
12175 ctrl->initCopy(this, *it);
12176 mStorageControllers->push_back(ctrl);
12177 }
12178
12179 /* create private copies of all USB controllers */
12180 mUSBControllers.backup();
12181 mUSBControllers->clear();
12182 for (USBControllerList::const_iterator
12183 it = aThat->mUSBControllers->begin();
12184 it != aThat->mUSBControllers->end();
12185 ++it)
12186 {
12187 ComObjPtr<USBController> ctrl;
12188 ctrl.createObject();
12189 ctrl->initCopy(this, *it);
12190 mUSBControllers->push_back(ctrl);
12191 }
12192
12193 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12194 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12195 {
12196 if (mNetworkAdapters[slot].isNotNull())
12197 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12198 else
12199 {
12200 unconst(mNetworkAdapters[slot]).createObject();
12201 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12202 }
12203 }
12204 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12205 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12206 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12207 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12208}
12209
12210/**
12211 * Returns whether the given storage controller is hotplug capable.
12212 *
12213 * @returns true if the controller supports hotplugging
12214 * false otherwise.
12215 * @param enmCtrlType The controller type to check for.
12216 */
12217bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12218{
12219 ComPtr<ISystemProperties> systemProperties;
12220 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12221 if (FAILED(rc))
12222 return false;
12223
12224 BOOL aHotplugCapable = FALSE;
12225 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12226
12227 return RT_BOOL(aHotplugCapable);
12228}
12229
12230#ifdef VBOX_WITH_RESOURCE_USAGE_API
12231
12232void Machine::i_getDiskList(MediaList &list)
12233{
12234 for (MediumAttachmentList::const_iterator
12235 it = mMediumAttachments->begin();
12236 it != mMediumAttachments->end();
12237 ++it)
12238 {
12239 MediumAttachment *pAttach = *it;
12240 /* just in case */
12241 AssertContinue(pAttach);
12242
12243 AutoCaller localAutoCallerA(pAttach);
12244 if (FAILED(localAutoCallerA.rc())) continue;
12245
12246 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12247
12248 if (pAttach->i_getType() == DeviceType_HardDisk)
12249 list.push_back(pAttach->i_getMedium());
12250 }
12251}
12252
12253void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12254{
12255 AssertReturnVoid(isWriteLockOnCurrentThread());
12256 AssertPtrReturnVoid(aCollector);
12257
12258 pm::CollectorHAL *hal = aCollector->getHAL();
12259 /* Create sub metrics */
12260 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12261 "Percentage of processor time spent in user mode by the VM process.");
12262 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12263 "Percentage of processor time spent in kernel mode by the VM process.");
12264 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12265 "Size of resident portion of VM process in memory.");
12266 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12267 "Actual size of all VM disks combined.");
12268 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12269 "Network receive rate.");
12270 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12271 "Network transmit rate.");
12272 /* Create and register base metrics */
12273 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12274 cpuLoadUser, cpuLoadKernel);
12275 aCollector->registerBaseMetric(cpuLoad);
12276 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12277 ramUsageUsed);
12278 aCollector->registerBaseMetric(ramUsage);
12279 MediaList disks;
12280 i_getDiskList(disks);
12281 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12282 diskUsageUsed);
12283 aCollector->registerBaseMetric(diskUsage);
12284
12285 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12286 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12287 new pm::AggregateAvg()));
12288 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12289 new pm::AggregateMin()));
12290 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12291 new pm::AggregateMax()));
12292 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12293 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12294 new pm::AggregateAvg()));
12295 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12296 new pm::AggregateMin()));
12297 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12298 new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12301 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12302 new pm::AggregateAvg()));
12303 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12304 new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12306 new pm::AggregateMax()));
12307
12308 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12309 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12310 new pm::AggregateAvg()));
12311 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12312 new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12314 new pm::AggregateMax()));
12315
12316
12317 /* Guest metrics collector */
12318 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12319 aCollector->registerGuest(mCollectorGuest);
12320 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12321
12322 /* Create sub metrics */
12323 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12324 "Percentage of processor time spent in user mode as seen by the guest.");
12325 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12326 "Percentage of processor time spent in kernel mode as seen by the guest.");
12327 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12328 "Percentage of processor time spent idling as seen by the guest.");
12329
12330 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12331 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12332 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12333 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12334 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12335 pm::SubMetric *guestMemCache = new pm::SubMetric(
12336 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12337
12338 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12339 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12340
12341 /* Create and register base metrics */
12342 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12343 machineNetRx, machineNetTx);
12344 aCollector->registerBaseMetric(machineNetRate);
12345
12346 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12347 guestLoadUser, guestLoadKernel, guestLoadIdle);
12348 aCollector->registerBaseMetric(guestCpuLoad);
12349
12350 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12351 guestMemTotal, guestMemFree,
12352 guestMemBalloon, guestMemShared,
12353 guestMemCache, guestPagedTotal);
12354 aCollector->registerBaseMetric(guestCpuMem);
12355
12356 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12357 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12358 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12359 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12360
12361 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12362 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12363 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12364 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12365
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12370
12371 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12372 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12375
12376 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12377 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12380
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12385
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12390
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12395
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12400
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12402 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12404 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12405
12406 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12407 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12408 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12409 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12410}
12411
12412void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12413{
12414 AssertReturnVoid(isWriteLockOnCurrentThread());
12415
12416 if (aCollector)
12417 {
12418 aCollector->unregisterMetricsFor(aMachine);
12419 aCollector->unregisterBaseMetricsFor(aMachine);
12420 }
12421}
12422
12423#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12424
12425
12426////////////////////////////////////////////////////////////////////////////////
12427
12428DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12429
12430HRESULT SessionMachine::FinalConstruct()
12431{
12432 LogFlowThisFunc(("\n"));
12433
12434 mClientToken = NULL;
12435
12436 return BaseFinalConstruct();
12437}
12438
12439void SessionMachine::FinalRelease()
12440{
12441 LogFlowThisFunc(("\n"));
12442
12443 Assert(!mClientToken);
12444 /* paranoia, should not hang around any more */
12445 if (mClientToken)
12446 {
12447 delete mClientToken;
12448 mClientToken = NULL;
12449 }
12450
12451 uninit(Uninit::Unexpected);
12452
12453 BaseFinalRelease();
12454}
12455
12456/**
12457 * @note Must be called only by Machine::LockMachine() from its own write lock.
12458 */
12459HRESULT SessionMachine::init(Machine *aMachine)
12460{
12461 LogFlowThisFuncEnter();
12462 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12463
12464 AssertReturn(aMachine, E_INVALIDARG);
12465
12466 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12467
12468 /* Enclose the state transition NotReady->InInit->Ready */
12469 AutoInitSpan autoInitSpan(this);
12470 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12471
12472 HRESULT rc = S_OK;
12473
12474 RT_ZERO(mAuthLibCtx);
12475
12476 /* create the machine client token */
12477 try
12478 {
12479 mClientToken = new ClientToken(aMachine, this);
12480 if (!mClientToken->isReady())
12481 {
12482 delete mClientToken;
12483 mClientToken = NULL;
12484 rc = E_FAIL;
12485 }
12486 }
12487 catch (std::bad_alloc &)
12488 {
12489 rc = E_OUTOFMEMORY;
12490 }
12491 if (FAILED(rc))
12492 return rc;
12493
12494 /* memorize the peer Machine */
12495 unconst(mPeer) = aMachine;
12496 /* share the parent pointer */
12497 unconst(mParent) = aMachine->mParent;
12498
12499 /* take the pointers to data to share */
12500 mData.share(aMachine->mData);
12501 mSSData.share(aMachine->mSSData);
12502
12503 mUserData.share(aMachine->mUserData);
12504 mHWData.share(aMachine->mHWData);
12505 mMediumAttachments.share(aMachine->mMediumAttachments);
12506
12507 mStorageControllers.allocate();
12508 for (StorageControllerList::const_iterator
12509 it = aMachine->mStorageControllers->begin();
12510 it != aMachine->mStorageControllers->end();
12511 ++it)
12512 {
12513 ComObjPtr<StorageController> ctl;
12514 ctl.createObject();
12515 ctl->init(this, *it);
12516 mStorageControllers->push_back(ctl);
12517 }
12518
12519 mUSBControllers.allocate();
12520 for (USBControllerList::const_iterator
12521 it = aMachine->mUSBControllers->begin();
12522 it != aMachine->mUSBControllers->end();
12523 ++it)
12524 {
12525 ComObjPtr<USBController> ctl;
12526 ctl.createObject();
12527 ctl->init(this, *it);
12528 mUSBControllers->push_back(ctl);
12529 }
12530
12531 unconst(mBIOSSettings).createObject();
12532 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12533
12534 unconst(mTrustedPlatformModule).createObject();
12535 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12536
12537 unconst(mNvramStore).createObject();
12538 mNvramStore->init(this, aMachine->mNvramStore);
12539
12540 unconst(mRecordingSettings).createObject();
12541 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12542 /* create another GraphicsAdapter object that will be mutable */
12543 unconst(mGraphicsAdapter).createObject();
12544 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12545 /* create another VRDEServer object that will be mutable */
12546 unconst(mVRDEServer).createObject();
12547 mVRDEServer->init(this, aMachine->mVRDEServer);
12548 /* create another audio adapter object that will be mutable */
12549 unconst(mAudioAdapter).createObject();
12550 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12551 /* create a list of serial ports that will be mutable */
12552 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12553 {
12554 unconst(mSerialPorts[slot]).createObject();
12555 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12556 }
12557 /* create a list of parallel ports that will be mutable */
12558 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12559 {
12560 unconst(mParallelPorts[slot]).createObject();
12561 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12562 }
12563
12564 /* create another USB device filters object that will be mutable */
12565 unconst(mUSBDeviceFilters).createObject();
12566 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12567
12568 /* create a list of network adapters that will be mutable */
12569 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12570 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12571 {
12572 unconst(mNetworkAdapters[slot]).createObject();
12573 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12574 }
12575
12576 /* create another bandwidth control object that will be mutable */
12577 unconst(mBandwidthControl).createObject();
12578 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12579
12580 /* default is to delete saved state on Saved -> PoweredOff transition */
12581 mRemoveSavedState = true;
12582
12583 /* Confirm a successful initialization when it's the case */
12584 autoInitSpan.setSucceeded();
12585
12586 miNATNetworksStarted = 0;
12587
12588 LogFlowThisFuncLeave();
12589 return rc;
12590}
12591
12592/**
12593 * Uninitializes this session object. If the reason is other than
12594 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12595 * or the client watcher code.
12596 *
12597 * @param aReason uninitialization reason
12598 *
12599 * @note Locks mParent + this object for writing.
12600 */
12601void SessionMachine::uninit(Uninit::Reason aReason)
12602{
12603 LogFlowThisFuncEnter();
12604 LogFlowThisFunc(("reason=%d\n", aReason));
12605
12606 /*
12607 * Strongly reference ourselves to prevent this object deletion after
12608 * mData->mSession.mMachine.setNull() below (which can release the last
12609 * reference and call the destructor). Important: this must be done before
12610 * accessing any members (and before AutoUninitSpan that does it as well).
12611 * This self reference will be released as the very last step on return.
12612 */
12613 ComObjPtr<SessionMachine> selfRef;
12614 if (aReason != Uninit::Unexpected)
12615 selfRef = this;
12616
12617 /* Enclose the state transition Ready->InUninit->NotReady */
12618 AutoUninitSpan autoUninitSpan(this);
12619 if (autoUninitSpan.uninitDone())
12620 {
12621 LogFlowThisFunc(("Already uninitialized\n"));
12622 LogFlowThisFuncLeave();
12623 return;
12624 }
12625
12626 if (autoUninitSpan.initFailed())
12627 {
12628 /* We've been called by init() because it's failed. It's not really
12629 * necessary (nor it's safe) to perform the regular uninit sequence
12630 * below, the following is enough.
12631 */
12632 LogFlowThisFunc(("Initialization failed.\n"));
12633 /* destroy the machine client token */
12634 if (mClientToken)
12635 {
12636 delete mClientToken;
12637 mClientToken = NULL;
12638 }
12639 uninitDataAndChildObjects();
12640 mData.free();
12641 unconst(mParent) = NULL;
12642 unconst(mPeer) = NULL;
12643 LogFlowThisFuncLeave();
12644 return;
12645 }
12646
12647 MachineState_T lastState;
12648 {
12649 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12650 lastState = mData->mMachineState;
12651 }
12652 NOREF(lastState);
12653
12654#ifdef VBOX_WITH_USB
12655 // release all captured USB devices, but do this before requesting the locks below
12656 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12657 {
12658 /* Console::captureUSBDevices() is called in the VM process only after
12659 * setting the machine state to Starting or Restoring.
12660 * Console::detachAllUSBDevices() will be called upon successful
12661 * termination. So, we need to release USB devices only if there was
12662 * an abnormal termination of a running VM.
12663 *
12664 * This is identical to SessionMachine::DetachAllUSBDevices except
12665 * for the aAbnormal argument. */
12666 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12667 AssertComRC(rc);
12668 NOREF(rc);
12669
12670 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12671 if (service)
12672 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12673 }
12674#endif /* VBOX_WITH_USB */
12675
12676 // we need to lock this object in uninit() because the lock is shared
12677 // with mPeer (as well as data we modify below). mParent lock is needed
12678 // by several calls to it.
12679 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12680
12681#ifdef VBOX_WITH_RESOURCE_USAGE_API
12682 /*
12683 * It is safe to call Machine::i_unregisterMetrics() here because
12684 * PerformanceCollector::samplerCallback no longer accesses guest methods
12685 * holding the lock.
12686 */
12687 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12688 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12689 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12690 if (mCollectorGuest)
12691 {
12692 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12693 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12694 mCollectorGuest = NULL;
12695 }
12696#endif
12697
12698 if (aReason == Uninit::Abnormal)
12699 {
12700 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12701
12702 /*
12703 * Move the VM to the 'Aborted' machine state unless we are restoring a
12704 * VM that was in the 'Saved' machine state. In that case, if the VM
12705 * fails before reaching either the 'Restoring' machine state or the
12706 * 'Running' machine state then we set the machine state to
12707 * 'AbortedSaved' in order to preserve the saved state file so that the
12708 * VM can be restored in the future.
12709 */
12710 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12711 i_setMachineState(MachineState_AbortedSaved);
12712 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12713 i_setMachineState(MachineState_Aborted);
12714 }
12715
12716 // any machine settings modified?
12717 if (mData->flModifications)
12718 {
12719 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12720 i_rollback(false /* aNotify */);
12721 }
12722
12723 mData->mSession.mPID = NIL_RTPROCESS;
12724
12725 if (aReason == Uninit::Unexpected)
12726 {
12727 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12728 * client watcher thread to update the set of machines that have open
12729 * sessions. */
12730 mParent->i_updateClientWatcher();
12731 }
12732
12733 /* uninitialize all remote controls */
12734 if (mData->mSession.mRemoteControls.size())
12735 {
12736 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12737 mData->mSession.mRemoteControls.size()));
12738
12739 /* Always restart a the beginning, since the iterator is invalidated
12740 * by using erase(). */
12741 for (Data::Session::RemoteControlList::iterator
12742 it = mData->mSession.mRemoteControls.begin();
12743 it != mData->mSession.mRemoteControls.end();
12744 it = mData->mSession.mRemoteControls.begin())
12745 {
12746 ComPtr<IInternalSessionControl> pControl = *it;
12747 mData->mSession.mRemoteControls.erase(it);
12748 multilock.release();
12749 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12750 HRESULT rc = pControl->Uninitialize();
12751 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12752 if (FAILED(rc))
12753 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12754 multilock.acquire();
12755 }
12756 mData->mSession.mRemoteControls.clear();
12757 }
12758
12759 /* Remove all references to the NAT network service. The service will stop
12760 * if all references (also from other VMs) are removed. */
12761 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12762 {
12763 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12764 {
12765 BOOL enabled;
12766 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12767 if ( FAILED(hrc)
12768 || !enabled)
12769 continue;
12770
12771 NetworkAttachmentType_T type;
12772 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12773 if ( SUCCEEDED(hrc)
12774 && type == NetworkAttachmentType_NATNetwork)
12775 {
12776 Bstr name;
12777 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12778 if (SUCCEEDED(hrc))
12779 {
12780 multilock.release();
12781 Utf8Str strName(name);
12782 LogRel(("VM '%s' stops using NAT network '%s'\n",
12783 mUserData->s.strName.c_str(), strName.c_str()));
12784 mParent->i_natNetworkRefDec(strName);
12785 multilock.acquire();
12786 }
12787 }
12788 }
12789 }
12790
12791 /*
12792 * An expected uninitialization can come only from #i_checkForDeath().
12793 * Otherwise it means that something's gone really wrong (for example,
12794 * the Session implementation has released the VirtualBox reference
12795 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12796 * etc). However, it's also possible, that the client releases the IPC
12797 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12798 * but the VirtualBox release event comes first to the server process.
12799 * This case is practically possible, so we should not assert on an
12800 * unexpected uninit, just log a warning.
12801 */
12802
12803 if (aReason == Uninit::Unexpected)
12804 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12805
12806 if (aReason != Uninit::Normal)
12807 {
12808 mData->mSession.mDirectControl.setNull();
12809 }
12810 else
12811 {
12812 /* this must be null here (see #OnSessionEnd()) */
12813 Assert(mData->mSession.mDirectControl.isNull());
12814 Assert(mData->mSession.mState == SessionState_Unlocking);
12815 Assert(!mData->mSession.mProgress.isNull());
12816 }
12817 if (mData->mSession.mProgress)
12818 {
12819 if (aReason == Uninit::Normal)
12820 mData->mSession.mProgress->i_notifyComplete(S_OK);
12821 else
12822 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12823 COM_IIDOF(ISession),
12824 getComponentName(),
12825 tr("The VM session was aborted"));
12826 mData->mSession.mProgress.setNull();
12827 }
12828
12829 if (mConsoleTaskData.mProgress)
12830 {
12831 Assert(aReason == Uninit::Abnormal);
12832 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12833 COM_IIDOF(ISession),
12834 getComponentName(),
12835 tr("The VM session was aborted"));
12836 mConsoleTaskData.mProgress.setNull();
12837 }
12838
12839 /* remove the association between the peer machine and this session machine */
12840 Assert( (SessionMachine*)mData->mSession.mMachine == this
12841 || aReason == Uninit::Unexpected);
12842
12843 /* reset the rest of session data */
12844 mData->mSession.mLockType = LockType_Null;
12845 mData->mSession.mMachine.setNull();
12846 mData->mSession.mState = SessionState_Unlocked;
12847 mData->mSession.mName.setNull();
12848
12849 /* destroy the machine client token before leaving the exclusive lock */
12850 if (mClientToken)
12851 {
12852 delete mClientToken;
12853 mClientToken = NULL;
12854 }
12855
12856 /* fire an event */
12857 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12858
12859 uninitDataAndChildObjects();
12860
12861 /* free the essential data structure last */
12862 mData.free();
12863
12864 /* release the exclusive lock before setting the below two to NULL */
12865 multilock.release();
12866
12867 unconst(mParent) = NULL;
12868 unconst(mPeer) = NULL;
12869
12870 AuthLibUnload(&mAuthLibCtx);
12871
12872 LogFlowThisFuncLeave();
12873}
12874
12875// util::Lockable interface
12876////////////////////////////////////////////////////////////////////////////////
12877
12878/**
12879 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12880 * with the primary Machine instance (mPeer).
12881 */
12882RWLockHandle *SessionMachine::lockHandle() const
12883{
12884 AssertReturn(mPeer != NULL, NULL);
12885 return mPeer->lockHandle();
12886}
12887
12888// IInternalMachineControl methods
12889////////////////////////////////////////////////////////////////////////////////
12890
12891/**
12892 * Passes collected guest statistics to performance collector object
12893 */
12894HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12895 ULONG aCpuKernel, ULONG aCpuIdle,
12896 ULONG aMemTotal, ULONG aMemFree,
12897 ULONG aMemBalloon, ULONG aMemShared,
12898 ULONG aMemCache, ULONG aPageTotal,
12899 ULONG aAllocVMM, ULONG aFreeVMM,
12900 ULONG aBalloonedVMM, ULONG aSharedVMM,
12901 ULONG aVmNetRx, ULONG aVmNetTx)
12902{
12903#ifdef VBOX_WITH_RESOURCE_USAGE_API
12904 if (mCollectorGuest)
12905 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12906 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12907 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12908 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12909
12910 return S_OK;
12911#else
12912 NOREF(aValidStats);
12913 NOREF(aCpuUser);
12914 NOREF(aCpuKernel);
12915 NOREF(aCpuIdle);
12916 NOREF(aMemTotal);
12917 NOREF(aMemFree);
12918 NOREF(aMemBalloon);
12919 NOREF(aMemShared);
12920 NOREF(aMemCache);
12921 NOREF(aPageTotal);
12922 NOREF(aAllocVMM);
12923 NOREF(aFreeVMM);
12924 NOREF(aBalloonedVMM);
12925 NOREF(aSharedVMM);
12926 NOREF(aVmNetRx);
12927 NOREF(aVmNetTx);
12928 return E_NOTIMPL;
12929#endif
12930}
12931
12932////////////////////////////////////////////////////////////////////////////////
12933//
12934// SessionMachine task records
12935//
12936////////////////////////////////////////////////////////////////////////////////
12937
12938/**
12939 * Task record for saving the machine state.
12940 */
12941class SessionMachine::SaveStateTask
12942 : public Machine::Task
12943{
12944public:
12945 SaveStateTask(SessionMachine *m,
12946 Progress *p,
12947 const Utf8Str &t,
12948 Reason_T enmReason,
12949 const Utf8Str &strStateFilePath)
12950 : Task(m, p, t),
12951 m_enmReason(enmReason),
12952 m_strStateFilePath(strStateFilePath)
12953 {}
12954
12955private:
12956 void handler()
12957 {
12958 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12959 }
12960
12961 Reason_T m_enmReason;
12962 Utf8Str m_strStateFilePath;
12963
12964 friend class SessionMachine;
12965};
12966
12967/**
12968 * Task thread implementation for SessionMachine::SaveState(), called from
12969 * SessionMachine::taskHandler().
12970 *
12971 * @note Locks this object for writing.
12972 *
12973 * @param task
12974 * @return
12975 */
12976void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12977{
12978 LogFlowThisFuncEnter();
12979
12980 AutoCaller autoCaller(this);
12981 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12982 if (FAILED(autoCaller.rc()))
12983 {
12984 /* we might have been uninitialized because the session was accidentally
12985 * closed by the client, so don't assert */
12986 HRESULT rc = setError(E_FAIL,
12987 tr("The session has been accidentally closed"));
12988 task.m_pProgress->i_notifyComplete(rc);
12989 LogFlowThisFuncLeave();
12990 return;
12991 }
12992
12993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12994
12995 HRESULT rc = S_OK;
12996
12997 try
12998 {
12999 ComPtr<IInternalSessionControl> directControl;
13000 if (mData->mSession.mLockType == LockType_VM)
13001 directControl = mData->mSession.mDirectControl;
13002 if (directControl.isNull())
13003 throw setError(VBOX_E_INVALID_VM_STATE,
13004 tr("Trying to save state without a running VM"));
13005 alock.release();
13006 BOOL fSuspendedBySave;
13007 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13008 Assert(!fSuspendedBySave);
13009 alock.acquire();
13010
13011 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13012 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13013 throw E_FAIL);
13014
13015 if (SUCCEEDED(rc))
13016 {
13017 mSSData->strStateFilePath = task.m_strStateFilePath;
13018
13019 /* save all VM settings */
13020 rc = i_saveSettings(NULL, alock);
13021 // no need to check whether VirtualBox.xml needs saving also since
13022 // we can't have a name change pending at this point
13023 }
13024 else
13025 {
13026 // On failure, set the state to the state we had at the beginning.
13027 i_setMachineState(task.m_machineStateBackup);
13028 i_updateMachineStateOnClient();
13029
13030 // Delete the saved state file (might have been already created).
13031 // No need to check whether this is shared with a snapshot here
13032 // because we certainly created a fresh saved state file here.
13033 RTFileDelete(task.m_strStateFilePath.c_str());
13034 }
13035 }
13036 catch (HRESULT aRC) { rc = aRC; }
13037
13038 task.m_pProgress->i_notifyComplete(rc);
13039
13040 LogFlowThisFuncLeave();
13041}
13042
13043/**
13044 * @note Locks this object for writing.
13045 */
13046HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13047{
13048 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13049}
13050
13051HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13052{
13053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13054
13055 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13056 if (FAILED(rc)) return rc;
13057
13058 if ( mData->mMachineState != MachineState_Running
13059 && mData->mMachineState != MachineState_Paused
13060 )
13061 return setError(VBOX_E_INVALID_VM_STATE,
13062 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13063 Global::stringifyMachineState(mData->mMachineState));
13064
13065 ComObjPtr<Progress> pProgress;
13066 pProgress.createObject();
13067 rc = pProgress->init(i_getVirtualBox(),
13068 static_cast<IMachine *>(this) /* aInitiator */,
13069 tr("Saving the execution state of the virtual machine"),
13070 FALSE /* aCancelable */);
13071 if (FAILED(rc))
13072 return rc;
13073
13074 Utf8Str strStateFilePath;
13075 i_composeSavedStateFilename(strStateFilePath);
13076
13077 /* create and start the task on a separate thread (note that it will not
13078 * start working until we release alock) */
13079 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13080 rc = pTask->createThread();
13081 if (FAILED(rc))
13082 return rc;
13083
13084 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13085 i_setMachineState(MachineState_Saving);
13086 i_updateMachineStateOnClient();
13087
13088 pProgress.queryInterfaceTo(aProgress.asOutParam());
13089
13090 return S_OK;
13091}
13092
13093/**
13094 * @note Locks this object for writing.
13095 */
13096HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13097{
13098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13099
13100 HRESULT rc = i_checkStateDependency(MutableStateDep);
13101 if (FAILED(rc)) return rc;
13102
13103 if ( mData->mMachineState != MachineState_PoweredOff
13104 && mData->mMachineState != MachineState_Teleported
13105 && mData->mMachineState != MachineState_Aborted
13106 )
13107 return setError(VBOX_E_INVALID_VM_STATE,
13108 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13109 Global::stringifyMachineState(mData->mMachineState));
13110
13111 com::Utf8Str stateFilePathFull;
13112 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13113 if (RT_FAILURE(vrc))
13114 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13115 tr("Invalid saved state file path '%s' (%Rrc)"),
13116 aSavedStateFile.c_str(),
13117 vrc);
13118
13119 mSSData->strStateFilePath = stateFilePathFull;
13120
13121 /* The below i_setMachineState() will detect the state transition and will
13122 * update the settings file */
13123
13124 return i_setMachineState(MachineState_Saved);
13125}
13126
13127/**
13128 * @note Locks this object for writing.
13129 */
13130HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13131{
13132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13133
13134 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13135 if (FAILED(rc)) return rc;
13136
13137 if ( mData->mMachineState != MachineState_Saved
13138 && mData->mMachineState != MachineState_AbortedSaved)
13139 return setError(VBOX_E_INVALID_VM_STATE,
13140 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13141 Global::stringifyMachineState(mData->mMachineState));
13142
13143 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13144
13145 /*
13146 * Saved -> PoweredOff transition will be detected in the SessionMachine
13147 * and properly handled.
13148 */
13149 rc = i_setMachineState(MachineState_PoweredOff);
13150 return rc;
13151}
13152
13153
13154/**
13155 * @note Locks the same as #i_setMachineState() does.
13156 */
13157HRESULT SessionMachine::updateState(MachineState_T aState)
13158{
13159 return i_setMachineState(aState);
13160}
13161
13162/**
13163 * @note Locks this object for writing.
13164 */
13165HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13166{
13167 IProgress *pProgress(aProgress);
13168
13169 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13170
13171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13172
13173 if (mData->mSession.mState != SessionState_Locked)
13174 return VBOX_E_INVALID_OBJECT_STATE;
13175
13176 if (!mData->mSession.mProgress.isNull())
13177 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13178
13179 /* If we didn't reference the NAT network service yet, add a reference to
13180 * force a start */
13181 if (miNATNetworksStarted < 1)
13182 {
13183 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13184 {
13185 BOOL enabled;
13186 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13187 if ( FAILED(hrc)
13188 || !enabled)
13189 continue;
13190
13191 NetworkAttachmentType_T type;
13192 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13193 if ( SUCCEEDED(hrc)
13194 && type == NetworkAttachmentType_NATNetwork)
13195 {
13196 Bstr name;
13197 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13198 if (SUCCEEDED(hrc))
13199 {
13200 Utf8Str strName(name);
13201 LogRel(("VM '%s' starts using NAT network '%s'\n",
13202 mUserData->s.strName.c_str(), strName.c_str()));
13203 mPeer->lockHandle()->unlockWrite();
13204 mParent->i_natNetworkRefInc(strName);
13205#ifdef RT_LOCK_STRICT
13206 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13207#else
13208 mPeer->lockHandle()->lockWrite();
13209#endif
13210 }
13211 }
13212 }
13213 miNATNetworksStarted++;
13214 }
13215
13216 LogFlowThisFunc(("returns S_OK.\n"));
13217 return S_OK;
13218}
13219
13220/**
13221 * @note Locks this object for writing.
13222 */
13223HRESULT SessionMachine::endPowerUp(LONG aResult)
13224{
13225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13226
13227 if (mData->mSession.mState != SessionState_Locked)
13228 return VBOX_E_INVALID_OBJECT_STATE;
13229
13230 /* Finalize the LaunchVMProcess progress object. */
13231 if (mData->mSession.mProgress)
13232 {
13233 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13234 mData->mSession.mProgress.setNull();
13235 }
13236
13237 if (SUCCEEDED((HRESULT)aResult))
13238 {
13239#ifdef VBOX_WITH_RESOURCE_USAGE_API
13240 /* The VM has been powered up successfully, so it makes sense
13241 * now to offer the performance metrics for a running machine
13242 * object. Doing it earlier wouldn't be safe. */
13243 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13244 mData->mSession.mPID);
13245#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13246 }
13247
13248 return S_OK;
13249}
13250
13251/**
13252 * @note Locks this object for writing.
13253 */
13254HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13255{
13256 LogFlowThisFuncEnter();
13257
13258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13259
13260 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13261 E_FAIL);
13262
13263 /* create a progress object to track operation completion */
13264 ComObjPtr<Progress> pProgress;
13265 pProgress.createObject();
13266 pProgress->init(i_getVirtualBox(),
13267 static_cast<IMachine *>(this) /* aInitiator */,
13268 tr("Stopping the virtual machine"),
13269 FALSE /* aCancelable */);
13270
13271 /* fill in the console task data */
13272 mConsoleTaskData.mLastState = mData->mMachineState;
13273 mConsoleTaskData.mProgress = pProgress;
13274
13275 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13276 i_setMachineState(MachineState_Stopping);
13277
13278 pProgress.queryInterfaceTo(aProgress.asOutParam());
13279
13280 return S_OK;
13281}
13282
13283/**
13284 * @note Locks this object for writing.
13285 */
13286HRESULT SessionMachine::endPoweringDown(LONG aResult,
13287 const com::Utf8Str &aErrMsg)
13288{
13289 HRESULT const hrcResult = (HRESULT)aResult;
13290 LogFlowThisFuncEnter();
13291
13292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13293
13294 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13295 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13296 && mConsoleTaskData.mLastState != MachineState_Null,
13297 E_FAIL);
13298
13299 /*
13300 * On failure, set the state to the state we had when BeginPoweringDown()
13301 * was called (this is expected by Console::PowerDown() and the associated
13302 * task). On success the VM process already changed the state to
13303 * MachineState_PoweredOff, so no need to do anything.
13304 */
13305 if (FAILED(hrcResult))
13306 i_setMachineState(mConsoleTaskData.mLastState);
13307
13308 /* notify the progress object about operation completion */
13309 Assert(mConsoleTaskData.mProgress);
13310 if (SUCCEEDED(hrcResult))
13311 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13312 else
13313 {
13314 if (aErrMsg.length())
13315 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13316 COM_IIDOF(ISession),
13317 getComponentName(),
13318 aErrMsg.c_str());
13319 else
13320 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13321 }
13322
13323 /* clear out the temporary saved state data */
13324 mConsoleTaskData.mLastState = MachineState_Null;
13325 mConsoleTaskData.mProgress.setNull();
13326
13327 LogFlowThisFuncLeave();
13328 return S_OK;
13329}
13330
13331
13332/**
13333 * Goes through the USB filters of the given machine to see if the given
13334 * device matches any filter or not.
13335 *
13336 * @note Locks the same as USBController::hasMatchingFilter() does.
13337 */
13338HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13339 BOOL *aMatched,
13340 ULONG *aMaskedInterfaces)
13341{
13342 LogFlowThisFunc(("\n"));
13343
13344#ifdef VBOX_WITH_USB
13345 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13346#else
13347 NOREF(aDevice);
13348 NOREF(aMaskedInterfaces);
13349 *aMatched = FALSE;
13350#endif
13351
13352 return S_OK;
13353}
13354
13355/**
13356 * @note Locks the same as Host::captureUSBDevice() does.
13357 */
13358HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13359{
13360 LogFlowThisFunc(("\n"));
13361
13362#ifdef VBOX_WITH_USB
13363 /* if captureDeviceForVM() fails, it must have set extended error info */
13364 clearError();
13365 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13366 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13367 return rc;
13368
13369 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13370 AssertReturn(service, E_FAIL);
13371 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13372#else
13373 RT_NOREF(aId, aCaptureFilename);
13374 return E_NOTIMPL;
13375#endif
13376}
13377
13378/**
13379 * @note Locks the same as Host::detachUSBDevice() does.
13380 */
13381HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13382 BOOL aDone)
13383{
13384 LogFlowThisFunc(("\n"));
13385
13386#ifdef VBOX_WITH_USB
13387 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13388 AssertReturn(service, E_FAIL);
13389 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13390#else
13391 NOREF(aId);
13392 NOREF(aDone);
13393 return E_NOTIMPL;
13394#endif
13395}
13396
13397/**
13398 * Inserts all machine filters to the USB proxy service and then calls
13399 * Host::autoCaptureUSBDevices().
13400 *
13401 * Called by Console from the VM process upon VM startup.
13402 *
13403 * @note Locks what called methods lock.
13404 */
13405HRESULT SessionMachine::autoCaptureUSBDevices()
13406{
13407 LogFlowThisFunc(("\n"));
13408
13409#ifdef VBOX_WITH_USB
13410 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13411 AssertComRC(rc);
13412 NOREF(rc);
13413
13414 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13415 AssertReturn(service, E_FAIL);
13416 return service->autoCaptureDevicesForVM(this);
13417#else
13418 return S_OK;
13419#endif
13420}
13421
13422/**
13423 * Removes all machine filters from the USB proxy service and then calls
13424 * Host::detachAllUSBDevices().
13425 *
13426 * Called by Console from the VM process upon normal VM termination or by
13427 * SessionMachine::uninit() upon abnormal VM termination (from under the
13428 * Machine/SessionMachine lock).
13429 *
13430 * @note Locks what called methods lock.
13431 */
13432HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13433{
13434 LogFlowThisFunc(("\n"));
13435
13436#ifdef VBOX_WITH_USB
13437 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13438 AssertComRC(rc);
13439 NOREF(rc);
13440
13441 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13442 AssertReturn(service, E_FAIL);
13443 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13444#else
13445 NOREF(aDone);
13446 return S_OK;
13447#endif
13448}
13449
13450/**
13451 * @note Locks this object for writing.
13452 */
13453HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13454 ComPtr<IProgress> &aProgress)
13455{
13456 LogFlowThisFuncEnter();
13457
13458 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13459 /*
13460 * We don't assert below because it might happen that a non-direct session
13461 * informs us it is closed right after we've been uninitialized -- it's ok.
13462 */
13463
13464 /* get IInternalSessionControl interface */
13465 ComPtr<IInternalSessionControl> control(aSession);
13466
13467 ComAssertRet(!control.isNull(), E_INVALIDARG);
13468
13469 /* Creating a Progress object requires the VirtualBox lock, and
13470 * thus locking it here is required by the lock order rules. */
13471 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13472
13473 if (control == mData->mSession.mDirectControl)
13474 {
13475 /* The direct session is being normally closed by the client process
13476 * ----------------------------------------------------------------- */
13477
13478 /* go to the closing state (essential for all open*Session() calls and
13479 * for #i_checkForDeath()) */
13480 Assert(mData->mSession.mState == SessionState_Locked);
13481 mData->mSession.mState = SessionState_Unlocking;
13482
13483 /* set direct control to NULL to release the remote instance */
13484 mData->mSession.mDirectControl.setNull();
13485 LogFlowThisFunc(("Direct control is set to NULL\n"));
13486
13487 if (mData->mSession.mProgress)
13488 {
13489 /* finalize the progress, someone might wait if a frontend
13490 * closes the session before powering on the VM. */
13491 mData->mSession.mProgress->notifyComplete(E_FAIL,
13492 COM_IIDOF(ISession),
13493 getComponentName(),
13494 tr("The VM session was closed before any attempt to power it on"));
13495 mData->mSession.mProgress.setNull();
13496 }
13497
13498 /* Create the progress object the client will use to wait until
13499 * #i_checkForDeath() is called to uninitialize this session object after
13500 * it releases the IPC semaphore.
13501 * Note! Because we're "reusing" mProgress here, this must be a proxy
13502 * object just like for LaunchVMProcess. */
13503 Assert(mData->mSession.mProgress.isNull());
13504 ComObjPtr<ProgressProxy> progress;
13505 progress.createObject();
13506 ComPtr<IUnknown> pPeer(mPeer);
13507 progress->init(mParent, pPeer,
13508 Bstr(tr("Closing session")).raw(),
13509 FALSE /* aCancelable */);
13510 progress.queryInterfaceTo(aProgress.asOutParam());
13511 mData->mSession.mProgress = progress;
13512 }
13513 else
13514 {
13515 /* the remote session is being normally closed */
13516 bool found = false;
13517 for (Data::Session::RemoteControlList::iterator
13518 it = mData->mSession.mRemoteControls.begin();
13519 it != mData->mSession.mRemoteControls.end();
13520 ++it)
13521 {
13522 if (control == *it)
13523 {
13524 found = true;
13525 // This MUST be erase(it), not remove(*it) as the latter
13526 // triggers a very nasty use after free due to the place where
13527 // the value "lives".
13528 mData->mSession.mRemoteControls.erase(it);
13529 break;
13530 }
13531 }
13532 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13533 E_INVALIDARG);
13534 }
13535
13536 /* signal the client watcher thread, because the client is going away */
13537 mParent->i_updateClientWatcher();
13538
13539 LogFlowThisFuncLeave();
13540 return S_OK;
13541}
13542
13543HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13544 std::vector<com::Utf8Str> &aValues,
13545 std::vector<LONG64> &aTimestamps,
13546 std::vector<com::Utf8Str> &aFlags)
13547{
13548 LogFlowThisFunc(("\n"));
13549
13550#ifdef VBOX_WITH_GUEST_PROPS
13551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13552
13553 size_t cEntries = mHWData->mGuestProperties.size();
13554 aNames.resize(cEntries);
13555 aValues.resize(cEntries);
13556 aTimestamps.resize(cEntries);
13557 aFlags.resize(cEntries);
13558
13559 size_t i = 0;
13560 for (HWData::GuestPropertyMap::const_iterator
13561 it = mHWData->mGuestProperties.begin();
13562 it != mHWData->mGuestProperties.end();
13563 ++it, ++i)
13564 {
13565 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13566 aNames[i] = it->first;
13567 aValues[i] = it->second.strValue;
13568 aTimestamps[i] = it->second.mTimestamp;
13569
13570 /* If it is NULL, keep it NULL. */
13571 if (it->second.mFlags)
13572 {
13573 GuestPropWriteFlags(it->second.mFlags, szFlags);
13574 aFlags[i] = szFlags;
13575 }
13576 else
13577 aFlags[i] = "";
13578 }
13579 return S_OK;
13580#else
13581 ReturnComNotImplemented();
13582#endif
13583}
13584
13585HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13586 const com::Utf8Str &aValue,
13587 LONG64 aTimestamp,
13588 const com::Utf8Str &aFlags)
13589{
13590 LogFlowThisFunc(("\n"));
13591
13592#ifdef VBOX_WITH_GUEST_PROPS
13593 try
13594 {
13595 /*
13596 * Convert input up front.
13597 */
13598 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13599 if (aFlags.length())
13600 {
13601 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13602 AssertRCReturn(vrc, E_INVALIDARG);
13603 }
13604
13605 /*
13606 * Now grab the object lock, validate the state and do the update.
13607 */
13608
13609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13610
13611 if (!Global::IsOnline(mData->mMachineState))
13612 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
13613
13614 i_setModified(IsModified_MachineData);
13615 mHWData.backup();
13616
13617 bool fDelete = !aValue.length();
13618 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13619 if (it != mHWData->mGuestProperties.end())
13620 {
13621 if (!fDelete)
13622 {
13623 it->second.strValue = aValue;
13624 it->second.mTimestamp = aTimestamp;
13625 it->second.mFlags = fFlags;
13626 }
13627 else
13628 mHWData->mGuestProperties.erase(it);
13629
13630 mData->mGuestPropertiesModified = TRUE;
13631 }
13632 else if (!fDelete)
13633 {
13634 HWData::GuestProperty prop;
13635 prop.strValue = aValue;
13636 prop.mTimestamp = aTimestamp;
13637 prop.mFlags = fFlags;
13638
13639 mHWData->mGuestProperties[aName] = prop;
13640 mData->mGuestPropertiesModified = TRUE;
13641 }
13642
13643 alock.release();
13644
13645 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13646 }
13647 catch (...)
13648 {
13649 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13650 }
13651 return S_OK;
13652#else
13653 ReturnComNotImplemented();
13654#endif
13655}
13656
13657
13658HRESULT SessionMachine::lockMedia()
13659{
13660 AutoMultiWriteLock2 alock(this->lockHandle(),
13661 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13662
13663 AssertReturn( mData->mMachineState == MachineState_Starting
13664 || mData->mMachineState == MachineState_Restoring
13665 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13666
13667 clearError();
13668 alock.release();
13669 return i_lockMedia();
13670}
13671
13672HRESULT SessionMachine::unlockMedia()
13673{
13674 HRESULT hrc = i_unlockMedia();
13675 return hrc;
13676}
13677
13678HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13679 ComPtr<IMediumAttachment> &aNewAttachment)
13680{
13681 // request the host lock first, since might be calling Host methods for getting host drives;
13682 // next, protect the media tree all the while we're in here, as well as our member variables
13683 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13684 this->lockHandle(),
13685 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13686
13687 IMediumAttachment *iAttach = aAttachment;
13688 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13689
13690 Utf8Str ctrlName;
13691 LONG lPort;
13692 LONG lDevice;
13693 bool fTempEject;
13694 {
13695 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13696
13697 /* Need to query the details first, as the IMediumAttachment reference
13698 * might be to the original settings, which we are going to change. */
13699 ctrlName = pAttach->i_getControllerName();
13700 lPort = pAttach->i_getPort();
13701 lDevice = pAttach->i_getDevice();
13702 fTempEject = pAttach->i_getTempEject();
13703 }
13704
13705 if (!fTempEject)
13706 {
13707 /* Remember previously mounted medium. The medium before taking the
13708 * backup is not necessarily the same thing. */
13709 ComObjPtr<Medium> oldmedium;
13710 oldmedium = pAttach->i_getMedium();
13711
13712 i_setModified(IsModified_Storage);
13713 mMediumAttachments.backup();
13714
13715 // The backup operation makes the pAttach reference point to the
13716 // old settings. Re-get the correct reference.
13717 pAttach = i_findAttachment(*mMediumAttachments.data(),
13718 ctrlName,
13719 lPort,
13720 lDevice);
13721
13722 {
13723 AutoCaller autoAttachCaller(this);
13724 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13725
13726 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13727 if (!oldmedium.isNull())
13728 oldmedium->i_removeBackReference(mData->mUuid);
13729
13730 pAttach->i_updateMedium(NULL);
13731 pAttach->i_updateEjected();
13732 }
13733
13734 i_setModified(IsModified_Storage);
13735 }
13736 else
13737 {
13738 {
13739 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13740 pAttach->i_updateEjected();
13741 }
13742 }
13743
13744 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13745
13746 return S_OK;
13747}
13748
13749HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13750 com::Utf8Str &aResult)
13751{
13752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13753
13754 HRESULT hr = S_OK;
13755
13756 if (!mAuthLibCtx.hAuthLibrary)
13757 {
13758 /* Load the external authentication library. */
13759 Bstr authLibrary;
13760 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13761
13762 Utf8Str filename = authLibrary;
13763
13764 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13765 if (RT_FAILURE(vrc))
13766 hr = setErrorBoth(E_FAIL, vrc,
13767 tr("Could not load the external authentication library '%s' (%Rrc)"),
13768 filename.c_str(), vrc);
13769 }
13770
13771 /* The auth library might need the machine lock. */
13772 alock.release();
13773
13774 if (FAILED(hr))
13775 return hr;
13776
13777 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13778 {
13779 enum VRDEAuthParams
13780 {
13781 parmUuid = 1,
13782 parmGuestJudgement,
13783 parmUser,
13784 parmPassword,
13785 parmDomain,
13786 parmClientId
13787 };
13788
13789 AuthResult result = AuthResultAccessDenied;
13790
13791 Guid uuid(aAuthParams[parmUuid]);
13792 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13793 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13794
13795 result = AuthLibAuthenticate(&mAuthLibCtx,
13796 uuid.raw(), guestJudgement,
13797 aAuthParams[parmUser].c_str(),
13798 aAuthParams[parmPassword].c_str(),
13799 aAuthParams[parmDomain].c_str(),
13800 u32ClientId);
13801
13802 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13803 size_t cbPassword = aAuthParams[parmPassword].length();
13804 if (cbPassword)
13805 {
13806 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13807 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13808 }
13809
13810 if (result == AuthResultAccessGranted)
13811 aResult = "granted";
13812 else
13813 aResult = "denied";
13814
13815 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13816 aAuthParams[parmUser].c_str(), aResult.c_str()));
13817 }
13818 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13819 {
13820 enum VRDEAuthDisconnectParams
13821 {
13822 parmUuid = 1,
13823 parmClientId
13824 };
13825
13826 Guid uuid(aAuthParams[parmUuid]);
13827 uint32_t u32ClientId = 0;
13828 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13829 }
13830 else
13831 {
13832 hr = E_INVALIDARG;
13833 }
13834
13835 return hr;
13836}
13837
13838// public methods only for internal purposes
13839/////////////////////////////////////////////////////////////////////////////
13840
13841#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13842/**
13843 * Called from the client watcher thread to check for expected or unexpected
13844 * death of the client process that has a direct session to this machine.
13845 *
13846 * On Win32 and on OS/2, this method is called only when we've got the
13847 * mutex (i.e. the client has either died or terminated normally) so it always
13848 * returns @c true (the client is terminated, the session machine is
13849 * uninitialized).
13850 *
13851 * On other platforms, the method returns @c true if the client process has
13852 * terminated normally or abnormally and the session machine was uninitialized,
13853 * and @c false if the client process is still alive.
13854 *
13855 * @note Locks this object for writing.
13856 */
13857bool SessionMachine::i_checkForDeath()
13858{
13859 Uninit::Reason reason;
13860 bool terminated = false;
13861
13862 /* Enclose autoCaller with a block because calling uninit() from under it
13863 * will deadlock. */
13864 {
13865 AutoCaller autoCaller(this);
13866 if (!autoCaller.isOk())
13867 {
13868 /* return true if not ready, to cause the client watcher to exclude
13869 * the corresponding session from watching */
13870 LogFlowThisFunc(("Already uninitialized!\n"));
13871 return true;
13872 }
13873
13874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13875
13876 /* Determine the reason of death: if the session state is Closing here,
13877 * everything is fine. Otherwise it means that the client did not call
13878 * OnSessionEnd() before it released the IPC semaphore. This may happen
13879 * either because the client process has abnormally terminated, or
13880 * because it simply forgot to call ISession::Close() before exiting. We
13881 * threat the latter also as an abnormal termination (see
13882 * Session::uninit() for details). */
13883 reason = mData->mSession.mState == SessionState_Unlocking ?
13884 Uninit::Normal :
13885 Uninit::Abnormal;
13886
13887 if (mClientToken)
13888 terminated = mClientToken->release();
13889 } /* AutoCaller block */
13890
13891 if (terminated)
13892 uninit(reason);
13893
13894 return terminated;
13895}
13896
13897void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13898{
13899 LogFlowThisFunc(("\n"));
13900
13901 strTokenId.setNull();
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturnVoid(autoCaller.rc());
13905
13906 Assert(mClientToken);
13907 if (mClientToken)
13908 mClientToken->getId(strTokenId);
13909}
13910#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13911IToken *SessionMachine::i_getToken()
13912{
13913 LogFlowThisFunc(("\n"));
13914
13915 AutoCaller autoCaller(this);
13916 AssertComRCReturn(autoCaller.rc(), NULL);
13917
13918 Assert(mClientToken);
13919 if (mClientToken)
13920 return mClientToken->getToken();
13921 else
13922 return NULL;
13923}
13924#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13925
13926Machine::ClientToken *SessionMachine::i_getClientToken()
13927{
13928 LogFlowThisFunc(("\n"));
13929
13930 AutoCaller autoCaller(this);
13931 AssertComRCReturn(autoCaller.rc(), NULL);
13932
13933 return mClientToken;
13934}
13935
13936
13937/**
13938 * @note Locks this object for reading.
13939 */
13940HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13941{
13942 LogFlowThisFunc(("\n"));
13943
13944 AutoCaller autoCaller(this);
13945 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13946
13947 ComPtr<IInternalSessionControl> directControl;
13948 {
13949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13950 if (mData->mSession.mLockType == LockType_VM)
13951 directControl = mData->mSession.mDirectControl;
13952 }
13953
13954 /* ignore notifications sent after #OnSessionEnd() is called */
13955 if (!directControl)
13956 return S_OK;
13957
13958 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13959}
13960
13961/**
13962 * @note Locks this object for reading.
13963 */
13964HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13965 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13966 const Utf8Str &aGuestIp, LONG aGuestPort)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 if (mData->mSession.mLockType == LockType_VM)
13977 directControl = mData->mSession.mDirectControl;
13978 }
13979
13980 /* ignore notifications sent after #OnSessionEnd() is called */
13981 if (!directControl)
13982 return S_OK;
13983 /*
13984 * instead acting like callback we ask IVirtualBox deliver corresponding event
13985 */
13986
13987 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13988 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13989 return S_OK;
13990}
13991
13992/**
13993 * @note Locks this object for reading.
13994 */
13995HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13996{
13997 LogFlowThisFunc(("\n"));
13998
13999 AutoCaller autoCaller(this);
14000 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14001
14002 ComPtr<IInternalSessionControl> directControl;
14003 {
14004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14005 if (mData->mSession.mLockType == LockType_VM)
14006 directControl = mData->mSession.mDirectControl;
14007 }
14008
14009 /* ignore notifications sent after #OnSessionEnd() is called */
14010 if (!directControl)
14011 return S_OK;
14012
14013 return directControl->OnAudioAdapterChange(audioAdapter);
14014}
14015
14016/**
14017 * @note Locks this object for reading.
14018 */
14019HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14020{
14021 LogFlowThisFunc(("\n"));
14022
14023 AutoCaller autoCaller(this);
14024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14025
14026 ComPtr<IInternalSessionControl> directControl;
14027 {
14028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14029 if (mData->mSession.mLockType == LockType_VM)
14030 directControl = mData->mSession.mDirectControl;
14031 }
14032
14033 /* ignore notifications sent after #OnSessionEnd() is called */
14034 if (!directControl)
14035 return S_OK;
14036
14037 return directControl->OnSerialPortChange(serialPort);
14038}
14039
14040/**
14041 * @note Locks this object for reading.
14042 */
14043HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14044{
14045 LogFlowThisFunc(("\n"));
14046
14047 AutoCaller autoCaller(this);
14048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14049
14050 ComPtr<IInternalSessionControl> directControl;
14051 {
14052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14053 if (mData->mSession.mLockType == LockType_VM)
14054 directControl = mData->mSession.mDirectControl;
14055 }
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnParallelPortChange(parallelPort);
14062}
14063
14064/**
14065 * @note Locks this object for reading.
14066 */
14067HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14068{
14069 LogFlowThisFunc(("\n"));
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 if (mData->mSession.mLockType == LockType_VM)
14078 directControl = mData->mSession.mDirectControl;
14079 }
14080
14081 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14082
14083 /* ignore notifications sent after #OnSessionEnd() is called */
14084 if (!directControl)
14085 return S_OK;
14086
14087 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14088}
14089
14090/**
14091 * @note Locks this object for reading.
14092 */
14093HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14094{
14095 LogFlowThisFunc(("\n"));
14096
14097 AutoCaller autoCaller(this);
14098 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14099
14100 ComPtr<IInternalSessionControl> directControl;
14101 {
14102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14103 if (mData->mSession.mLockType == LockType_VM)
14104 directControl = mData->mSession.mDirectControl;
14105 }
14106
14107 mParent->i_onMediumChanged(aAttachment);
14108
14109 /* ignore notifications sent after #OnSessionEnd() is called */
14110 if (!directControl)
14111 return S_OK;
14112
14113 return directControl->OnMediumChange(aAttachment, aForce);
14114}
14115
14116HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14117{
14118 LogFlowThisFunc(("\n"));
14119
14120 AutoCaller autoCaller(this);
14121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14122
14123 ComPtr<IInternalSessionControl> directControl;
14124 {
14125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14126 if (mData->mSession.mLockType == LockType_VM)
14127 directControl = mData->mSession.mDirectControl;
14128 }
14129
14130 /* ignore notifications sent after #OnSessionEnd() is called */
14131 if (!directControl)
14132 return S_OK;
14133
14134 return directControl->OnVMProcessPriorityChange(aPriority);
14135}
14136
14137/**
14138 * @note Locks this object for reading.
14139 */
14140HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14141{
14142 LogFlowThisFunc(("\n"));
14143
14144 AutoCaller autoCaller(this);
14145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14146
14147 ComPtr<IInternalSessionControl> directControl;
14148 {
14149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14150 if (mData->mSession.mLockType == LockType_VM)
14151 directControl = mData->mSession.mDirectControl;
14152 }
14153
14154 /* ignore notifications sent after #OnSessionEnd() is called */
14155 if (!directControl)
14156 return S_OK;
14157
14158 return directControl->OnCPUChange(aCPU, aRemove);
14159}
14160
14161HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178
14179 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14180}
14181
14182/**
14183 * @note Locks this object for reading.
14184 */
14185HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14186{
14187 LogFlowThisFunc(("\n"));
14188
14189 AutoCaller autoCaller(this);
14190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14191
14192 ComPtr<IInternalSessionControl> directControl;
14193 {
14194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14195 if (mData->mSession.mLockType == LockType_VM)
14196 directControl = mData->mSession.mDirectControl;
14197 }
14198
14199 /* ignore notifications sent after #OnSessionEnd() is called */
14200 if (!directControl)
14201 return S_OK;
14202
14203 return directControl->OnVRDEServerChange(aRestart);
14204}
14205
14206/**
14207 * @note Locks this object for reading.
14208 */
14209HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14210{
14211 LogFlowThisFunc(("\n"));
14212
14213 AutoCaller autoCaller(this);
14214 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14215
14216 ComPtr<IInternalSessionControl> directControl;
14217 {
14218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14219 if (mData->mSession.mLockType == LockType_VM)
14220 directControl = mData->mSession.mDirectControl;
14221 }
14222
14223 /* ignore notifications sent after #OnSessionEnd() is called */
14224 if (!directControl)
14225 return S_OK;
14226
14227 return directControl->OnRecordingChange(aEnable);
14228}
14229
14230/**
14231 * @note Locks this object for reading.
14232 */
14233HRESULT SessionMachine::i_onUSBControllerChange()
14234{
14235 LogFlowThisFunc(("\n"));
14236
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 if (mData->mSession.mLockType == LockType_VM)
14244 directControl = mData->mSession.mDirectControl;
14245 }
14246
14247 /* ignore notifications sent after #OnSessionEnd() is called */
14248 if (!directControl)
14249 return S_OK;
14250
14251 return directControl->OnUSBControllerChange();
14252}
14253
14254/**
14255 * @note Locks this object for reading.
14256 */
14257HRESULT SessionMachine::i_onSharedFolderChange()
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturnRC(autoCaller.rc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 if (mData->mSession.mLockType == LockType_VM)
14268 directControl = mData->mSession.mDirectControl;
14269 }
14270
14271 /* ignore notifications sent after #OnSessionEnd() is called */
14272 if (!directControl)
14273 return S_OK;
14274
14275 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14276}
14277
14278/**
14279 * @note Locks this object for reading.
14280 */
14281HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14282{
14283 LogFlowThisFunc(("\n"));
14284
14285 AutoCaller autoCaller(this);
14286 AssertComRCReturnRC(autoCaller.rc());
14287
14288 ComPtr<IInternalSessionControl> directControl;
14289 {
14290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14291 if (mData->mSession.mLockType == LockType_VM)
14292 directControl = mData->mSession.mDirectControl;
14293 }
14294
14295 /* ignore notifications sent after #OnSessionEnd() is called */
14296 if (!directControl)
14297 return S_OK;
14298
14299 return directControl->OnClipboardModeChange(aClipboardMode);
14300}
14301
14302/**
14303 * @note Locks this object for reading.
14304 */
14305HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14306{
14307 LogFlowThisFunc(("\n"));
14308
14309 AutoCaller autoCaller(this);
14310 AssertComRCReturnRC(autoCaller.rc());
14311
14312 ComPtr<IInternalSessionControl> directControl;
14313 {
14314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14315 if (mData->mSession.mLockType == LockType_VM)
14316 directControl = mData->mSession.mDirectControl;
14317 }
14318
14319 /* ignore notifications sent after #OnSessionEnd() is called */
14320 if (!directControl)
14321 return S_OK;
14322
14323 return directControl->OnClipboardFileTransferModeChange(aEnable);
14324}
14325
14326/**
14327 * @note Locks this object for reading.
14328 */
14329HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14330{
14331 LogFlowThisFunc(("\n"));
14332
14333 AutoCaller autoCaller(this);
14334 AssertComRCReturnRC(autoCaller.rc());
14335
14336 ComPtr<IInternalSessionControl> directControl;
14337 {
14338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14339 if (mData->mSession.mLockType == LockType_VM)
14340 directControl = mData->mSession.mDirectControl;
14341 }
14342
14343 /* ignore notifications sent after #OnSessionEnd() is called */
14344 if (!directControl)
14345 return S_OK;
14346
14347 return directControl->OnDnDModeChange(aDnDMode);
14348}
14349
14350/**
14351 * @note Locks this object for reading.
14352 */
14353HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14354{
14355 LogFlowThisFunc(("\n"));
14356
14357 AutoCaller autoCaller(this);
14358 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14359
14360 ComPtr<IInternalSessionControl> directControl;
14361 {
14362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14363 if (mData->mSession.mLockType == LockType_VM)
14364 directControl = mData->mSession.mDirectControl;
14365 }
14366
14367 /* ignore notifications sent after #OnSessionEnd() is called */
14368 if (!directControl)
14369 return S_OK;
14370
14371 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14372}
14373
14374/**
14375 * @note Locks this object for reading.
14376 */
14377HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14378{
14379 LogFlowThisFunc(("\n"));
14380
14381 AutoCaller autoCaller(this);
14382 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14383
14384 ComPtr<IInternalSessionControl> directControl;
14385 {
14386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14387 if (mData->mSession.mLockType == LockType_VM)
14388 directControl = mData->mSession.mDirectControl;
14389 }
14390
14391 /* ignore notifications sent after #OnSessionEnd() is called */
14392 if (!directControl)
14393 return S_OK;
14394
14395 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14396}
14397
14398/**
14399 * Returns @c true if this machine's USB controller reports it has a matching
14400 * filter for the given USB device and @c false otherwise.
14401 *
14402 * @note locks this object for reading.
14403 */
14404bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14405{
14406 AutoCaller autoCaller(this);
14407 /* silently return if not ready -- this method may be called after the
14408 * direct machine session has been called */
14409 if (!autoCaller.isOk())
14410 return false;
14411
14412#ifdef VBOX_WITH_USB
14413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14414
14415 switch (mData->mMachineState)
14416 {
14417 case MachineState_Starting:
14418 case MachineState_Restoring:
14419 case MachineState_TeleportingIn:
14420 case MachineState_Paused:
14421 case MachineState_Running:
14422 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14423 * elsewhere... */
14424 alock.release();
14425 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14426 default: break;
14427 }
14428#else
14429 NOREF(aDevice);
14430 NOREF(aMaskedIfs);
14431#endif
14432 return false;
14433}
14434
14435/**
14436 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14437 */
14438HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14439 IVirtualBoxErrorInfo *aError,
14440 ULONG aMaskedIfs,
14441 const com::Utf8Str &aCaptureFilename)
14442{
14443 LogFlowThisFunc(("\n"));
14444
14445 AutoCaller autoCaller(this);
14446
14447 /* This notification may happen after the machine object has been
14448 * uninitialized (the session was closed), so don't assert. */
14449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14450
14451 ComPtr<IInternalSessionControl> directControl;
14452 {
14453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14454 if (mData->mSession.mLockType == LockType_VM)
14455 directControl = mData->mSession.mDirectControl;
14456 }
14457
14458 /* fail on notifications sent after #OnSessionEnd() is called, it is
14459 * expected by the caller */
14460 if (!directControl)
14461 return E_FAIL;
14462
14463 /* No locks should be held at this point. */
14464 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14465 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14466
14467 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14468}
14469
14470/**
14471 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14472 */
14473HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14474 IVirtualBoxErrorInfo *aError)
14475{
14476 LogFlowThisFunc(("\n"));
14477
14478 AutoCaller autoCaller(this);
14479
14480 /* This notification may happen after the machine object has been
14481 * uninitialized (the session was closed), so don't assert. */
14482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14483
14484 ComPtr<IInternalSessionControl> directControl;
14485 {
14486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14487 if (mData->mSession.mLockType == LockType_VM)
14488 directControl = mData->mSession.mDirectControl;
14489 }
14490
14491 /* fail on notifications sent after #OnSessionEnd() is called, it is
14492 * expected by the caller */
14493 if (!directControl)
14494 return E_FAIL;
14495
14496 /* No locks should be held at this point. */
14497 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14498 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14499
14500 return directControl->OnUSBDeviceDetach(aId, aError);
14501}
14502
14503// protected methods
14504/////////////////////////////////////////////////////////////////////////////
14505
14506/**
14507 * Deletes the given file if it is no longer in use by either the current machine state
14508 * (if the machine is "saved") or any of the machine's snapshots.
14509 *
14510 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14511 * but is different for each SnapshotMachine. When calling this, the order of calling this
14512 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14513 * is therefore critical. I know, it's all rather messy.
14514 *
14515 * @param strStateFile
14516 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14517 * the test for whether the saved state file is in use.
14518 */
14519void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14520 Snapshot *pSnapshotToIgnore)
14521{
14522 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14523 if ( (strStateFile.isNotEmpty())
14524 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14525 )
14526 // ... and it must also not be shared with other snapshots
14527 if ( !mData->mFirstSnapshot
14528 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14529 // this checks the SnapshotMachine's state file paths
14530 )
14531 RTFileDelete(strStateFile.c_str());
14532}
14533
14534/**
14535 * Locks the attached media.
14536 *
14537 * All attached hard disks are locked for writing and DVD/floppy are locked for
14538 * reading. Parents of attached hard disks (if any) are locked for reading.
14539 *
14540 * This method also performs accessibility check of all media it locks: if some
14541 * media is inaccessible, the method will return a failure and a bunch of
14542 * extended error info objects per each inaccessible medium.
14543 *
14544 * Note that this method is atomic: if it returns a success, all media are
14545 * locked as described above; on failure no media is locked at all (all
14546 * succeeded individual locks will be undone).
14547 *
14548 * The caller is responsible for doing the necessary state sanity checks.
14549 *
14550 * The locks made by this method must be undone by calling #unlockMedia() when
14551 * no more needed.
14552 */
14553HRESULT SessionMachine::i_lockMedia()
14554{
14555 AutoCaller autoCaller(this);
14556 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14557
14558 AutoMultiWriteLock2 alock(this->lockHandle(),
14559 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14560
14561 /* bail out if trying to lock things with already set up locking */
14562 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14563
14564 MultiResult mrc(S_OK);
14565
14566 /* Collect locking information for all medium objects attached to the VM. */
14567 for (MediumAttachmentList::const_iterator
14568 it = mMediumAttachments->begin();
14569 it != mMediumAttachments->end();
14570 ++it)
14571 {
14572 MediumAttachment *pAtt = *it;
14573 DeviceType_T devType = pAtt->i_getType();
14574 Medium *pMedium = pAtt->i_getMedium();
14575
14576 MediumLockList *pMediumLockList(new MediumLockList());
14577 // There can be attachments without a medium (floppy/dvd), and thus
14578 // it's impossible to create a medium lock list. It still makes sense
14579 // to have the empty medium lock list in the map in case a medium is
14580 // attached later.
14581 if (pMedium != NULL)
14582 {
14583 MediumType_T mediumType = pMedium->i_getType();
14584 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14585 || mediumType == MediumType_Shareable;
14586 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14587
14588 alock.release();
14589 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14590 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14591 false /* fMediumLockWriteAll */,
14592 NULL,
14593 *pMediumLockList);
14594 alock.acquire();
14595 if (FAILED(mrc))
14596 {
14597 delete pMediumLockList;
14598 mData->mSession.mLockedMedia.Clear();
14599 break;
14600 }
14601 }
14602
14603 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14604 if (FAILED(rc))
14605 {
14606 mData->mSession.mLockedMedia.Clear();
14607 mrc = setError(rc,
14608 tr("Collecting locking information for all attached media failed"));
14609 break;
14610 }
14611 }
14612
14613 if (SUCCEEDED(mrc))
14614 {
14615 /* Now lock all media. If this fails, nothing is locked. */
14616 alock.release();
14617 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14618 alock.acquire();
14619 if (FAILED(rc))
14620 {
14621 mrc = setError(rc,
14622 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14623 }
14624 }
14625
14626 return mrc;
14627}
14628
14629/**
14630 * Undoes the locks made by by #lockMedia().
14631 */
14632HRESULT SessionMachine::i_unlockMedia()
14633{
14634 AutoCaller autoCaller(this);
14635 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14636
14637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14638
14639 /* we may be holding important error info on the current thread;
14640 * preserve it */
14641 ErrorInfoKeeper eik;
14642
14643 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14644 AssertComRC(rc);
14645 return rc;
14646}
14647
14648/**
14649 * Helper to change the machine state (reimplementation).
14650 *
14651 * @note Locks this object for writing.
14652 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14653 * it can cause crashes in random places due to unexpectedly committing
14654 * the current settings. The caller is responsible for that. The call
14655 * to saveStateSettings is fine, because this method does not commit.
14656 */
14657HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14658{
14659 LogFlowThisFuncEnter();
14660
14661 AutoCaller autoCaller(this);
14662 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14663
14664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14665
14666 MachineState_T oldMachineState = mData->mMachineState;
14667
14668 AssertMsgReturn(oldMachineState != aMachineState,
14669 ("oldMachineState=%s, aMachineState=%s\n",
14670 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
14671 E_FAIL);
14672
14673 HRESULT rc = S_OK;
14674
14675 int stsFlags = 0;
14676 bool deleteSavedState = false;
14677
14678 /* detect some state transitions */
14679
14680 if ( ( ( oldMachineState == MachineState_Saved
14681 || oldMachineState == MachineState_AbortedSaved
14682 )
14683 && aMachineState == MachineState_Restoring
14684 )
14685 || ( ( oldMachineState == MachineState_PoweredOff
14686 || oldMachineState == MachineState_Teleported
14687 || oldMachineState == MachineState_Aborted
14688 )
14689 && ( aMachineState == MachineState_TeleportingIn
14690 || aMachineState == MachineState_Starting
14691 )
14692 )
14693 )
14694 {
14695 /* The EMT thread is about to start */
14696
14697 /* Nothing to do here for now... */
14698
14699 /// @todo NEWMEDIA don't let mDVDDrive and other children
14700 /// change anything when in the Starting/Restoring state
14701 }
14702 else if ( ( oldMachineState == MachineState_Running
14703 || oldMachineState == MachineState_Paused
14704 || oldMachineState == MachineState_Teleporting
14705 || oldMachineState == MachineState_OnlineSnapshotting
14706 || oldMachineState == MachineState_LiveSnapshotting
14707 || oldMachineState == MachineState_Stuck
14708 || oldMachineState == MachineState_Starting
14709 || oldMachineState == MachineState_Stopping
14710 || oldMachineState == MachineState_Saving
14711 || oldMachineState == MachineState_Restoring
14712 || oldMachineState == MachineState_TeleportingPausedVM
14713 || oldMachineState == MachineState_TeleportingIn
14714 )
14715 && ( aMachineState == MachineState_PoweredOff
14716 || aMachineState == MachineState_Saved
14717 || aMachineState == MachineState_Teleported
14718 || aMachineState == MachineState_Aborted
14719 || aMachineState == MachineState_AbortedSaved
14720 )
14721 )
14722 {
14723 /* The EMT thread has just stopped, unlock attached media. Note that as
14724 * opposed to locking that is done from Console, we do unlocking here
14725 * because the VM process may have aborted before having a chance to
14726 * properly unlock all media it locked. */
14727
14728 unlockMedia();
14729 }
14730
14731 if (oldMachineState == MachineState_Restoring)
14732 {
14733 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14734 {
14735 /*
14736 * delete the saved state file once the machine has finished
14737 * restoring from it (note that Console sets the state from
14738 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14739 * to give the user an ability to fix an error and retry --
14740 * we keep the saved state file in this case)
14741 */
14742 deleteSavedState = true;
14743 }
14744 }
14745 else if ( oldMachineState == MachineState_Saved
14746 && ( aMachineState == MachineState_PoweredOff
14747 || aMachineState == MachineState_Teleported
14748 )
14749 )
14750 {
14751 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14752 deleteSavedState = true;
14753 mData->mCurrentStateModified = TRUE;
14754 stsFlags |= SaveSTS_CurStateModified;
14755 }
14756 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14757 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14758
14759 if ( aMachineState == MachineState_Starting
14760 || aMachineState == MachineState_Restoring
14761 || aMachineState == MachineState_TeleportingIn
14762 )
14763 {
14764 /* set the current state modified flag to indicate that the current
14765 * state is no more identical to the state in the
14766 * current snapshot */
14767 if (!mData->mCurrentSnapshot.isNull())
14768 {
14769 mData->mCurrentStateModified = TRUE;
14770 stsFlags |= SaveSTS_CurStateModified;
14771 }
14772 }
14773
14774 if (deleteSavedState)
14775 {
14776 if (mRemoveSavedState)
14777 {
14778 Assert(!mSSData->strStateFilePath.isEmpty());
14779
14780 // it is safe to delete the saved state file if ...
14781 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14782 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14783 // ... none of the snapshots share the saved state file
14784 )
14785 RTFileDelete(mSSData->strStateFilePath.c_str());
14786 }
14787
14788 mSSData->strStateFilePath.setNull();
14789 stsFlags |= SaveSTS_StateFilePath;
14790 }
14791
14792 /* redirect to the underlying peer machine */
14793 mPeer->i_setMachineState(aMachineState);
14794
14795 if ( oldMachineState != MachineState_RestoringSnapshot
14796 && ( aMachineState == MachineState_PoweredOff
14797 || aMachineState == MachineState_Teleported
14798 || aMachineState == MachineState_Aborted
14799 || aMachineState == MachineState_AbortedSaved
14800 || aMachineState == MachineState_Saved))
14801 {
14802 /* the machine has stopped execution
14803 * (or the saved state file was adopted) */
14804 stsFlags |= SaveSTS_StateTimeStamp;
14805 }
14806
14807 if ( ( oldMachineState == MachineState_PoweredOff
14808 || oldMachineState == MachineState_Aborted
14809 || oldMachineState == MachineState_Teleported
14810 )
14811 && aMachineState == MachineState_Saved)
14812 {
14813 /* the saved state file was adopted */
14814 Assert(!mSSData->strStateFilePath.isEmpty());
14815 stsFlags |= SaveSTS_StateFilePath;
14816 }
14817
14818#ifdef VBOX_WITH_GUEST_PROPS
14819 if ( aMachineState == MachineState_PoweredOff
14820 || aMachineState == MachineState_Aborted
14821 || aMachineState == MachineState_Teleported)
14822 {
14823 /* Make sure any transient guest properties get removed from the
14824 * property store on shutdown. */
14825 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14826
14827 /* remove it from the settings representation */
14828 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14829 for (settings::GuestPropertiesList::iterator
14830 it = llGuestProperties.begin();
14831 it != llGuestProperties.end();
14832 /*nothing*/)
14833 {
14834 const settings::GuestProperty &prop = *it;
14835 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14836 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14837 {
14838 it = llGuestProperties.erase(it);
14839 fNeedsSaving = true;
14840 }
14841 else
14842 {
14843 ++it;
14844 }
14845 }
14846
14847 /* Additionally remove it from the HWData representation. Required to
14848 * keep everything in sync, as this is what the API keeps using. */
14849 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14850 for (HWData::GuestPropertyMap::iterator
14851 it = llHWGuestProperties.begin();
14852 it != llHWGuestProperties.end();
14853 /*nothing*/)
14854 {
14855 uint32_t fFlags = it->second.mFlags;
14856 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14857 {
14858 /* iterator where we need to continue after the erase call
14859 * (C++03 is a fact still, and it doesn't return the iterator
14860 * which would allow continuing) */
14861 HWData::GuestPropertyMap::iterator it2 = it;
14862 ++it2;
14863 llHWGuestProperties.erase(it);
14864 it = it2;
14865 fNeedsSaving = true;
14866 }
14867 else
14868 {
14869 ++it;
14870 }
14871 }
14872
14873 if (fNeedsSaving)
14874 {
14875 mData->mCurrentStateModified = TRUE;
14876 stsFlags |= SaveSTS_CurStateModified;
14877 }
14878 }
14879#endif /* VBOX_WITH_GUEST_PROPS */
14880
14881 rc = i_saveStateSettings(stsFlags);
14882
14883 if ( ( oldMachineState != MachineState_PoweredOff
14884 && oldMachineState != MachineState_Aborted
14885 && oldMachineState != MachineState_Teleported
14886 )
14887 && ( aMachineState == MachineState_PoweredOff
14888 || aMachineState == MachineState_Aborted
14889 || aMachineState == MachineState_Teleported
14890 )
14891 )
14892 {
14893 /* we've been shut down for any reason */
14894 /* no special action so far */
14895 }
14896
14897 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
14898 LogFlowThisFuncLeave();
14899 return rc;
14900}
14901
14902/**
14903 * Sends the current machine state value to the VM process.
14904 *
14905 * @note Locks this object for reading, then calls a client process.
14906 */
14907HRESULT SessionMachine::i_updateMachineStateOnClient()
14908{
14909 AutoCaller autoCaller(this);
14910 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14911
14912 ComPtr<IInternalSessionControl> directControl;
14913 {
14914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14915 AssertReturn(!!mData, E_FAIL);
14916 if (mData->mSession.mLockType == LockType_VM)
14917 directControl = mData->mSession.mDirectControl;
14918
14919 /* directControl may be already set to NULL here in #OnSessionEnd()
14920 * called too early by the direct session process while there is still
14921 * some operation (like deleting the snapshot) in progress. The client
14922 * process in this case is waiting inside Session::close() for the
14923 * "end session" process object to complete, while #uninit() called by
14924 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14925 * operation to complete. For now, we accept this inconsistent behavior
14926 * and simply do nothing here. */
14927
14928 if (mData->mSession.mState == SessionState_Unlocking)
14929 return S_OK;
14930 }
14931
14932 /* ignore notifications sent after #OnSessionEnd() is called */
14933 if (!directControl)
14934 return S_OK;
14935
14936 return directControl->UpdateMachineState(mData->mMachineState);
14937}
14938
14939
14940/*static*/
14941HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14942{
14943 va_list args;
14944 va_start(args, pcszMsg);
14945 HRESULT rc = setErrorInternalV(aResultCode,
14946 getStaticClassIID(),
14947 getStaticComponentName(),
14948 pcszMsg, args,
14949 false /* aWarning */,
14950 true /* aLogIt */);
14951 va_end(args);
14952 return rc;
14953}
14954
14955
14956HRESULT Machine::updateState(MachineState_T aState)
14957{
14958 NOREF(aState);
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14963{
14964 NOREF(aProgress);
14965 ReturnComNotImplemented();
14966}
14967
14968HRESULT Machine::endPowerUp(LONG aResult)
14969{
14970 NOREF(aResult);
14971 ReturnComNotImplemented();
14972}
14973
14974HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14975{
14976 NOREF(aProgress);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::endPoweringDown(LONG aResult,
14981 const com::Utf8Str &aErrMsg)
14982{
14983 NOREF(aResult);
14984 NOREF(aErrMsg);
14985 ReturnComNotImplemented();
14986}
14987
14988HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14989 BOOL *aMatched,
14990 ULONG *aMaskedInterfaces)
14991{
14992 NOREF(aDevice);
14993 NOREF(aMatched);
14994 NOREF(aMaskedInterfaces);
14995 ReturnComNotImplemented();
14996
14997}
14998
14999HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15000{
15001 NOREF(aId); NOREF(aCaptureFilename);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15006 BOOL aDone)
15007{
15008 NOREF(aId);
15009 NOREF(aDone);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::autoCaptureUSBDevices()
15014{
15015 ReturnComNotImplemented();
15016}
15017
15018HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15019{
15020 NOREF(aDone);
15021 ReturnComNotImplemented();
15022}
15023
15024HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15025 ComPtr<IProgress> &aProgress)
15026{
15027 NOREF(aSession);
15028 NOREF(aProgress);
15029 ReturnComNotImplemented();
15030}
15031
15032HRESULT Machine::finishOnlineMergeMedium()
15033{
15034 ReturnComNotImplemented();
15035}
15036
15037HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15038 std::vector<com::Utf8Str> &aValues,
15039 std::vector<LONG64> &aTimestamps,
15040 std::vector<com::Utf8Str> &aFlags)
15041{
15042 NOREF(aNames);
15043 NOREF(aValues);
15044 NOREF(aTimestamps);
15045 NOREF(aFlags);
15046 ReturnComNotImplemented();
15047}
15048
15049HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15050 const com::Utf8Str &aValue,
15051 LONG64 aTimestamp,
15052 const com::Utf8Str &aFlags)
15053{
15054 NOREF(aName);
15055 NOREF(aValue);
15056 NOREF(aTimestamp);
15057 NOREF(aFlags);
15058 ReturnComNotImplemented();
15059}
15060
15061HRESULT Machine::lockMedia()
15062{
15063 ReturnComNotImplemented();
15064}
15065
15066HRESULT Machine::unlockMedia()
15067{
15068 ReturnComNotImplemented();
15069}
15070
15071HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15072 ComPtr<IMediumAttachment> &aNewAttachment)
15073{
15074 NOREF(aAttachment);
15075 NOREF(aNewAttachment);
15076 ReturnComNotImplemented();
15077}
15078
15079HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15080 ULONG aCpuUser,
15081 ULONG aCpuKernel,
15082 ULONG aCpuIdle,
15083 ULONG aMemTotal,
15084 ULONG aMemFree,
15085 ULONG aMemBalloon,
15086 ULONG aMemShared,
15087 ULONG aMemCache,
15088 ULONG aPagedTotal,
15089 ULONG aMemAllocTotal,
15090 ULONG aMemFreeTotal,
15091 ULONG aMemBalloonTotal,
15092 ULONG aMemSharedTotal,
15093 ULONG aVmNetRx,
15094 ULONG aVmNetTx)
15095{
15096 NOREF(aValidStats);
15097 NOREF(aCpuUser);
15098 NOREF(aCpuKernel);
15099 NOREF(aCpuIdle);
15100 NOREF(aMemTotal);
15101 NOREF(aMemFree);
15102 NOREF(aMemBalloon);
15103 NOREF(aMemShared);
15104 NOREF(aMemCache);
15105 NOREF(aPagedTotal);
15106 NOREF(aMemAllocTotal);
15107 NOREF(aMemFreeTotal);
15108 NOREF(aMemBalloonTotal);
15109 NOREF(aMemSharedTotal);
15110 NOREF(aVmNetRx);
15111 NOREF(aVmNetTx);
15112 ReturnComNotImplemented();
15113}
15114
15115HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15116 com::Utf8Str &aResult)
15117{
15118 NOREF(aAuthParams);
15119 NOREF(aResult);
15120 ReturnComNotImplemented();
15121}
15122
15123com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15124{
15125 com::Utf8Str strControllerName = "Unknown";
15126 switch (aBusType)
15127 {
15128 case StorageBus_IDE:
15129 {
15130 strControllerName = "IDE";
15131 break;
15132 }
15133 case StorageBus_SATA:
15134 {
15135 strControllerName = "SATA";
15136 break;
15137 }
15138 case StorageBus_SCSI:
15139 {
15140 strControllerName = "SCSI";
15141 break;
15142 }
15143 case StorageBus_Floppy:
15144 {
15145 strControllerName = "Floppy";
15146 break;
15147 }
15148 case StorageBus_SAS:
15149 {
15150 strControllerName = "SAS";
15151 break;
15152 }
15153 case StorageBus_USB:
15154 {
15155 strControllerName = "USB";
15156 break;
15157 }
15158 default:
15159 break;
15160 }
15161 return strControllerName;
15162}
15163
15164HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15165{
15166 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15167
15168 AutoCaller autoCaller(this);
15169 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15170
15171 HRESULT rc = S_OK;
15172
15173 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15174 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15175 rc = getUSBDeviceFilters(usbDeviceFilters);
15176 if (FAILED(rc)) return rc;
15177
15178 NOREF(aFlags);
15179 com::Utf8Str osTypeId;
15180 ComObjPtr<GuestOSType> osType = NULL;
15181
15182 /* Get the guest os type as a string from the VB. */
15183 rc = getOSTypeId(osTypeId);
15184 if (FAILED(rc)) return rc;
15185
15186 /* Get the os type obj that coresponds, can be used to get
15187 * the defaults for this guest OS. */
15188 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15189 if (FAILED(rc)) return rc;
15190
15191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15192
15193 /* Let the OS type select 64-bit ness. */
15194 mHWData->mLongMode = osType->i_is64Bit()
15195 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15196
15197 /* Let the OS type enable the X2APIC */
15198 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15199
15200 /* This one covers IOAPICEnabled. */
15201 mBIOSSettings->i_applyDefaults(osType);
15202
15203 /* Initialize default record settings. */
15204 mRecordingSettings->i_applyDefaults();
15205
15206 /* Initialize default BIOS settings here */
15207 /* Hardware virtualization must be ON by default */
15208 mHWData->mAPIC = true;
15209 mHWData->mHWVirtExEnabled = true;
15210
15211 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15212 if (FAILED(rc)) return rc;
15213
15214 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15215 if (FAILED(rc)) return rc;
15216
15217 /* Graphics stuff. */
15218 GraphicsControllerType_T graphicsController;
15219 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15220 if (FAILED(rc)) return rc;
15221
15222 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15223 if (FAILED(rc)) return rc;
15224
15225 ULONG vramSize;
15226 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15227 if (FAILED(rc)) return rc;
15228
15229 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15230 if (FAILED(rc)) return rc;
15231
15232 BOOL fAccelerate2DVideoEnabled;
15233 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15237 if (FAILED(rc)) return rc;
15238
15239 BOOL fAccelerate3DEnabled;
15240 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15241 if (FAILED(rc)) return rc;
15242
15243 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15244 if (FAILED(rc)) return rc;
15245
15246 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15247 if (FAILED(rc)) return rc;
15248
15249 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15250 if (FAILED(rc)) return rc;
15251
15252 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15253 if (FAILED(rc)) return rc;
15254
15255 BOOL mRTCUseUTC;
15256 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15257 if (FAILED(rc)) return rc;
15258
15259 setRTCUseUTC(mRTCUseUTC);
15260 if (FAILED(rc)) return rc;
15261
15262 /* the setter does more than just the assignment, so use it */
15263 ChipsetType_T enmChipsetType;
15264 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15265 if (FAILED(rc)) return rc;
15266
15267 rc = COMSETTER(ChipsetType)(enmChipsetType);
15268 if (FAILED(rc)) return rc;
15269
15270 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15271 if (FAILED(rc)) return rc;
15272
15273 /* Apply IOMMU defaults. */
15274 IommuType_T enmIommuType;
15275 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15276 if (FAILED(rc)) return rc;
15277
15278 rc = COMSETTER(IommuType)(enmIommuType);
15279 if (FAILED(rc)) return rc;
15280
15281 /* Apply network adapters defaults */
15282 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15283 mNetworkAdapters[slot]->i_applyDefaults(osType);
15284
15285 /* Apply serial port defaults */
15286 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15287 mSerialPorts[slot]->i_applyDefaults(osType);
15288
15289 /* Apply parallel port defaults - not OS dependent*/
15290 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15291 mParallelPorts[slot]->i_applyDefaults();
15292
15293 /* This one covers the TPM type. */
15294 mTrustedPlatformModule->i_applyDefaults(osType);
15295
15296 /* This one covers secure boot. */
15297 rc = mNvramStore->i_applyDefaults(osType);
15298 if (FAILED(rc)) return rc;
15299
15300 /* Audio stuff. */
15301 AudioControllerType_T audioController;
15302 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15303 if (FAILED(rc)) return rc;
15304
15305 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15306 if (FAILED(rc)) return rc;
15307
15308 AudioCodecType_T audioCodec;
15309 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15310 if (FAILED(rc)) return rc;
15311
15312 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15313 if (FAILED(rc)) return rc;
15314
15315 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15316 if (FAILED(rc)) return rc;
15317
15318 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15319 if (FAILED(rc)) return rc;
15320
15321 /* Storage Controllers */
15322 StorageControllerType_T hdStorageControllerType;
15323 StorageBus_T hdStorageBusType;
15324 StorageControllerType_T dvdStorageControllerType;
15325 StorageBus_T dvdStorageBusType;
15326 BOOL recommendedFloppy;
15327 ComPtr<IStorageController> floppyController;
15328 ComPtr<IStorageController> hdController;
15329 ComPtr<IStorageController> dvdController;
15330 Utf8Str strFloppyName, strDVDName, strHDName;
15331
15332 /* GUI auto generates controller names using bus type. Do the same*/
15333 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15334
15335 /* Floppy recommended? add one. */
15336 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15337 if (FAILED(rc)) return rc;
15338 if (recommendedFloppy)
15339 {
15340 rc = addStorageController(strFloppyName,
15341 StorageBus_Floppy,
15342 floppyController);
15343 if (FAILED(rc)) return rc;
15344 }
15345
15346 /* Setup one DVD storage controller. */
15347 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15348 if (FAILED(rc)) return rc;
15349
15350 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15351 if (FAILED(rc)) return rc;
15352
15353 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15354
15355 rc = addStorageController(strDVDName,
15356 dvdStorageBusType,
15357 dvdController);
15358 if (FAILED(rc)) return rc;
15359
15360 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15361 if (FAILED(rc)) return rc;
15362
15363 /* Setup one HDD storage controller. */
15364 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15365 if (FAILED(rc)) return rc;
15366
15367 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15368 if (FAILED(rc)) return rc;
15369
15370 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15371
15372 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15373 {
15374 rc = addStorageController(strHDName,
15375 hdStorageBusType,
15376 hdController);
15377 if (FAILED(rc)) return rc;
15378
15379 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15380 if (FAILED(rc)) return rc;
15381 }
15382 else
15383 {
15384 /* The HD controller is the same as DVD: */
15385 hdController = dvdController;
15386 }
15387
15388 /* Limit the AHCI port count if it's used because windows has trouble with
15389 * too many ports and other guest (OS X in particular) may take extra long
15390 * boot: */
15391
15392 // pParent = static_cast<Medium*>(aP)
15393 IStorageController *temp = hdController;
15394 ComObjPtr<StorageController> storageController;
15395 storageController = static_cast<StorageController *>(temp);
15396
15397 // tempHDController = aHDController;
15398 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15399 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15400 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15401 storageController->COMSETTER(PortCount)(1);
15402
15403 /* USB stuff */
15404
15405 bool ohciEnabled = false;
15406
15407 ComPtr<IUSBController> usbController;
15408 BOOL recommendedUSB3;
15409 BOOL recommendedUSB;
15410 BOOL usbProxyAvailable;
15411
15412 getUSBProxyAvailable(&usbProxyAvailable);
15413 if (FAILED(rc)) return rc;
15414
15415 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15416 if (FAILED(rc)) return rc;
15417 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15418 if (FAILED(rc)) return rc;
15419
15420 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15421 {
15422#ifdef VBOX_WITH_EXTPACK
15423 /* USB 3.0 is only available if the proper ExtPack is installed. */
15424 ExtPackManager *aManager = mParent->i_getExtPackManager();
15425 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15426 {
15427 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15428 if (FAILED(rc)) return rc;
15429
15430 /* xHci includes OHCI */
15431 ohciEnabled = true;
15432 }
15433#endif
15434 }
15435 if ( !ohciEnabled
15436 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15437 {
15438 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15439 if (FAILED(rc)) return rc;
15440 ohciEnabled = true;
15441
15442#ifdef VBOX_WITH_EXTPACK
15443 /* USB 2.0 is only available if the proper ExtPack is installed.
15444 * Note. Configuring EHCI here and providing messages about
15445 * the missing extpack isn't exactly clean, but it is a
15446 * necessary evil to patch over legacy compatability issues
15447 * introduced by the new distribution model. */
15448 ExtPackManager *manager = mParent->i_getExtPackManager();
15449 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15450 {
15451 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15452 if (FAILED(rc)) return rc;
15453 }
15454#endif
15455 }
15456
15457 /* Set recommended human interface device types: */
15458 BOOL recommendedUSBHID;
15459 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15460 if (FAILED(rc)) return rc;
15461
15462 if (recommendedUSBHID)
15463 {
15464 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15465 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15466 if (!ohciEnabled && !usbDeviceFilters.isNull())
15467 {
15468 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15469 if (FAILED(rc)) return rc;
15470 }
15471 }
15472
15473 BOOL recommendedUSBTablet;
15474 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15475 if (FAILED(rc)) return rc;
15476
15477 if (recommendedUSBTablet)
15478 {
15479 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15480 if (!ohciEnabled && !usbDeviceFilters.isNull())
15481 {
15482 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15483 if (FAILED(rc)) return rc;
15484 }
15485 }
15486
15487 /* Enable the VMMDev testing feature for bootsector VMs: */
15488 if (osTypeId == "VBoxBS_64")
15489 {
15490 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15491 if (FAILED(rc))
15492 return rc;
15493 }
15494
15495 return S_OK;
15496}
15497
15498/* This isn't handled entirely by the wrapper generator yet. */
15499#ifdef VBOX_WITH_XPCOM
15500NS_DECL_CLASSINFO(SessionMachine)
15501NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15502
15503NS_DECL_CLASSINFO(SnapshotMachine)
15504NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15505#endif
Note: See TracBrowser for help on using the repository browser.

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